diff --git a/cds/container/details/ellen_bintree_base.h b/cds/container/details/ellen_bintree_base.h index 5daf245ac..a132b8669 100644 --- a/cds/container/details/ellen_bintree_base.h +++ b/cds/container/details/ellen_bintree_base.h @@ -457,4 +457,4 @@ namespace cds { namespace container { }} // namespace cds::container -#endif // #ifndef CDSLIB_CONTAINER_DETAILS_ELLEN_BINTREE_BASE_H +#endif // #ifndef CDSLIB_CONTAINER_DETAILS_ELLEN_BINTREE_BASE_H \ No newline at end of file diff --git a/cds/container/ellen_bintree_map_nogc.h b/cds/container/ellen_bintree_map_nogc.h new file mode 100644 index 000000000..623d3de5f --- /dev/null +++ b/cds/container/ellen_bintree_map_nogc.h @@ -0,0 +1,220 @@ +#ifndef CDS_ELLEN_BINTREE_MAP_NOGC_H +#define CDS_ELLEN_BINTREE_MAP_NOGC_H +#include +#include + +namespace cds { namespace container { + + template < + typename Key, + typename T, +#ifdef CDS_DOXYGEN_INVOKED + class Traits = ellen_bintree::traits +#else + class Traits +#endif + > + class EllenBinTreeMap + : public ellen_bintree::details::make_ellen_bintree_map< gc::nogc, Key, T, Traits >::type + { + typedef ellen_bintree::details::make_ellen_bintree_map< gc::nogc,Key, T, Traits > maker; + typedef typename maker::type base_class; + //@endcond + + public: + typedef cds::gc::nogc gc; ///< Garbage collector + typedef Key key_type; ///< type of a key to be stored in internal nodes; key is a part of \p value_type + typedef T value_type; ///< type of value to be stored in the binary tree + typedef Traits traits; ///< Traits template parameter + +# ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined key_comparator ; ///< key compare functor based on opt::compare and opt::less option setter. +# else + typedef typename maker::intrusive_traits::compare key_comparator; +# endif + typedef typename base_class::item_counter item_counter; ///< Item counting policy used + typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename traits::key_extractor key_extractor; ///< key extracting functor + typedef typename traits::back_off back_off; ///< Back-off strategy + + typedef typename traits::allocator allocator_type; ///< Allocator for leaf nodes + typedef typename base_class::node_allocator node_allocator; ///< Internal node allocator + typedef typename base_class::update_desc_allocator update_desc_allocator; ///< Update descriptor allocator + + protected: + //@cond + + typedef typename maker::cxx_leaf_node_allocator cxx_leaf_node_allocator; + typedef typename base_class::value_type leaf_node; + typedef typename base_class::internal_node internal_node; + + typedef std::unique_ptr< leaf_node, typename maker::leaf_deallocator > scoped_node_ptr; + + public: + /// Default constructor + EllenBinTreeMap() + : base_class() + {} + + /// Clears the map + ~EllenBinTreeMap() + {} + + /// Inserts new node with key and default value + template + bool insert( K const& key ) + { + return insert_with( key, [](value_type&){} ); + } + + /// Inserts new node + template + bool insert( K const& key, V const& val ) + { + scoped_node_ptr pNode( cxx_leaf_node_allocator().New( key, val )); + if ( base_class::insert( *pNode )) + { + pNode.release(); + return true; + } + return false; + } + + // Inserts new node and initialize it by a functor + template + bool insert_with( K const& key, Func func ) + { + scoped_node_ptr pNode( cxx_leaf_node_allocator().New( key )); + if ( base_class::insert( *pNode, [&func]( leaf_node& item ) { func( item.m_Value ); } )) { + pNode.release(); + return true; + } + return false; + } + + /// For key \p key inserts data of type \p value_type created in-place from \p args + template + bool emplace( K&& key, Args&&... args ) + { + scoped_node_ptr pNode( cxx_leaf_node_allocator().MoveNew( key_type( std::forward(key)), mapped_type( std::forward(args)... ))); + if ( base_class::insert( *pNode )) { + pNode.release(); + return true; + } + return false; + } + + /// Updates the node + template + std::pair update( K const& key, Func func, bool bAllowInsert = true ) + { + scoped_node_ptr pNode( cxx_leaf_node_allocator().New( key )); + std::pair res = base_class::update( *pNode, + [&func](bool bNew, leaf_node& item, leaf_node const& ){ func( bNew, item.m_Value ); }, + bAllowInsert + ); + if ( res.first && res.second ) + pNode.release(); + return res; + } + //@cond + template + CDS_DEPRECATED("ensure() is deprecated, use update()") + std::pair ensure( K const& key, Func func ) + { + return update( key, func, true ); + } + //@endcond + + /// Delete \p key from the map + template + bool find( K const& key, Func f ) + { + return base_class::find( key, [&f](leaf_node& item, K const& ) { f( item.m_Value );}); + } + + /// Finds the key \p val using \p pred predicate for searching + template + bool find_with( K const& key, Less pred, Func f ) + { + CDS_UNUSED( pred ); + return base_class::find_with( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::key_accessor >(), + [&f](leaf_node& item, K const& ) { f( item.m_Value );}); + } + + /// Clears the map + void clear() + { + base_class::clear(); + } + + /// Checks whether the map contains \p key + template + bool contains( K const& key ) + { + return base_class::contains( key ); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find( K const& key ) + { + return contains( key ); + } + //@endcond + + /// Checks whether the map contains \p key using \p pred predicate for searching + template + bool contains( K const& key, Less pred ) + { + CDS_UNUSED( pred ); + return base_class::contains( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::key_accessor >()); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find_with( K const& key, Less pred ) + { + return contains( key, pred ); + } + //@endcond + + /// Finds \p key and return the item found + template + value_type * get( Q const& key ) const + { + leaf_node * pNode = base_class::get( key ); + return pNode ? &pNode->m_Value : nullptr; + } + + /// Finds \p key with \p pred predicate and return the item found + template + value_type * get_with( Q const& key, Less pred ) const + { + CDS_UNUSED( pred ); + leaf_node * pNode = base_class::get_with( key, + cds::details::predicate_wrapper< leaf_node, Less, typename maker::key_accessor >()); + return pNode ? &pNode->m_Value : nullptr; + } + + /// Checks if the map is empty + bool empty() const + { + return base_class::empty(); + } + + /// Returns item count in the map + size_t size() const + { + return base_class::size(); + } + + + + + }; + } +} + + +#endif //CDS_ELLEN_BINTREE_MAP_NOGC_H diff --git a/cds/intrusive/ellen_bintree_nogc.h b/cds/intrusive/ellen_bintree_nogc.h new file mode 100644 index 000000000..6ec022114 --- /dev/null +++ b/cds/intrusive/ellen_bintree_nogc.h @@ -0,0 +1,652 @@ +// +// Created by pasha on 23.12.17. +// + +#ifndef MDP_ELLEN_BEEN_TREE_ELLEN_BEENTREE_NOGC2_H +#define MDP_ELLEN_BEEN_TREE_ELLEN_BEENTREE_NOGC2_H +#include +#include +#include +namespace cds { + namespace intrusive { + template + class EllenBinTree + { + public: + typedef gc::nogc gc; ///< Garbage collector + typedef Key key_type; ///< type of a key to be stored in internal nodes; key is a part of \p value_type + typedef T value_type; ///< type of value stored in the binary tree + typedef Traits traits;///< Traits template parameter + + typedef typename traits::hook hook; ///< hook type + typedef typename hook::node_type node_type; ///< node type + typedef typename traits::disposer disposer; ///< leaf node disposer + typedef typename traits::back_off back_off; ///< back-off strategy + protected: + typedef ellen_bintree::base_node< gc > tree_node; + typedef node_type leaf_node; + typedef ellen_bintree::node_types< gc, key_type, typename leaf_node::tag > node_factory; + typedef typename node_factory::internal_node_type internal_node; + typedef ellen_bintree::update_desc< leaf_node, internal_node> update_desc; + typedef typename update_desc::update_ptr update_ptr; + public: +# ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined key_comparator; ///< key compare functor based on \p Traits::compare and \p Traits::less + typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< Node traits +# else + typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator; + struct node_traits: public get_node_traits< value_type, node_type, hook>::type + { + static internal_node const& to_internal_node( tree_node const& n ) + { + assert( n.is_internal()); + return static_cast( n ); + } + + static leaf_node const& to_leaf_node( tree_node const& n ) + { + assert( n.is_leaf()); + return static_cast( n ); + } + }; +# endif + + typedef typename traits::node_allocator node_allocator; + typedef typename traits::item_counter item_counter; + typedef typename traits::memory_model memory_model; + typedef typename traits::stat stat; ///< internal statistics type + typedef typename traits::key_extractor key_extractor; + typedef typename traits::update_desc_allocator update_desc_allocator; + protected: + typedef ellen_bintree::details::compare< key_type, value_type, key_comparator, node_traits > node_compare; + + typedef cds::details::Allocator< internal_node, node_allocator > cxx_node_allocator; + typedef cds::details::Allocator< update_desc, update_desc_allocator > cxx_update_desc_allocator; + struct search_result{ + internal_node *pParent; + leaf_node * pLeaf; + bool bRightLeaf; + update_ptr updParent; + + search_result() + :pParent( nullptr ) + ,pLeaf( nullptr ) + ,bRightLeaf( false ) + {} + }; + protected: + //@cond + internal_node m_Root; ///< Tree root node (key= Infinite2) + leaf_node m_LeafInf1; ///< Infinite leaf 1 (key= Infinite1) + leaf_node m_LeafInf2; ///< Infinite leaf 2 (key= Infinite2) + + item_counter m_ItemCounter; + mutable stat m_Stat; ///< internal statistics + + protected: + + static void free_leaf_node( void* p ) + { + disposer()( reinterpret_cast( p )); + } + + static void free_internal_node( void* pNode ) + { + cxx_node_allocator().Delete( reinterpret_cast( pNode )); + } + + struct internal_node_deleter { + void operator()( internal_node* p) const + { + cxx_node_allocator().Delete( p ); + } + }; + + typedef std::unique_ptr< internal_node, internal_node_deleter> unique_internal_node_ptr; + + internal_node * alloc_internal_node() const + { + m_Stat.onInternalNodeCreated(); + internal_node * pNode = cxx_node_allocator().New(); + return pNode; + } + update_desc * alloc_update_desc() const + { + m_Stat.onUpdateDescCreated(); + return cxx_update_desc_allocator().New(); + } + + static void free_update_desc( void* pDesc ) + { + cxx_update_desc_allocator().Delete( reinterpret_cast( pDesc )); + } + + + + public: + + EllenBinTree() + { + make_empty_tree(); + } + + ~EllenBinTree() + { + clear(); + } + + /// Inserts new node + /** + The function inserts \p val in the tree if it does not contain + an item with key equal to \p val. + + Returns \p true if \p val is placed into the tree, \p false otherwise. + */ + bool insert( value_type& val ) + { + return insert( val, []( value_type& ) {} ); + } + + /// Inserts new node + /** + This function is intended for derived non-intrusive containers. + + The function allows to split creating of new item into two part: + - create item with key only + - insert new item into the tree + - if inserting is success, calls \p f functor to initialize value-field of \p val. + + The functor signature is: + \code + void func( value_type& val ); + \endcode + where \p val is the item inserted. User-defined functor \p f should guarantee that during changing + \p val no any other changes could be made on this tree's item by concurrent threads. + The user-defined functor is called only if the inserting is success. + */ + template + bool insert( value_type& val, Func f ) + { + search_result res; + back_off bkoff; + unique_internal_node_ptr pNewInternal; + + while(true) { + if (search(res, val, node_compare())) { + m_Stat.onInsertFailed(); + return false; // uniq value + } + + if (res.updParent.bits() == update_desc::Clean) { + if (!pNewInternal.get()) + { + pNewInternal.reset(alloc_internal_node()); + } + + if (try_insert(val, pNewInternal.get(), res)) { + f( val ); + pNewInternal.release(); + break; + } + } + bkoff(); + m_Stat.onInsertRetry(); + } + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; + } + + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If the item \p val is not found in the set, then \p val is inserted into the set + iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + void func( bool bNew, value_type& item, value_type& val ); + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - item of the set + - \p val - argument \p val passed into the \p %update() function + If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments + refer to the same thing. + + The functor can change non-key fields of the \p item; however, \p func must guarantee + that during changing no any other modifications could be made on this item by concurrent threads. + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + + @warning See \ref cds_intrusive_item_creating "insert item troubleshooting" + */ + template + std::pair update( value_type& val, Func func, bool bAllowInsert = true ) + { + + unique_internal_node_ptr pNewInternal; + search_result res; + back_off bkoff; + + for ( ;; ) { + if ( search( res, val, node_compare())) { + func( false, *node_traits::to_value_ptr( res.pLeaf ), val ); + m_Stat.onUpdateExist(); + return std::make_pair( true, false ); + } + + if ( res.updParent.bits() == update_desc::Clean ) { + if ( !bAllowInsert ) + return std::make_pair( false, false ); + + if ( !pNewInternal.get()) + pNewInternal.reset( alloc_internal_node()); + + if ( try_insert( val, pNewInternal.get(), res )) { + func( true, val, val ); + pNewInternal.release() ; // internal node has been linked into the tree and should not be deleted + break; + } + } + bkoff(); + m_Stat.onUpdateRetry(); + } + + ++m_ItemCounter; + m_Stat.onUpdateNew(); + return std::make_pair( true, true ); + } + + /// Checks whether the set contains \p key + /** + The function searches the item with key equal to \p key + and returns \p true if it is found, and \p false otherwise. + */ + template + bool contains( Q const& key ) const + { + search_result res; + if ( search( res, key, node_compare())) { + m_Stat.onFindSuccess(); + return true; + } + m_Stat.onFindFailed(); + return false; + } + + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find( Q const& key ) + { + return contains( key ); + } + //@endcond + + /// Checks whether the set contains \p key using \p pred predicate for searching + /** + The function is similar to contains( key ) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p Less must imply the same element order as the comparator used for building the set. + */ + template + bool contains( Q const& key, Less pred ) const + { + typedef ellen_bintree::details::compare< + key_type, + value_type, + opt::details::make_comparator_from_less, + node_traits + > compare_functor; + + search_result res; + if ( search( res, key, compare_functor())) { + m_Stat.onFindSuccess(); + return true; + } + m_Stat.onFindFailed(); + return false; + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find_with( Q const& key, Less pred ) const + { + return contains( key, pred ); + } + //@endcond + + /// Finds the key \p key + /** @anchor cds_intrusive_EllenBinTree_find_func + The function searches the item with key equal to \p key and calls the functor \p f for item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item, Q& key ); + }; + \endcode + where \p item is the item found, \p key is the find function argument. + + The functor can change non-key fields of \p item. Note that the functor is only guarantee + that \p item cannot be disposed during functor is executing. + The functor does not serialize simultaneous access to the tree \p item. If such access is + possible you must provide your own synchronization schema on item level to exclude unsafe item modifications. + + The function returns \p true if \p key is found, \p false otherwise. + */ + template + bool find( Q& key, Func f ) const + { + return find_( key, f ); + } + //@cond + template + bool find( Q const& key, Func f ) const + { + return find_( key, f ); + } + //@endcond + /// Finds the key \p key with comparing functor \p pred + /** + The function is an analog of \ref cds_intrusive_EllenBinTree_find_func "find(Q&, Func)" + but \p pred is used for key comparison. + \p Less functor has the interface like \p std::less and should meet \ref cds_intrusive_EllenBinTree_less + "Predicate requirements". + \p pred must imply the same element order as the comparator used for building the tree. + */ + //@cond + template + bool find_with( Q const& key, Less pred, Func f ) const + { + return find_with_( key, pred, f ); + } + //@endcond + + /// Checks if the tree is empty + bool empty() const + { + return m_Root.m_pLeft.load( memory_model::memory_order_relaxed )->is_leaf(); + } + + /// Clears the tree + void clear() + { + while ( true ) { + internal_node * pParent = nullptr; + internal_node * pGrandParent = nullptr; + tree_node * pLeaf = const_cast( &m_Root ); + + // Get leftmost leaf + while ( pLeaf->is_internal()) { + pGrandParent = pParent; + pParent = static_cast( pLeaf ); + pLeaf = pParent->m_pLeft.load( memory_model::memory_order_relaxed ); + } + + if ( pLeaf->infinite_key()) { + m_ItemCounter.reset(); + return; + } + + // Remove leftmost leaf and its parent node + assert( pGrandParent ); + assert( pParent ); + assert( pLeaf->is_leaf()); + + pGrandParent->m_pLeft.store( pParent->m_pRight.load( memory_model::memory_order_relaxed ), memory_model::memory_order_relaxed ); + free_leaf_node( node_traits::to_value_ptr( static_cast( pLeaf ))); + free_internal_node( pParent ); + m_ItemCounter--; + } + } + + /// Returns item count in the tree + /** + Only leaf nodes containing user data are counted. + + The value returned depends on item counter type provided by \p Traits template parameter. + If it is \p atomicity::empty_item_counter this function always returns 0. + The function is not suitable for checking the tree emptiness, use \p empty() + member function for this purpose. + */ + size_t size() const + { + return m_ItemCounter; + } + + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + + /// Checks internal consistency (not atomic, not thread-safe) + /** + The debugging function to check internal consistency of the tree. + */ + bool check_consistency() const + { + return check_consistency( &m_Root ); + } + + protected: + //@cond + + bool check_consistency( internal_node const * pRoot ) const + { + tree_node * pLeft = pRoot->m_pLeft.load( atomics::memory_order_relaxed ); + tree_node * pRight = pRoot->m_pRight.load( atomics::memory_order_relaxed ); + assert( pLeft ); + assert( pRight ); + + if ( node_compare()( *pLeft, *pRoot ) < 0 + && node_compare()( *pRoot, *pRight ) <= 0 + && node_compare()( *pLeft, *pRight ) < 0 ) + { + bool bRet = true; + if ( pLeft->is_internal()) + bRet = check_consistency( static_cast( pLeft )); + assert( bRet ); + + if ( bRet && pRight->is_internal()) + bRet = bRet && check_consistency( static_cast( pRight )); + assert( bRet ); + + return bRet; + } + return false; + } + + template + bool search(search_result& res, KeyValue const& key, Compare cmp) const + { + internal_node * pParent; + tree_node * pLeaf; + update_ptr updParent; + + bool bRightLeaf; + bool bRightParent = false; + + int nCmp = 0; + + pParent = nullptr; + pLeaf = const_cast( &m_Root ); + updParent = nullptr; + bRightLeaf = false; + while ( pLeaf->is_internal()) + { + pParent = static_cast( pLeaf ); + bRightParent = bRightLeaf; + updParent = pParent->m_pUpdate.load( memory_model::memory_order_acquire ); + nCmp = cmp( key, *pParent ); + bRightLeaf = nCmp >= 0; + pLeaf = pParent->get_child( nCmp >= 0, memory_model::memory_order_acquire ); + }; + assert( pLeaf->is_leaf()); + + leaf_node* keyValue = static_cast( pLeaf ); + nCmp = cmp( key, *static_cast(pLeaf)); + + res.pParent = pParent; + res.pLeaf = static_cast( pLeaf ); + res.updParent = updParent; + res.bRightLeaf = bRightLeaf; + return nCmp == 0; + } + + void help_insert( update_desc * pOp ) + { + tree_node * pLeaf = static_cast( pOp->iInfo.pLeaf ); + if ( pOp->iInfo.bRightLeaf ) { + pOp->iInfo.pParent->m_pRight.compare_exchange_strong( pLeaf, static_cast( pOp->iInfo.pNew ), + memory_model::memory_order_release, atomics::memory_order_relaxed ); + } + else { + pOp->iInfo.pParent->m_pLeft.compare_exchange_strong( pLeaf, static_cast( pOp->iInfo.pNew ), + memory_model::memory_order_release, atomics::memory_order_relaxed ); + } + + // Unflag parent + update_ptr cur( pOp, update_desc::IFlag ); + CDS_VERIFY( pOp->iInfo.pParent->m_pUpdate.compare_exchange_strong( cur, pOp->iInfo.pParent->null_update_desc(), + memory_model::memory_order_release, atomics::memory_order_relaxed )); + } + + bool try_insert( value_type& val, internal_node * pNewInternal, search_result& res ) + { + assert( res.updParent.bits() == update_desc::Clean ); + assert( res.pLeaf->is_leaf()); + + if ( static_cast( res.pParent->get_child( res.bRightLeaf, memory_model::memory_order_relaxed )) == res.pLeaf) + { + leaf_node * pNewLeaf = node_traits::to_node_ptr( val ); + + int nCmp = node_compare()(val, *res.pLeaf); + if ( nCmp < 0 ) + { + if( res.pLeaf->infinite_key() ) + { + pNewInternal->infinite_key( 1 ); + } + else + { + pNewInternal->infinite_key( 0 ); + key_extractor()(pNewInternal->m_Key, *node_traits::to_value_ptr( res.pLeaf )); + } + pNewInternal->m_pLeft.store( static_cast(pNewLeaf), memory_model::memory_order_relaxed ); + pNewInternal->m_pRight.store( static_cast(res.pLeaf), memory_model::memory_order_relaxed ); + } + else + { + pNewInternal->infinite_key( 0 ); + key_extractor()(pNewInternal->m_Key, val); + pNewInternal->m_pLeft.store( static_cast(res.pLeaf), memory_model::memory_order_relaxed ); + pNewInternal->m_pRight.store( static_cast(pNewLeaf), memory_model::memory_order_relaxed ); + } + update_desc * pOp = alloc_update_desc(); + + pOp->iInfo.pParent = res.pParent; + pOp->iInfo.pNew = pNewInternal; + pOp->iInfo.pLeaf = res.pLeaf; + pOp->iInfo.bRightLeaf = res.bRightLeaf; + + update_ptr updCur( res.updParent.ptr()); + + if ( res.pParent->m_pUpdate.compare_exchange_strong( updCur, update_ptr( pOp, update_desc::IFlag ), + memory_model::memory_order_acq_rel, atomics::memory_order_acquire )) + { + + // do insert + help_insert( pOp ); + return true; + } + else + { + free_update_desc( pOp ); + } + } + return false; + } + + + + + + + + + + + + + + + template + bool find_with( Q& key, Less pred, Func f ) const + { + return find_with_( key, pred, f ); + } + + + + + + + + + template + bool find_with_( Q& val, Less pred, Func f ) const + { + typedef ellen_bintree::details::compare< + key_type, + value_type, + opt::details::make_comparator_from_less, + node_traits + > compare_functor; + + search_result res; + if ( search( res, val, compare_functor())) { + assert( res.pLeaf ); + f( *node_traits::to_value_ptr( res.pLeaf ), val ); + m_Stat.onFindSuccess(); + return true; + } + m_Stat.onFindFailed(); + return false; + } + + template + bool find_( Q& val, Func f ) const + { + search_result res; + if ( search( res, val, node_compare())) { + f( *node_traits::to_value_ptr( res.pLeaf ), val ); + m_Stat.onFindSuccess(); + return true; + } + m_Stat.onFindFailed(); + return false; + } + + + void make_empty_tree() + { + m_Root.infinite_key( 2 ); + m_LeafInf1.infinite_key( 1 ); + m_LeafInf2.infinite_key( 2 ); + m_Root.m_pLeft.store( &m_LeafInf1, memory_model::memory_order_relaxed ); + m_Root.m_pRight.store( &m_LeafInf2, memory_model::memory_order_release ); + } + }; + } +} +#endif //MDP_ELLEN_BEEN_TREE_ELLEN_BEENTREE_NOGC2_H diff --git a/test/stress/map/find_string/map_find_string_ellentree.cpp b/test/stress/map/find_string/map_find_string_ellentree.cpp index 9cd3da26f..43e5371b2 100644 --- a/test/stress/map/find_string/map_find_string_ellentree.cpp +++ b/test/stress/map/find_string/map_find_string_ellentree.cpp @@ -34,5 +34,6 @@ namespace map { CDSSTRESS_EllenBinTreeMap( Map_find_string, run_test, std::string, Map_find_string::value_type ) + CDSSTRESS_EllenBinTreeMapNOGC( Map_find_string, run_test, std::string, Map_find_string::value_type ) } // namespace map diff --git a/test/stress/map/map_type_ellen_bintree.h b/test/stress/map/map_type_ellen_bintree.h index 3c5b02de2..da55f1724 100644 --- a/test/stress/map/map_type_ellen_bintree.h +++ b/test/stress/map/map_type_ellen_bintree.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "framework/ellen_bintree_update_desc_pool.h" @@ -49,7 +50,7 @@ namespace map { public: template EllenBinTreeMap( Config const& /*cfg*/) - : base_class() + : base_class() {} std::pair extract_min_key() @@ -109,6 +110,12 @@ namespace map { typedef cc::ellen_bintree::internal_node< Key, leaf_node > internal_node; typedef cc::ellen_bintree::update_desc< leaf_node, internal_node > update_desc; }; + + struct no_gc { + typedef cc::ellen_bintree::map_node leaf_node; + typedef cc::ellen_bintree::internal_node< Key, leaf_node > internal_node; + typedef cc::ellen_bintree::update_desc< leaf_node, internal_node > update_desc; + }; #ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED struct shb { typedef cc::ellen_bintree::map_node leaf_node; @@ -122,7 +129,7 @@ namespace map { co::less< less > ,co::node_allocator< ellen_bintree_pool::internal_node_allocator< int > > ,co::item_counter< cds::atomicity::cache_friendly_item_counter > - >::type + >::type {}; struct traits_EllenBinTreeMap_hp : traits_EllenBinTreeMap { typedef cds::memory::pool_allocator< typename ellen_bintree_props::hp_gc::update_desc, ellen_bintree_pool::update_desc_pool_accessor > update_desc_allocator; @@ -149,6 +156,11 @@ namespace map { }; typedef EllenBinTreeMap< rcu_gpt, Key, Value, traits_EllenBinTreeMap_gpt > EllenBinTreeMap_rcu_gpt; + struct traits_EllenBinTreeMap_nogc : traits_EllenBinTreeMap { + typedef cds::memory::pool_allocator< typename ellen_bintree_props::no_gc::update_desc, ellen_bintree_pool::update_desc_pool_accessor > update_desc_allocator; + }; + typedef EllenBinTreeMap EllenBinTreeMap_nogc; + #ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED struct traits_EllenBinTreeMap_shb : traits_EllenBinTreeMap { typedef cds::memory::pool_allocator< typename ellen_bintree_props::shb::update_desc, ellen_bintree_pool::update_desc_pool_accessor > update_desc_allocator; @@ -179,12 +191,12 @@ namespace map { struct traits_EllenBinTreeMap_stat: public cc::ellen_bintree::make_set_traits< co::less< less > ,cc::ellen_bintree::update_desc_allocator< - cds::memory::pool_allocator< typename ellen_bintree_props::hp_gc::update_desc, ellen_bintree_pool::update_desc_pool_accessor > + cds::memory::pool_allocator< typename ellen_bintree_props::hp_gc::update_desc, ellen_bintree_pool::update_desc_pool_accessor > > ,co::node_allocator< ellen_bintree_pool::internal_node_allocator< int > > ,co::stat< cc::ellen_bintree::stat<> > ,co::item_counter< cds::atomicity::cache_friendly_item_counter > - >::type + >::type {}; struct traits_EllenBinTreeMap_stat_hp : public traits_EllenBinTreeMap_stat @@ -217,6 +229,13 @@ namespace map { }; typedef EllenBinTreeMap< rcu_gpt, Key, Value, traits_EllenBinTreeMap_stat_gpt > EllenBinTreeMap_rcu_gpt_stat; + struct traits_EllenBinTreeMap_stat_nogc : public traits_EllenBinTreeMap_stat + { + typedef cds::memory::pool_allocator< typename ellen_bintree_props::no_gc::update_desc, ellen_bintree_pool::update_desc_pool_accessor > update_desc_allocator; + }; + typedef EllenBinTreeMap< cds::gc::nogc, Key, Value, traits_EllenBinTreeMap_stat_nogc > EllenBinTreeMap_nogc_stat; + + #ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED struct traits_EllenBinTreeMap_stat_shb : public traits_EllenBinTreeMap_stat { @@ -225,7 +244,6 @@ namespace map { typedef EllenBinTreeMap< rcu_shb, Key, Value, traits_EllenBinTreeMap_stat_shb > EllenBinTreeMap_rcu_shb_stat; #endif }; - template static inline void print_stat( cds_test::property_stream& o, EllenBinTreeMap const& s ) { @@ -267,6 +285,7 @@ namespace map { { EXPECT_TRUE( m.check_consistency()); } + } // namespace map @@ -317,8 +336,15 @@ namespace map { CDSSTRESS_EllenBinTreeMap_case( fixture, test_case, EllenBinTreeMap_rcu_gpt_stat, key_type, value_type ) \ CDSSTRESS_EllenBinTreeMap_RCU_1( fixture, test_case, key_type, value_type ) \ +#define CDSSTRESS_EllenBinTreeMap_NOGC( fixture, test_case, key_type, value_type ) \ + CDSSTRESS_EllenBinTreeMap_case( fixture, test_case, EllenBinTreeMap_nogc, key_type, value_type ) \ + + #define CDSSTRESS_EllenBinTreeMap( fixture, test_case, key_type, value_type ) \ CDSSTRESS_EllenBinTreeMap_HP( fixture, test_case, key_type, value_type ) \ CDSSTRESS_EllenBinTreeMap_RCU( fixture, test_case, key_type, value_type ) \ +#define CDSSTRESS_EllenBinTreeMapNOGC( fixture, test_case, key_type, value_type ) \ + CDSSTRESS_EllenBinTreeMap_NOGC( fixture, test_case, key_type, value_type ) \ + #endif // ifndef CDSUNIT_MAP_TYPE_ELLEN_BINTREE_H diff --git a/test/unit/tree/CMakeLists.txt b/test/unit/tree/CMakeLists.txt index 3f0e473ee..f52c511e8 100644 --- a/test/unit/tree/CMakeLists.txt +++ b/test/unit/tree/CMakeLists.txt @@ -1,7 +1,7 @@ set(PACKAGE_NAME unit-tree) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof") - + set(CDSGTEST_TREE_SOURCES ../main.cpp bronson_avltree_map_rcu_gpb.cpp @@ -31,6 +31,8 @@ set(CDSGTEST_TREE_SOURCES intrusive_ellenbintree_rcu_gpi.cpp intrusive_ellenbintree_rcu_gpt.cpp intrusive_ellenbintree_rcu_shb.cpp + intrusive_ellen_bintree_nogc.cpp + ) include_directories( diff --git a/test/unit/tree/intrusive_ellen_bintree_nogc.cpp b/test/unit/tree/intrusive_ellen_bintree_nogc.cpp new file mode 100644 index 000000000..2eec504f7 --- /dev/null +++ b/test/unit/tree/intrusive_ellen_bintree_nogc.cpp @@ -0,0 +1,46 @@ +// +// Created by pasha on 24.12.17. +// + +#ifndef MDP_ELLEN_BEEN_TREE_TEST_INTRUSIVE_ELLEN_BINTREE_NOGC_H +#define MDP_ELLEN_BEEN_TREE_TEST_INTRUSIVE_ELLEN_BINTREE_NOGC_H +#include "cds/intrusive/ellen_bintree_nogc.h" +#include "test_intrusive_tree_nogc.h" + + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::nogc gc_type; + class IntrusiveEllenBinTreeNogc: public cds_test::intrusive_tree_nogc + { + public: + typedef intrusive_tree base_class; + + typedef base_class::key_type key_type; + typedef typename base_class::base_int_item< ci::ellen_bintree::node> base_item_type; + typedef typename base_class::member_int_item< ci::ellen_bintree::node> member_item_type; + + struct generic_traits: public ci::ellen_bintree::traits + { + typedef base_class::key_extractor key_extractor; + typedef mock_disposer disposer; + }; + }; + + TEST_F( IntrusiveEllenBinTreeNogc, base_cmp ) + { + typedef ci::EllenBinTree + ,ci::opt::hook< ci::ellen_bintree::base_hook< ci::opt::gc< gc_type >>> + ,ci::opt::compare< cmp> + >::type + > tree_type; + + tree_type t; + test( t ); + } + +} + +#endif //MDP_ELLEN_BEEN_TREE_TEST_INTRUSIVE_ELLEN_BINTREE_NOGC_H diff --git a/test/unit/tree/test_intrusive_tree_nogc.h b/test/unit/tree/test_intrusive_tree_nogc.h new file mode 100644 index 000000000..1e0f09a96 --- /dev/null +++ b/test/unit/tree/test_intrusive_tree_nogc.h @@ -0,0 +1,138 @@ +// +// Created by pasha on 24.12.17. +// + +#ifndef MDP_ELLEN_BEEN_TREE_TEST_INTRUSIVE_TREE_NOGC_H +#define MDP_ELLEN_BEEN_TREE_TEST_INTRUSIVE_TREE_NOGC_H + +#include "test_intrusive_tree.h" +#include "cds_test/fixture.h" +#include +namespace cds { namespace intrusive {}} + +namespace cds_test { + + namespace ci = cds::intrusive; + + class intrusive_tree_nogc : public intrusive_tree { + typedef intrusive_tree base_class; + + protected: + template + void test( Tree& t ) + { + ASSERT_TRUE( t.empty()); + ASSERT_CONTAINER_SIZE( t, 0 ); + size_t const nTreeSize = kSize; + + typedef typename Tree::value_type value_type; + + std::vector< value_type > data; + std::vector< size_t > indices; + + data.reserve( kSize ); + indices.reserve( kSize ); + for (size_t key = 0; key < kSize; ++key) { + data.push_back( value_type( static_cast( key ))); + indices.push_back( key ); + } + shuffle( indices.begin(), indices.end()); + // insert/find + for ( auto idx : indices ) { + auto& i = data[ idx ]; + + ASSERT_FALSE( t.contains( i.nKey )); + ASSERT_FALSE( t.contains( i )); + ASSERT_FALSE( t.contains( other_item( i.key()), other_less())); + ASSERT_FALSE( t.find( i.nKey, []( value_type&, int ) {} )); + ASSERT_FALSE( t.find_with( other_item( i.key()), other_less(), []( value_type&, other_item const& ) {} )); + + std::pair updResult; + + updResult = t.update( i, []( bool, value_type&, value_type& ) + { + ASSERT_TRUE( false ); + }, false ); + EXPECT_FALSE( updResult.first ); + EXPECT_FALSE( updResult.second ); + + switch ( i.key() % 3 ) { + case 0: + ASSERT_TRUE( t.insert( i )); + ASSERT_FALSE( t.insert( i )); + updResult = t.update( i, []( bool bNew, value_type& val, value_type& arg) + { + EXPECT_FALSE( bNew ); + EXPECT_EQ( &val, &arg ); + }, false ); + EXPECT_TRUE( updResult.first ); + EXPECT_FALSE( updResult.second ); + break; + case 1: + EXPECT_EQ( i.nUpdateNewCount, 0u ); + ASSERT_TRUE( t.insert( i, []( value_type& v ) { ++v.nUpdateNewCount;} )); + EXPECT_EQ( i.nUpdateNewCount, 1u ); + ASSERT_FALSE( t.insert( i, []( value_type& v ) { ++v.nUpdateNewCount;} )); + EXPECT_EQ( i.nUpdateNewCount, 1u ); + i.nUpdateNewCount = 0; + break; + case 2: + updResult = t.update( i, []( bool, value_type&, value_type& ) + { + EXPECT_TRUE( false ); + }, false ); + EXPECT_FALSE( updResult.first ); + EXPECT_FALSE( updResult.second ); + + EXPECT_EQ( i.nUpdateNewCount, 0u ); + updResult = t.update( i, []( bool bNew, value_type& val, value_type& arg ) + { + EXPECT_TRUE( bNew ); + EXPECT_EQ( &val, &arg ); + ++val.nUpdateNewCount; + }); + EXPECT_TRUE( updResult.first ); + EXPECT_TRUE( updResult.second ); + EXPECT_EQ( i.nUpdateNewCount, 1u ); + i.nUpdateNewCount = 0; + + EXPECT_EQ( i.nUpdateCount, 0u ); + updResult = t.update( i, []( bool bNew, value_type& val, value_type& arg ) + { + EXPECT_FALSE( bNew ); + EXPECT_EQ( &val, &arg ); + ++val.nUpdateCount; + }, false ); + EXPECT_TRUE( updResult.first ); + EXPECT_FALSE( updResult.second ); + EXPECT_EQ( i.nUpdateCount, 1u ); + i.nUpdateCount = 0; + + break; + } + + ASSERT_TRUE( t.contains( i.nKey )); + ASSERT_TRUE( t.contains( i )); + ASSERT_TRUE( t.contains( other_item( i.key()), other_less())); + EXPECT_EQ( i.nFindCount, 0u ); + ASSERT_TRUE( t.find( i.nKey, []( value_type& v, int ) { ++v.nFindCount; } )); + EXPECT_EQ( i.nFindCount, 1u ); + ASSERT_TRUE( t.find_with( other_item( i.key()), other_less(), []( value_type& v, other_item const& ) { ++v.nFindCount; } )); + EXPECT_EQ( i.nFindCount, 2u ); + ASSERT_TRUE( t.find( i, []( value_type& v, value_type& ) { ++v.nFindCount; } )); + EXPECT_EQ( i.nFindCount, 3u ); + } + + ASSERT_FALSE( t.empty()); + ASSERT_CONTAINER_SIZE( t, nTreeSize ); + + std::for_each( data.begin(), data.end(), []( value_type& v ) { v.clear_stat(); }); + + } + + + }; +} + + +#endif //MDP_ELLEN_BEEN_TREE_TEST_INTRUSIVE_TREE_NOGC_H \ No newline at end of file