X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=cds%2Fcontainer%2Fmichael_list_nogc.h;h=747d58db6c06e9ea67b6328f043c5b423546f4db;hb=HEAD;hp=512d61737a997a62ba10caf01fcaeccf58efb3f8;hpb=bab1583cb2db30355c138a507943cd5ad068ccf4;p=libcds.git diff --git a/cds/container/michael_list_nogc.h b/cds/container/michael_list_nogc.h index 512d6173..747d58db 100644 --- a/cds/container/michael_list_nogc.h +++ b/cds/container/michael_list_nogc.h @@ -1,10 +1,38 @@ -//$$CDS-header$$ +/* + This file is a part of libcds - Concurrent Data Structures library -#ifndef __CDS_CONTAINER_MICHAEL_LIST_NOGC_H -#define __CDS_CONTAINER_MICHAEL_LIST_NOGC_H + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CDSLIB_CONTAINER_MICHAEL_LIST_NOGC_H +#define CDSLIB_CONTAINER_MICHAEL_LIST_NOGC_H #include -#include +#include #include #include @@ -19,31 +47,35 @@ namespace cds { namespace container { typedef make_michael_list base_maker; typedef typename base_maker::node_type node_type; - struct type_traits: public base_maker::type_traits + struct intrusive_traits: public base_maker::intrusive_traits { typedef typename base_maker::node_deallocator disposer; }; - typedef intrusive::MichaelList type; + typedef intrusive::MichaelList type; }; } // namespace details //@endcond - /// Michael's lock-free ordered single-linked list (template specialization for gc::nogc) + /// Michael's lock-free ordered single-linked list (template specialization for \p gc::nogc) /** @ingroup cds_nonintrusive_list \anchor cds_nonintrusive_MichaelList_nogc - This specialization is intended for so-called persistent usage when no item + This specialization is intended for so-called append-only usage when no item reclamation may be performed. The class does not support deleting of list item. Usually, ordered single-linked list is used as a building block for the hash table implementation. The complexity of searching is O(N). See \ref cds_nonintrusive_MichaelList_gc "MichaelList" for description of template parameters. - - The interface of the specialization is a little different. */ - template + template class MichaelList: #ifdef CDS_DOXYGEN_INVOKED protected intrusive::MichaelList< gc::nogc, T, Traits > @@ -52,70 +84,46 @@ namespace cds { namespace container { #endif { //@cond - typedef details::make_michael_list_nogc< T, Traits > options; - typedef typename options::type base_class; + typedef details::make_michael_list_nogc< T, Traits > maker; + typedef typename maker::type base_class; //@endcond public: - typedef T value_type ; ///< Type of value stored in the list - typedef typename base_class::gc gc ; ///< Garbage collector used - typedef typename base_class::back_off back_off ; ///< Back-off strategy used - typedef typename options::allocator_type allocator_type ; ///< Allocator type used for allocate/deallocate the nodes - typedef typename base_class::item_counter item_counter ; ///< Item counting policy used - typedef typename options::key_comparator key_comparator ; ///< key comparison functor - typedef typename base_class::memory_model memory_model ; ///< Memory ordering. See cds::opt::memory_model option + typedef cds::gc::nogc gc; ///< Garbage collector used + typedef T value_type; ///< Type of value stored in the list + typedef Traits traits; ///< List traits - protected: - //@cond - typedef typename base_class::value_type node_type; - typedef typename options::cxx_allocator cxx_allocator; - typedef typename options::node_deallocator node_deallocator; - typedef typename options::type_traits::compare intrusive_key_comparator; + typedef typename base_class::back_off back_off; ///< Back-off strategy used + typedef typename maker::allocator_type allocator_type; ///< Allocator type used for allocate/deallocate the nodes + typedef typename base_class::item_counter item_counter; ///< Item counting policy used + typedef typename maker::key_comparator key_comparator; ///< key comparison functor + typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics - typedef typename base_class::atomic_node_ptr head_type; - //@endcond - - protected: //@cond -# ifndef CDS_CXX11_LAMBDA_SUPPORT - struct ensure_functor - { - node_type * m_pItemFound; - - ensure_functor() - : m_pItemFound( nullptr ) - {} - - void operator ()(bool, node_type& item, node_type& ) - { - m_pItemFound = &item; - } + // Rebind traits (split-list support) + template + struct rebind_traits { + typedef MichaelList< + gc + , value_type + , typename cds::opt::make_options< traits, Options...>::type + > type; }; -# endif + + // Stat selector + template + using select_stat_wrapper = typename base_class::template select_stat_wrapper< Stat >; //@endcond protected: //@cond - static node_type * alloc_node() - { - return cxx_allocator().New(); - } + typedef typename base_class::value_type node_type; + typedef typename maker::cxx_allocator cxx_allocator; + typedef typename maker::node_deallocator node_deallocator; + typedef typename maker::intrusive_traits::compare intrusive_key_comparator; - static node_type * alloc_node( value_type const& v ) - { - return cxx_allocator().New( v ); - } - - template - static node_type * alloc_node( Args&&... args ) - { - return cxx_allocator().MoveNew( std::forward(args)... ); - } - - static void free_node( node_type * pNode ) - { - cxx_allocator().Delete( pNode ); - } + typedef typename base_class::atomic_node_ptr head_type; struct node_disposer { void operator()( node_type * pNode ) @@ -123,17 +131,8 @@ namespace cds { namespace container { free_node( pNode ); } }; - typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr; - head_type& head() - { - return base_class::m_pHead; - } - - head_type const& head() const - { - return base_class::m_pHead; - } + typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr; //@endcond protected: @@ -207,6 +206,8 @@ namespace cds { namespace container { //@endcond public: + ///@name Forward iterators + //@{ /// Returns a forward iterator addressing the first element in a list /** For empty list \code begin() == end() \endcode @@ -225,7 +226,7 @@ namespace cds { namespace container { */ iterator begin() { - return iterator( head() ); + return iterator( head()); } /// Returns an iterator that addresses the location succeeding the last element in a list @@ -242,38 +243,29 @@ namespace cds { namespace container { } /// Returns a forward const iterator addressing the first element in a list - //@{ const_iterator begin() const { - return const_iterator( head() ); + return const_iterator( head()); } - const_iterator cbegin() + + /// Returns a forward const iterator addressing the first element in a list + const_iterator cbegin() const { - return const_iterator( head() ); + return const_iterator( head()); } - //@} /// Returns an const iterator that addresses the location succeeding the last element in a list - //@{ const_iterator end() const { return const_iterator(); } - const_iterator cend() - { - return const_iterator(); - } - //@} - protected: - //@cond - iterator node_to_iterator( node_type * pNode ) + /// Returns an const iterator that addresses the location succeeding the last element in a list + const_iterator cend() const { - if ( pNode ) - return iterator( *pNode ); - return end(); + return const_iterator(); } - //@endcond + //@} public: /// Default constructor @@ -283,7 +275,14 @@ namespace cds { namespace container { MichaelList() {} - /// List desctructor + //@cond + template >::value >> + explicit MichaelList( Stat& st ) + : base_class( st ) + {} + //@endcond + + /// List destructor /** Clears the list */ @@ -300,26 +299,35 @@ namespace cds { namespace container { Return an iterator pointing to inserted item if success \ref end() otherwise */ template - iterator insert( const Q& val ) + iterator insert( Q&& val ) { - return node_to_iterator( insert_at( head(), val ) ); + return node_to_iterator( insert_at( head(), std::forward( val ))); } - /// Ensures that the item \p val exists in the list + /// Updates the item /** - The operation inserts new item if the key \p val is not found in the list. - Otherwise, the function returns an iterator that points to item found. + If \p key is not in the list and \p bAllowInsert is \p true, + the function inserts a new item. + Otherwise, the function returns an iterator pointing to the item found. Returns std::pair where \p first is an iterator pointing to item found or inserted, \p second is true if new item has been added or \p false if the item already is in the list. */ template - std::pair ensure( const Q& val ) + std::pair update( Q&& key, bool bAllowInsert = true ) { - std::pair< node_type *, bool > ret = ensure_at( head(), val ); + std::pair< node_type *, bool > ret = update_at( head(), std::forward( key ), bAllowInsert ); return std::make_pair( node_to_iterator( ret.first ), ret.second ); } + //@cond + template + CDS_DEPRECATED("ensure() is deprecated, use update()") + std::pair ensure( const Q& val ) + { + return update( val, true ); + } + //@endcond /// Inserts data of type \ref value_type constructed with std::forward(args)... /** @@ -331,30 +339,46 @@ namespace cds { namespace container { return node_to_iterator( emplace_at( head(), std::forward(args)... )); } - /// Find the key \p val - /** \anchor cds_nonintrusive_MichaelList_nogc_find_val - The function searches the item with key equal to \p val + /// Checks whether the list contains \p key + /** + The function searches the item with key equal to \p key and returns an iterator pointed to item found if the key is found, and \ref end() otherwise */ template + iterator contains( Q const& key ) + { + return node_to_iterator( find_at( head(), key, intrusive_key_comparator())); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") iterator find( Q const& key ) { - return node_to_iterator( find_at( head(), key, intrusive_key_comparator() )); + return contains( key ); } + //@endcond - /// Finds the key \p val using \p pred predicate for searching + /// Checks whether the map contains \p key using \p pred predicate for searching /** - The function is an analog of \ref cds_nonintrusive_MichaelList_nogc_find_val "find(Q const&)" - but \p pred is used for key comparing. + The function is an analog of contains( key ) but \p pred is used for key comparing. \p Less functor has the interface like \p std::less. - \p pred must imply the same element order as the comparator used for building the list. + \p Less must imply the same element order as the comparator used for building the list. */ template + iterator contains( Q const& key, Less pred ) + { + CDS_UNUSED( pred ); + return node_to_iterator( find_at( head(), key, typename maker::template less_wrapper::type())); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") iterator find_with( Q const& key, Less pred ) { - return node_to_iterator( find_at( head(), key, typename options::template less_wrapper::type() )); + return contains( key, pred ); } + //@endcond /// Check if the list is empty bool empty() const @@ -364,21 +388,24 @@ namespace cds { namespace container { /// Returns list's item count /** - The value returned depends on opt::item_counter option. For atomics::empty_item_counter, + The value returned depends on item counter provided by \p Traits. For \p atomicity::empty_item_counter, this function always returns 0. - Warning: even if you use real item counter and it returns 0, this fact is not mean that the list - is empty. To check list emptyness use \ref empty() method. + @note Even if you use real item counter and it returns 0, this fact does not mean that the list + is empty. To check list emptyness use \p empty() method. */ size_t size() const { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list - /** - Post-condition: the list is empty - */ void clear() { base_class::clear(); @@ -386,6 +413,54 @@ namespace cds { namespace container { protected: //@cond + static value_type& node_to_value( node_type& n ) + { + return n.m_Value; + } + + static node_type * alloc_node() + { + return cxx_allocator().New(); + } + + static node_type * alloc_node( value_type const& v ) + { + return cxx_allocator().New( v ); + } + + template + static node_type * alloc_node( Args&&... args ) + { + return cxx_allocator().MoveNew( std::forward( args )... ); + } + + static void free_node( node_type * pNode ) + { + cxx_allocator().Delete( pNode ); + } + + head_type& head() + { + return base_class::m_pHead; + } + + head_type const& head() const + { + return base_class::m_pHead; + } + + iterator node_to_iterator( node_type * pNode ) + { + if ( pNode ) + return iterator( *pNode ); + return end(); + } + + iterator insert_node( node_type * pNode ) + { + return node_to_iterator( insert_node_at( head(), pNode )); + } + node_type * insert_node_at( head_type& refHead, node_type * pNode ) { assert( pNode != nullptr ); @@ -397,27 +472,22 @@ namespace cds { namespace container { } template - node_type * insert_at( head_type& refHead, const Q& val ) + node_type * insert_at( head_type& refHead, Q&& val ) { - return insert_node_at( refHead, alloc_node( val )); + return insert_node_at( refHead, alloc_node( std::forward( val ))); } template - std::pair< node_type *, bool > ensure_at( head_type& refHead, const Q& val ) + std::pair< node_type *, bool > update_at( head_type& refHead, Q&& val, bool bAllowInsert ) { - scoped_node_ptr pNode( alloc_node( val )); + scoped_node_ptr pNode( alloc_node( std::forward( val ))); node_type * pItemFound = nullptr; -# ifdef CDS_CXX11_LAMBDA_SUPPORT - std::pair ret = base_class::ensure_at( refHead, *pNode, [&pItemFound](bool, node_type& item, node_type&) { pItemFound = &item; }); -# else - ensure_functor func; - std::pair ret = base_class::ensure_at( refHead, *pNode, boost::ref(func) ); - pItemFound = func.m_pItemFound; -# endif - assert( pItemFound != nullptr ); + std::pair ret = base_class::update_at( refHead, *pNode, + [&pItemFound](bool, node_type& item, node_type&) { pItemFound = &item; }, + bAllowInsert ); - if ( ret.first && ret.second ) + if ( ret.second ) pNode.release(); return std::make_pair( pItemFound, ret.second ); } @@ -433,9 +503,8 @@ namespace cds { namespace container { { return base_class::find_at( refHead, key, cmp ); } - //@endcond }; }} // namespace cds::container -#endif // #ifndef __CDS_CONTAINER_MICHAEL_LIST_NOGC_H +#endif // #ifndef CDSLIB_CONTAINER_MICHAEL_LIST_NOGC_H