From a2c7188a90b743800c6ae9e6919fd213d35d3f45 Mon Sep 17 00:00:00 2001 From: Borsuk Pavel Date: Wed, 27 Dec 2017 08:39:39 +0300 Subject: [PATCH 01/10] ellen bintree nogc --- cds/container/details/ellen_bintree_base.h | 49 ++ cds/container/ellen_bintree_set_nogc.h | 311 +++++++++++ cds/intrusive/ellen_bintree_nogc.h | 483 ++++++++++++++++++ test/unit/tree/CMakeLists.txt | 1 + .../test_intrusive_ellen_bintree_nogc.cpp | 46 ++ test/unit/tree/test_intrusive_tree_nogc.h | 138 +++++ 6 files changed, 1028 insertions(+) create mode 100644 cds/container/ellen_bintree_set_nogc.h create mode 100644 cds/intrusive/ellen_bintree_nogc.h create mode 100644 test/unit/tree/test_intrusive_ellen_bintree_nogc.cpp create mode 100644 test/unit/tree/test_intrusive_tree_nogc.h diff --git a/cds/container/details/ellen_bintree_base.h b/cds/container/details/ellen_bintree_base.h index 5daf245ac..df286c081 100644 --- a/cds/container/details/ellen_bintree_base.h +++ b/cds/container/details/ellen_bintree_base.h @@ -380,6 +380,55 @@ namespace cds { namespace container { typedef cds::intrusive::EllenBinTree< gc, key_type, leaf_node, intrusive_traits > type; }; + template < class GC, typename Key, typename T, class Traits> + struct make_ellen_bintree_set_nogc + { + typedef GC gc; + typedef Key key_type; + typedef T value_type; + typedef Traits original_traits; + + typedef node< gc, value_type > leaf_node; + + struct intrusive_key_extractor + { + void operator()( key_type& dest, leaf_node const& src ) const + { + typename original_traits::key_extractor()( dest, src.m_Value ); + } + }; + + struct value_accessor + { + value_type const& operator()( leaf_node const& node ) const + { + return node.m_Value; + } + }; + + typedef typename cds::opt::details::make_comparator< value_type, original_traits, false >::type key_comparator; + + typedef cds::details::Allocator< leaf_node, typename original_traits::allocator> cxx_leaf_node_allocator; + struct leaf_deallocator + { + void operator()( leaf_node * p ) const + { + cxx_leaf_node_allocator().Delete( p ); + } + }; + + struct intrusive_traits: public original_traits + { + typedef cds::intrusive::ellen_bintree::base_hook< cds::opt::gc< gc >> hook; + typedef intrusive_key_extractor key_extractor; + typedef leaf_deallocator disposer; + typedef cds::details::compare_wrapper< leaf_node, key_comparator, value_accessor > compare; + }; + + // Metafunction result + typedef cds::intrusive::EllenBinTreeNogc< key_type, leaf_node, intrusive_traits > type; + }; + template < class GC, typename Key, typename T, class Traits> struct make_ellen_bintree_map { diff --git a/cds/container/ellen_bintree_set_nogc.h b/cds/container/ellen_bintree_set_nogc.h new file mode 100644 index 000000000..ff45b951e --- /dev/null +++ b/cds/container/ellen_bintree_set_nogc.h @@ -0,0 +1,311 @@ +// +// Created by pasha on 27.12.17. +// + +#ifndef CDS_ELLEN_BINTREE_SET_NOGC_H +#define CDS_ELLEN_BINTREE_SET_NOGC_H +#include +#include +#include +#include "../intrusive/ellen_bintree_nogc.h" + +namespace cds { namespace container { + + template < + typename Key, + typename T, +#ifdef CDS_DOXYGEN_INVOKED + class Traits = ellen_bintree::traits +#else + class Traits +#endif + > + class EllenBinTreeSetNogc: public ellen_bintree::details::make_ellen_bintree_set_nogc< gc::nogc, + Key, T, Traits >::type + { + typedef ellen_bintree::details::make_ellen_bintree_set_nogc< gc::nogc, Key, T, Traits > maker; + typedef typename maker::type base_class; + //@endcond + + 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 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 + EllenBinTreeSetNogc() + : base_class() + {} + + /// Clears the set + ~EllenBinTreeSetNogc() + {} + + + + /// Inserts new node + /** + The function creates a node with copy of \p val value + and then inserts the node created into the set. + + The type \p Q should contain at least the complete key for the node. + The object of \ref value_type should be constructible from a value of type \p Q. + In trivial case, \p Q is equal to \ref value_type. + + Returns \p true if \p val is inserted into the set, \p false otherwise. + */ + template + bool insert( Q const& val ) + { + /* scoped_node_ptr sp( cxx_leaf_node_allocator().New( val )); + if ( base_class::insert( *sp.get())) { + sp.release(); + return true; + }*/ + return false; + } + + /// Inserts new node + /** + The function allows to split creating of new item into two part: + - create item with key only + - insert new item into the set + - if inserting is success, calls \p f functor to initialize value-fields 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 set's item by concurrent threads. + The user-defined functor is called only if the inserting is success. + */ + template + bool insert( Q const& val, Func f ) + { + /*scoped_node_ptr sp( cxx_leaf_node_allocator().New( val )); + if ( base_class::insert( *sp.get(), [&f]( leaf_node& v ) { f( v.m_Value ); } )) { + sp.release(); + return true; + }*/ + return false; + } + + /// 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 + struct my_functor { + void operator()( bool bNew, value_type& item, const Q& val ); + }; + \endcode + with arguments: + 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 key passed into the \p %update() function + + 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( const Q& val, Func func, bool bAllowInsert = true ) + { + scoped_node_ptr sp( cxx_leaf_node_allocator().New( val )); + std::pair bRes = base_class::update( *sp, + [&func, &val](bool bNew, leaf_node& node, leaf_node&){ func( bNew, node.m_Value, val ); }, + bAllowInsert ); + if ( bRes.first && bRes.second ) + sp.release(); + return bRes; + } + //@cond + template + CDS_DEPRECATED("ensure() is deprecated, use update()") + std::pair ensure( const Q& val, Func func ) + { + return update( val, func, true ); + } + //@endcond + + /// Inserts data of type \p value_type created in-place from \p args + /** + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool emplace( Args&&... args ) + { + scoped_node_ptr sp( cxx_leaf_node_allocator().MoveNew( std::forward(args)... )); + if ( base_class::insert( *sp.get())) { + sp.release(); + return true; + } + return false; + } + + /// Find the key \p key + /** + @anchor cds_nonintrusive_EllenBinTreeSet_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 may 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 set's \p item. If such access is + possible you must provide your own synchronization schema on item level to exclude unsafe item modifications. + + The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor + can modify both arguments. + + Note the hash functor specified for class \p Traits template parameter + should accept a parameter of type \p Q that may be not the same as \p value_type. + + The function returns \p true if \p key is found, \p false otherwise. + */ + template + bool find( Q& key, Func f ) + { + return base_class::find( key, [&f]( leaf_node& node, Q& v ) { f( node.m_Value, v ); }); + } + //@cond + template + bool find( Q const& key, Func f ) + { + return base_class::find( key, [&f]( leaf_node& node, Q const& v ) { f( node.m_Value, v ); } ); + } + //@endcond + + /// Finds the key \p key using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_EllenBinTreeSet_find_func "find(Q&, Func)" + 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 find_with( Q& key, Less pred, Func f ) + { + CDS_UNUSED( pred ); + return base_class::find_with( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::value_accessor >(), + [&f]( leaf_node& node, Q& v ) { f( node.m_Value, v ); } ); + } + //@cond + template + bool find_with( Q const& key, Less pred, Func f ) + { + CDS_UNUSED( pred ); + return base_class::find_with( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::value_accessor >(), + [&f]( leaf_node& node, Q const& v ) { f( node.m_Value, v ); } ); + } + //@endcond + + /// 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 ) + { + return base_class::contains( key ); + } + //@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 ) + { + CDS_UNUSED( pred ); + return base_class::contains( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::value_accessor >()); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find_with( Q const& key, Less pred ) + { + return contains( key, pred ); + } + //@endcond + + /// Checks if the set is empty + bool empty() const + { + return base_class::empty(); + } + + /// Returns item count in the set + /** + 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 base_class::size(); + } + }; + } +} + + + +#endif //CDS_ELLEN_BINTREE_SET_NOGC_H diff --git a/cds/intrusive/ellen_bintree_nogc.h b/cds/intrusive/ellen_bintree_nogc.h new file mode 100644 index 000000000..6f34baf2d --- /dev/null +++ b/cds/intrusive/ellen_bintree_nogc.h @@ -0,0 +1,483 @@ +// +// 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 GC, + typename Key, + typename T, + class Traits + > + class EllenBinTreeNogc + { + public: + typedef gc::nogc gc; + typedef Key key_type; + typedef T value_type; + typedef Traits traits; + + typedef typename traits::hook hook; + typedef typename traits::node_allocator node_allocator; + typedef typename traits::memory_model memory_model; + typedef typename traits::key_extractor key_extractor; + typedef typename traits::back_off back_off; + typedef typename traits::update_desc_allocator update_desc_allocator; + typedef typename hook::node_type node_type; + + + + 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; + public: + typedef typename node_factory::internal_node_type internal_node; + protected: + typedef ellen_bintree::update_desc< leaf_node, internal_node> update_desc; + typedef typename update_desc::update_ptr update_ptr; + + typedef cds::details::Allocator< internal_node, node_allocator > cxx_node_allocator; + public: + typedef typename traits::item_counter item_counter; + 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 ); + } + }; + + 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; + + public: + typedef ellen_bintree::details::compare< key_type, value_type, key_comparator, node_traits > node_compare; + protected: + typedef cds::details::Allocator< update_desc, update_desc_allocator > cxx_update_desc_allocator; + struct search_result{ + internal_node * pGrandParent; + internal_node *pParent; + leaf_node * pLeaf; + bool bRightLeaf; // true if pLeaf is right child of pParent, false otherwise + // bool bRightParent; // true if pParent is right child of pGrandParent, false otherwise + + update_ptr updParent; + search_result() + :pGrandParent( nullptr ) + ,pParent( nullptr ) + ,pLeaf( nullptr ) + ,bRightLeaf( false ) +// ,bRightParent( false ) + {} + }; + + struct internal_node_deleter { + void operator()( internal_node* p) const + { + cxx_node_allocator().Delete( p ); + } + }; + + internal_node * alloc_internal_node() const + { + internal_node * pNode = cxx_node_allocator().New(); + return pNode; + } + update_desc * alloc_update_desc() const + { + return cxx_update_desc_allocator().New(); + } + + static void free_update_desc( void* pDesc ) + { + cxx_update_desc_allocator().Delete( reinterpret_cast( pDesc )); + } + + typedef std::unique_ptr< internal_node, internal_node_deleter> unique_internal_node_ptr; + + + + public: + + EllenBinTreeNogc() + { + //static_assert( !std::is_same< key_extractor, opt::none >::value, "The key extractor option must be specified" ); + make_empty_tree(); + } + + + + template + bool search(search_result& res, KeyValue const& key, Compare cmp) const + { + internal_node * pParent; + internal_node * pGrandParent = nullptr; + tree_node * pLeaf; + update_ptr updParent; + + bool bRightLeaf; + bool bRightParent = false; + + int nCmp = 0; + + + retry: + + pParent = nullptr; + pLeaf = const_cast( &m_Root ); + updParent = nullptr; + bRightLeaf = false; + while ( pLeaf->is_internal()) + { + pGrandParent = pParent; + 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; + res.pGrandParent = pGrandParent; + + return nCmp == 0; + } + + + 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())) { + 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; + } + } + int z = res.updParent.bits(); + bkoff(); + } + //my_printf_tree(m_Root); + ++m_ItemCounter; + return true; + } + + bool insert( value_type& val ) + { + return insert( val, []( value_type& ) {} ); + } + + + bool empty() const + { + return m_Root.m_pLeft.load( memory_model::memory_order_relaxed )->is_leaf(); + } + + size_t size() const + { + return m_ItemCounter; + } + + template + bool contains( Q const& key ) const + { + search_result res; + if ( search( res, key, node_compare())) { + return true; + } + + return false; + } + + + 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())) { + return true; + } + return false; + } + + template + bool find( Q const& key ) + { + return contains( key ); + } + + void my_printf_tree (internal_node nodePrint) + { + + internal_node * pParent; + tree_node * left; + tree_node * right; + tree_node * pLeaf; + leaf_node * leaf; + + pLeaf = const_cast( &nodePrint ); + while ( pLeaf->is_internal()) + { + pParent = static_cast( pLeaf ); + left = pParent->get_child( false, memory_model::memory_order_acquire ); + right = pParent->get_child( true, memory_model::memory_order_acquire ); + my_printf_tree(*(static_cast( left ))); + my_printf_tree(*(static_cast( right ))); + }; + + leaf = static_cast(pLeaf ); + //TUT LEAF + } + + 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 ); + } + + template + bool find_with( Q& key, Less pred, Func f ) const + { + return find_with_( key, pred, f ); + } + + template + bool find_with( Q const& key, Less pred, Func f ) const + { + return find_with_( key, pred, f ); + } + + 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 ); + 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_ItemCounter; + return std::make_pair( true, true ); + } + + protected: + 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.pGrandParent ) + { + pNewInternal->infinite_key( 0 ); + key_extractor()(pNewInternal->m_Key, *node_traits::to_value_ptr( res.pLeaf )); + } + else + { + pNewInternal->infinite_key( 1 ); + } + 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 ); + + //retire_update_desc( pOp ); + return true; + } + else + { + free_update_desc( pOp ); + } + + /*if ( res.pParent->m_pUpdate.compare_exchange_strong( updCur, + update_ptr( nullptr, update_desc::IFlag ), + memory_model::memory_order_acq_rel, atomics::memory_order_relaxed )) + { + tree_node * pLeaf = static_cast( res.pLeaf ); + + if ( res.bRightLeaf ) + { + res.pParent->m_pRight.compare_exchange_strong( pLeaf, static_cast( pNewInternal ), + memory_model::memory_order_release, atomics::memory_order_relaxed ); + } + else + { + res.pParent->m_pLeft.compare_exchange_strong( pLeaf, static_cast( pNewInternal ), + memory_model::memory_order_release, atomics::memory_order_relaxed ); + } + update_ptr cur( nullptr, update_desc::IFlag ); + + return true; + }*/ + } + return false; + } + + void help_insert( update_desc * pOp ) + { + // pOp must be guarded + + tree_node * pLeaf = static_cast( pOp->iInfo.pLeaf ); + if ( pOp->iInfo.bRightLeaf ) { + CDS_VERIFY( pOp->iInfo.pParent->m_pRight.compare_exchange_strong( pLeaf, static_cast( pOp->iInfo.pNew ), + memory_model::memory_order_release, atomics::memory_order_relaxed )); + } + else { + CDS_VERIFY( 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 )); + + } + + 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 ); + } + + 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 ); + + return true; + } + + 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 ); + return true; + } + + return false; + } + }; + } +} +#endif //MDP_ELLEN_BEEN_TREE_ELLEN_BEENTREE_NOGC2_H diff --git a/test/unit/tree/CMakeLists.txt b/test/unit/tree/CMakeLists.txt index 3f0e473ee..1d011273c 100644 --- a/test/unit/tree/CMakeLists.txt +++ b/test/unit/tree/CMakeLists.txt @@ -31,6 +31,7 @@ set(CDSGTEST_TREE_SOURCES intrusive_ellenbintree_rcu_gpi.cpp intrusive_ellenbintree_rcu_gpt.cpp intrusive_ellenbintree_rcu_shb.cpp + test_intrusive_ellen_bintree_nogc.cpp ) include_directories( diff --git a/test/unit/tree/test_intrusive_ellen_bintree_nogc.cpp b/test/unit/tree/test_intrusive_ellen_bintree_nogc.cpp new file mode 100644 index 000000000..16ac3fc26 --- /dev/null +++ b/test/unit/tree/test_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 IntrusiveEllenBinTree_Nogc: 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( IntrusiveEllenBinTree_Nogc, base_cmp ) + { + typedef ci::EllenBinTreeNogc + ,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 From a7451baf50ee439caad3a66e68913796acdd9c84 Mon Sep 17 00:00:00 2001 From: Borsuk Pavel Date: Wed, 27 Dec 2017 18:22:38 +0300 Subject: [PATCH 02/10] small fix --- cds/intrusive/ellen_bintree_nogc.h | 42 +++++++++--------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/cds/intrusive/ellen_bintree_nogc.h b/cds/intrusive/ellen_bintree_nogc.h index 6f34baf2d..374c84b8c 100644 --- a/cds/intrusive/ellen_bintree_nogc.h +++ b/cds/intrusive/ellen_bintree_nogc.h @@ -74,19 +74,16 @@ namespace cds { protected: typedef cds::details::Allocator< update_desc, update_desc_allocator > cxx_update_desc_allocator; struct search_result{ - internal_node * pGrandParent; internal_node *pParent; leaf_node * pLeaf; - bool bRightLeaf; // true if pLeaf is right child of pParent, false otherwise - // bool bRightParent; // true if pParent is right child of pGrandParent, false otherwise + bool bRightLeaf; + update_ptr updParent; search_result() - :pGrandParent( nullptr ) - ,pParent( nullptr ) + :pParent( nullptr ) ,pLeaf( nullptr ) ,bRightLeaf( false ) -// ,bRightParent( false ) {} }; @@ -120,7 +117,6 @@ namespace cds { EllenBinTreeNogc() { - //static_assert( !std::is_same< key_extractor, opt::none >::value, "The key extractor option must be specified" ); make_empty_tree(); } @@ -130,7 +126,6 @@ namespace cds { bool search(search_result& res, KeyValue const& key, Compare cmp) const { internal_node * pParent; - internal_node * pGrandParent = nullptr; tree_node * pLeaf; update_ptr updParent; @@ -139,21 +134,15 @@ namespace cds { int nCmp = 0; - - retry: - pParent = nullptr; pLeaf = const_cast( &m_Root ); updParent = nullptr; bRightLeaf = false; while ( pLeaf->is_internal()) { - pGrandParent = pParent; 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 ); @@ -167,8 +156,6 @@ namespace cds { res.pLeaf = static_cast( pLeaf ); res.updParent = updParent; res.bRightLeaf = bRightLeaf; - res.pGrandParent = pGrandParent; - return nCmp == 0; } @@ -187,7 +174,9 @@ namespace cds { if (res.updParent.bits() == update_desc::Clean) { if (!pNewInternal.get()) + { pNewInternal.reset(alloc_internal_node()); + } if (try_insert(val, pNewInternal.get(), res)) { f( val ); @@ -195,10 +184,8 @@ namespace cds { break; } } - int z = res.updParent.bits(); bkoff(); } - //my_printf_tree(m_Root); ++m_ItemCounter; return true; } @@ -348,14 +335,14 @@ namespace cds { int nCmp = node_compare()(val, *res.pLeaf); if ( nCmp < 0 ) { - if( res.pGrandParent ) + if( res.pLeaf->infinite_key() ) { - pNewInternal->infinite_key( 0 ); - key_extractor()(pNewInternal->m_Key, *node_traits::to_value_ptr( res.pLeaf )); + pNewInternal->infinite_key( 1 ); } else { - pNewInternal->infinite_key( 1 ); + 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 ); @@ -382,8 +369,6 @@ namespace cds { // do insert help_insert( pOp ); - - //retire_update_desc( pOp ); return true; } else @@ -421,19 +406,18 @@ namespace cds { tree_node * pLeaf = static_cast( pOp->iInfo.pLeaf ); if ( pOp->iInfo.bRightLeaf ) { - CDS_VERIFY( pOp->iInfo.pParent->m_pRight.compare_exchange_strong( pLeaf, static_cast( pOp->iInfo.pNew ), - memory_model::memory_order_release, atomics::memory_order_relaxed )); + pOp->iInfo.pParent->m_pRight.compare_exchange_strong( pLeaf, static_cast( pOp->iInfo.pNew ), + memory_model::memory_order_release, atomics::memory_order_relaxed ); } else { - CDS_VERIFY( pOp->iInfo.pParent->m_pLeft.compare_exchange_strong( pLeaf, static_cast( pOp->iInfo.pNew ), - memory_model::memory_order_release, atomics::memory_order_relaxed )); + 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 )); - } void make_empty_tree() From bf6058848b3ddb22fc3acf32288565d0a540d713 Mon Sep 17 00:00:00 2001 From: Borsuk Pavel Date: Sun, 14 Jan 2018 20:13:10 +0300 Subject: [PATCH 03/10] ellen bintree with stress test --- cds/container/ellen_bintree_map_nogc.h | 367 ++++++++++++ cds/intrusive/ellen_bintree_nogc.h | 559 ++++++++++++------ .../find_string/map_find_string_ellentree.cpp | 1 + test/stress/map/map_type_ellen_bintree.h | 36 +- test/unit/tree/CMakeLists.txt | 5 +- .../tree/intrusive_ellen_bintree_nogc.cpp | 46 ++ test/unit/tree/test_intrusive_tree_nogc.cpp | 141 +++++ 7 files changed, 961 insertions(+), 194 deletions(-) create mode 100644 cds/container/ellen_bintree_map_nogc.h create mode 100644 test/unit/tree/intrusive_ellen_bintree_nogc.cpp create mode 100644 test/unit/tree/test_intrusive_tree_nogc.cpp diff --git a/cds/container/ellen_bintree_map_nogc.h b/cds/container/ellen_bintree_map_nogc.h new file mode 100644 index 000000000..7f2324a3f --- /dev/null +++ b/cds/container/ellen_bintree_map_nogc.h @@ -0,0 +1,367 @@ +// +// Created by pasha on 10.01.18. +// + +#ifndef CDS_ELLEN_BINTREE_MAP_NOGC_H +#define CDS_ELLEN_BINTREE_MAP_NOGC_H +#include +#include + +namespace cds { namespace container { + + /*namespace details { + + template + struct make_ellen_bintree_map_nogc: public make_ellen_bintree_map + { + typedef make_ellen_bintree_map base_maker; + typedef typename base_maker::node_type node_type; + + struct intrusive_traits: public base_maker::intrusive_traits + { + typedef typename base_maker::node_deallocator disposer; + }; + + typedef intrusive::MichaelList type; + }; + + } // namespace details*/ + + 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 + /** + The function creates a node with \p key and default value, and then inserts the node created into the map. + + Preconditions: + - The \p key_type should be constructible from a value of type \p K. + - The \p mapped_type should be default-constructible. + + RCU \p synchronize() can be called. RCU should not be locked. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert( K const& key ) + { + return insert_with( key, [](value_type&){} ); + } + + /// Inserts new node + /** + The function creates a node with copy of \p val value + and then inserts the node created into the map. + + Preconditions: + - The \p key_type should be constructible from \p key of type \p K. + - The \p value_type should be constructible from \p val of type \p V. + + RCU \p synchronize() method can be called. RCU should not be locked. + + Returns \p true if \p val is inserted into the map, \p false otherwise. + */ + 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 + /** + This function inserts new node with key \p key and if inserting is successful then it calls + \p func functor with signature + \code + struct functor { + void operator()( value_type& item ); + }; + \endcode + + The argument \p item of user-defined functor \p func is the reference + to the map's item inserted: + - item.first is a const reference to item's key that cannot be changed. + - item.second is a reference to item's value that may be changed. + + The key_type should be constructible from value of type \p K. + + The function allows to split creating of new item into two part: + - create item from \p key; + - insert new item into the map; + - if inserting is successful, initialize the value of item by calling \p func functor + + This can be useful if complete initialization of object of \p value_type is heavyweight and + it is preferable that the initialization should be completed only if inserting is successful. + + RCU \p synchronize() method can be called. RCU should not be locked. + */ + 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 + /** + Returns \p true if inserting successful, \p false otherwise. + + RCU \p synchronize() method can be called. RCU should not be locked. + */ + 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 + /** + The operation performs inserting or changing data with lock-free manner. + + If the item \p val is not found in the map, then \p val is inserted iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + struct my_functor { + void operator()( bool bNew, value_type& item ); + }; + \endcode + + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - item of the map + + The functor may change any fields of the \p item.second that is \p mapped_type; + however, \p func must guarantee that during changing no any other modifications + could be made on this item by concurrent threads. + + RCU \p synchronize() method can be called. RCU should not be locked. + + 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( 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 + /**\anchor cds_nonintrusive_EllenBinTreeMap_rcu_erase_val + + RCU \p synchronize() method can be called. RCU should not be locked. + + Return \p true if \p key is found and deleted, \p false otherwise + */ + 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 + /** + The function is an analog of \ref cds_nonintrusive_EllenBinTreeMap_rcu_find_cfunc "find(K const&, Func)" + 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 map. + */ + 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 );}); + } + + /// Checks whether the map 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. + + The function applies RCU lock internally. + */ + 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 + /** + 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 and should meet \ref cds_intrusive_EllenBinTree_rcu_less + "Predicate requirements". + \p Less must imply the same element order as the comparator used for building the set. + */ + 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 + /** \anchor cds_nonintrusive_EllenBinTreeMap_rcu_get + The function searches the item with key equal to \p key and returns the pointer to item found. + If \p key is not found it returns \p nullptr. + + RCU should be locked before call the function. + Returned pointer is valid while RCU is locked. + */ + 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 + /** + The function is an analog of \ref cds_nonintrusive_EllenBinTreeMap_rcu_get "get(Q const&)" + but \p pred is used for comparing the keys. + + \p Less functor has the semantics like \p std::less but should take arguments of type \p key_type + and \p Q in any order. + \p pred must imply the same element order as the comparator used for building the map. + */ + 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 + /** + 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 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 index 374c84b8c..6ec022114 100644 --- a/cds/intrusive/ellen_bintree_nogc.h +++ b/cds/intrusive/ellen_bintree_nogc.h @@ -9,42 +9,34 @@ #include namespace cds { namespace intrusive { - template < //class GC, - typename Key, + template - class EllenBinTreeNogc + class EllenBinTree { public: - typedef gc::nogc gc; - typedef Key key_type; - typedef T value_type; - typedef Traits traits; - - typedef typename traits::hook hook; - typedef typename traits::node_allocator node_allocator; - typedef typename traits::memory_model memory_model; - typedef typename traits::key_extractor key_extractor; - typedef typename traits::back_off back_off; - typedef typename traits::update_desc_allocator update_desc_allocator; - typedef typename hook::node_type node_type; - - - + 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; - public: typedef typename node_factory::internal_node_type internal_node; - protected: typedef ellen_bintree::update_desc< leaf_node, internal_node> update_desc; typedef typename update_desc::update_ptr update_ptr; - - typedef cds::details::Allocator< internal_node, node_allocator > cxx_node_allocator; public: - typedef typename traits::item_counter item_counter; +# 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 { @@ -60,32 +52,51 @@ namespace cds { 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: - //@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; - - public: typedef ellen_bintree::details::compare< key_type, value_type, key_comparator, node_traits > node_compare; - protected: + + 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 @@ -94,13 +105,17 @@ namespace cds { } }; + 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(); } @@ -109,57 +124,49 @@ namespace cds { cxx_update_desc_allocator().Delete( reinterpret_cast( pDesc )); } - typedef std::unique_ptr< internal_node, internal_node_deleter> unique_internal_node_ptr; - public: - EllenBinTreeNogc() + EllenBinTree() { make_empty_tree(); } - - - template - bool search(search_result& res, KeyValue const& key, Compare cmp) const + ~EllenBinTree() { - 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()); + clear(); + } - leaf_node* keyValue = static_cast( pLeaf ); - nCmp = cmp( key, *static_cast(pLeaf)); + /// Inserts new node + /** + The function inserts \p val in the tree if it does not contain + an item with key equal to \p val. - res.pParent = pParent; - res.pLeaf = static_cast( pLeaf ); - res.updParent = updParent; - res.bRightLeaf = bRightLeaf; - return nCmp == 0; + 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 ) { @@ -169,6 +176,7 @@ namespace cds { while(true) { if (search(res, val, node_compare())) { + m_Stat.onInsertFailed(); return false; // uniq value } @@ -185,39 +193,111 @@ namespace cds { } } bkoff(); + m_Stat.onInsertRetry(); } ++m_ItemCounter; + m_Stat.onInsertSuccess(); return true; - } - - bool insert( value_type& val ) - { - return insert( val, []( value_type& ) {} ); } - bool empty() const + /// 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 ) { - return m_Root.m_pLeft.load( memory_model::memory_order_relaxed )->is_leaf(); - } - size_t size() const - { - return m_ItemCounter; + 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 { @@ -230,40 +310,39 @@ namespace cds { search_result res; if ( search( res, key, compare_functor())) { + m_Stat.onFindSuccess(); return true; } + m_Stat.onFindFailed(); return false; } - - template - bool find( Q const& key ) + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find_with( Q const& key, Less pred ) const { - return contains( key ); + return contains( key, pred ); } - - void my_printf_tree (internal_node nodePrint) - { - - internal_node * pParent; - tree_node * left; - tree_node * right; - tree_node * pLeaf; - leaf_node * leaf; - - pLeaf = const_cast( &nodePrint ); - while ( pLeaf->is_internal()) - { - pParent = static_cast( pLeaf ); - left = pParent->get_child( false, memory_model::memory_order_acquire ); - right = pParent->get_child( true, memory_model::memory_order_acquire ); - my_printf_tree(*(static_cast( left ))); - my_printf_tree(*(static_cast( right ))); + //@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. - leaf = static_cast(pLeaf ); - //TUT LEAF - } + 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 { @@ -275,54 +354,173 @@ namespace cds { { 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& key, Less pred, Func f ) const + bool find_with( Q const& key, Less pred, Func f ) const { return find_with_( key, pred, f ); } + //@endcond - template - bool find_with( Q const& key, Less pred, Func f ) const + /// Checks if the tree is empty + bool empty() const { - return find_with_( key, pred, f ); + return m_Root.m_pLeft.load( memory_model::memory_order_relaxed )->is_leaf(); } - template - std::pair update( value_type& val, Func func, bool bAllowInsert = true ) + /// Clears the tree + void clear() { - - 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 ); - return std::make_pair( true, false ); + 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 ( res.updParent.bits() == update_desc::Clean ) { - if ( !bAllowInsert ) - return std::make_pair( false, false ); + if ( pLeaf->infinite_key()) { + m_ItemCounter.reset(); + return; + } - if ( !pNewInternal.get()) - pNewInternal.reset( alloc_internal_node()); + // Remove leftmost leaf and its parent node + assert( pGrandParent ); + assert( pParent ); + assert( pLeaf->is_leaf()); - 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(); + 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--; } + } - ++m_ItemCounter; - return std::make_pair( true, true ); + /// 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 ); @@ -375,60 +573,36 @@ namespace cds { { free_update_desc( pOp ); } - - /*if ( res.pParent->m_pUpdate.compare_exchange_strong( updCur, - update_ptr( nullptr, update_desc::IFlag ), - memory_model::memory_order_acq_rel, atomics::memory_order_relaxed )) - { - tree_node * pLeaf = static_cast( res.pLeaf ); - - if ( res.bRightLeaf ) - { - res.pParent->m_pRight.compare_exchange_strong( pLeaf, static_cast( pNewInternal ), - memory_model::memory_order_release, atomics::memory_order_relaxed ); - } - else - { - res.pParent->m_pLeft.compare_exchange_strong( pLeaf, static_cast( pNewInternal ), - memory_model::memory_order_release, atomics::memory_order_relaxed ); - } - update_ptr cur( nullptr, update_desc::IFlag ); - - return true; - }*/ } return false; } - void help_insert( update_desc * pOp ) - { - // pOp must be guarded - 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 )); - } - void make_empty_tree() + + + + + + + + + + + template + bool find_with( Q& key, Less pred, Func f ) const { - 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 ); + return find_with_( key, pred, f ); } + + + + + + + template bool find_with_( Q& val, Less pred, Func f ) const { @@ -443,10 +617,10 @@ namespace cds { 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; } @@ -456,11 +630,22 @@ namespace cds { 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 ); + } }; } } 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 1d011273c..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,7 +31,8 @@ set(CDSGTEST_TREE_SOURCES intrusive_ellenbintree_rcu_gpi.cpp intrusive_ellenbintree_rcu_gpt.cpp intrusive_ellenbintree_rcu_shb.cpp - test_intrusive_ellen_bintree_nogc.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.cpp b/test/unit/tree/test_intrusive_tree_nogc.cpp new file mode 100644 index 000000000..92b59057d --- /dev/null +++ b/test/unit/tree/test_intrusive_tree_nogc.cpp @@ -0,0 +1,141 @@ +// +// 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 "/home/pasha/Project/libcds/libcds/test/unit/tree/test_intrusive_tree.h" +#include "/home/pasha/Project/libcds/libcds/test/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 ) + { + std::cout< 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 From 19cb44c6fbd062740af0412088ad64c53293d3e0 Mon Sep 17 00:00:00 2001 From: Borsuk Pavel Date: Sun, 14 Jan 2018 20:18:28 +0300 Subject: [PATCH 04/10] small fix --- cds/container/details/ellen_bintree_base.h | 2 +- cds/container/ellen_bintree_set_nogc.h | 311 --------------------- 2 files changed, 1 insertion(+), 312 deletions(-) delete mode 100644 cds/container/ellen_bintree_set_nogc.h diff --git a/cds/container/details/ellen_bintree_base.h b/cds/container/details/ellen_bintree_base.h index df286c081..a4c2b55e7 100644 --- a/cds/container/details/ellen_bintree_base.h +++ b/cds/container/details/ellen_bintree_base.h @@ -506,4 +506,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_set_nogc.h b/cds/container/ellen_bintree_set_nogc.h deleted file mode 100644 index ff45b951e..000000000 --- a/cds/container/ellen_bintree_set_nogc.h +++ /dev/null @@ -1,311 +0,0 @@ -// -// Created by pasha on 27.12.17. -// - -#ifndef CDS_ELLEN_BINTREE_SET_NOGC_H -#define CDS_ELLEN_BINTREE_SET_NOGC_H -#include -#include -#include -#include "../intrusive/ellen_bintree_nogc.h" - -namespace cds { namespace container { - - template < - typename Key, - typename T, -#ifdef CDS_DOXYGEN_INVOKED - class Traits = ellen_bintree::traits -#else - class Traits -#endif - > - class EllenBinTreeSetNogc: public ellen_bintree::details::make_ellen_bintree_set_nogc< gc::nogc, - Key, T, Traits >::type - { - typedef ellen_bintree::details::make_ellen_bintree_set_nogc< gc::nogc, Key, T, Traits > maker; - typedef typename maker::type base_class; - //@endcond - - 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 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 - EllenBinTreeSetNogc() - : base_class() - {} - - /// Clears the set - ~EllenBinTreeSetNogc() - {} - - - - /// Inserts new node - /** - The function creates a node with copy of \p val value - and then inserts the node created into the set. - - The type \p Q should contain at least the complete key for the node. - The object of \ref value_type should be constructible from a value of type \p Q. - In trivial case, \p Q is equal to \ref value_type. - - Returns \p true if \p val is inserted into the set, \p false otherwise. - */ - template - bool insert( Q const& val ) - { - /* scoped_node_ptr sp( cxx_leaf_node_allocator().New( val )); - if ( base_class::insert( *sp.get())) { - sp.release(); - return true; - }*/ - return false; - } - - /// Inserts new node - /** - The function allows to split creating of new item into two part: - - create item with key only - - insert new item into the set - - if inserting is success, calls \p f functor to initialize value-fields 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 set's item by concurrent threads. - The user-defined functor is called only if the inserting is success. - */ - template - bool insert( Q const& val, Func f ) - { - /*scoped_node_ptr sp( cxx_leaf_node_allocator().New( val )); - if ( base_class::insert( *sp.get(), [&f]( leaf_node& v ) { f( v.m_Value ); } )) { - sp.release(); - return true; - }*/ - return false; - } - - /// 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 - struct my_functor { - void operator()( bool bNew, value_type& item, const Q& val ); - }; - \endcode - with arguments: - 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 key passed into the \p %update() function - - 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( const Q& val, Func func, bool bAllowInsert = true ) - { - scoped_node_ptr sp( cxx_leaf_node_allocator().New( val )); - std::pair bRes = base_class::update( *sp, - [&func, &val](bool bNew, leaf_node& node, leaf_node&){ func( bNew, node.m_Value, val ); }, - bAllowInsert ); - if ( bRes.first && bRes.second ) - sp.release(); - return bRes; - } - //@cond - template - CDS_DEPRECATED("ensure() is deprecated, use update()") - std::pair ensure( const Q& val, Func func ) - { - return update( val, func, true ); - } - //@endcond - - /// Inserts data of type \p value_type created in-place from \p args - /** - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool emplace( Args&&... args ) - { - scoped_node_ptr sp( cxx_leaf_node_allocator().MoveNew( std::forward(args)... )); - if ( base_class::insert( *sp.get())) { - sp.release(); - return true; - } - return false; - } - - /// Find the key \p key - /** - @anchor cds_nonintrusive_EllenBinTreeSet_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 may 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 set's \p item. If such access is - possible you must provide your own synchronization schema on item level to exclude unsafe item modifications. - - The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor - can modify both arguments. - - Note the hash functor specified for class \p Traits template parameter - should accept a parameter of type \p Q that may be not the same as \p value_type. - - The function returns \p true if \p key is found, \p false otherwise. - */ - template - bool find( Q& key, Func f ) - { - return base_class::find( key, [&f]( leaf_node& node, Q& v ) { f( node.m_Value, v ); }); - } - //@cond - template - bool find( Q const& key, Func f ) - { - return base_class::find( key, [&f]( leaf_node& node, Q const& v ) { f( node.m_Value, v ); } ); - } - //@endcond - - /// Finds the key \p key using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_EllenBinTreeSet_find_func "find(Q&, Func)" - 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 find_with( Q& key, Less pred, Func f ) - { - CDS_UNUSED( pred ); - return base_class::find_with( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::value_accessor >(), - [&f]( leaf_node& node, Q& v ) { f( node.m_Value, v ); } ); - } - //@cond - template - bool find_with( Q const& key, Less pred, Func f ) - { - CDS_UNUSED( pred ); - return base_class::find_with( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::value_accessor >(), - [&f]( leaf_node& node, Q const& v ) { f( node.m_Value, v ); } ); - } - //@endcond - - /// 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 ) - { - return base_class::contains( key ); - } - //@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 ) - { - CDS_UNUSED( pred ); - return base_class::contains( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::value_accessor >()); - } - //@cond - template - CDS_DEPRECATED("deprecated, use contains()") - bool find_with( Q const& key, Less pred ) - { - return contains( key, pred ); - } - //@endcond - - /// Checks if the set is empty - bool empty() const - { - return base_class::empty(); - } - - /// Returns item count in the set - /** - 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 base_class::size(); - } - }; - } -} - - - -#endif //CDS_ELLEN_BINTREE_SET_NOGC_H From 57e2fc7f080573c4273f2b68238f9b2519c5158a Mon Sep 17 00:00:00 2001 From: Borsuk Pavel Date: Sun, 14 Jan 2018 20:21:28 +0300 Subject: [PATCH 05/10] small fix --- cds/container/details/ellen_bintree_base.h | 49 ---------------------- 1 file changed, 49 deletions(-) diff --git a/cds/container/details/ellen_bintree_base.h b/cds/container/details/ellen_bintree_base.h index a4c2b55e7..a132b8669 100644 --- a/cds/container/details/ellen_bintree_base.h +++ b/cds/container/details/ellen_bintree_base.h @@ -380,55 +380,6 @@ namespace cds { namespace container { typedef cds::intrusive::EllenBinTree< gc, key_type, leaf_node, intrusive_traits > type; }; - template < class GC, typename Key, typename T, class Traits> - struct make_ellen_bintree_set_nogc - { - typedef GC gc; - typedef Key key_type; - typedef T value_type; - typedef Traits original_traits; - - typedef node< gc, value_type > leaf_node; - - struct intrusive_key_extractor - { - void operator()( key_type& dest, leaf_node const& src ) const - { - typename original_traits::key_extractor()( dest, src.m_Value ); - } - }; - - struct value_accessor - { - value_type const& operator()( leaf_node const& node ) const - { - return node.m_Value; - } - }; - - typedef typename cds::opt::details::make_comparator< value_type, original_traits, false >::type key_comparator; - - typedef cds::details::Allocator< leaf_node, typename original_traits::allocator> cxx_leaf_node_allocator; - struct leaf_deallocator - { - void operator()( leaf_node * p ) const - { - cxx_leaf_node_allocator().Delete( p ); - } - }; - - struct intrusive_traits: public original_traits - { - typedef cds::intrusive::ellen_bintree::base_hook< cds::opt::gc< gc >> hook; - typedef intrusive_key_extractor key_extractor; - typedef leaf_deallocator disposer; - typedef cds::details::compare_wrapper< leaf_node, key_comparator, value_accessor > compare; - }; - - // Metafunction result - typedef cds::intrusive::EllenBinTreeNogc< key_type, leaf_node, intrusive_traits > type; - }; - template < class GC, typename Key, typename T, class Traits> struct make_ellen_bintree_map { From a9a9583cd59f65740d61a475cbf3f2bb03e3de9c Mon Sep 17 00:00:00 2001 From: Pasha <19908565+gfuf@users.noreply.github.com> Date: Sun, 14 Jan 2018 20:23:48 +0300 Subject: [PATCH 06/10] fix --- test/unit/tree/test_intrusive_tree_nogc.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/unit/tree/test_intrusive_tree_nogc.cpp b/test/unit/tree/test_intrusive_tree_nogc.cpp index 92b59057d..34ad4fbf1 100644 --- a/test/unit/tree/test_intrusive_tree_nogc.cpp +++ b/test/unit/tree/test_intrusive_tree_nogc.cpp @@ -23,8 +23,8 @@ namespace cds_test{ void test( Tree& t ) { std::cout< Date: Sun, 14 Jan 2018 20:27:54 +0300 Subject: [PATCH 07/10] fix --- .../test_intrusive_ellen_bintree_nogc.cpp | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 test/unit/tree/test_intrusive_ellen_bintree_nogc.cpp diff --git a/test/unit/tree/test_intrusive_ellen_bintree_nogc.cpp b/test/unit/tree/test_intrusive_ellen_bintree_nogc.cpp deleted file mode 100644 index 16ac3fc26..000000000 --- a/test/unit/tree/test_intrusive_ellen_bintree_nogc.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// -// 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 IntrusiveEllenBinTree_Nogc: 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( IntrusiveEllenBinTree_Nogc, base_cmp ) - { - typedef ci::EllenBinTreeNogc - ,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 From 65f7d1c19f198934833dd949a80f82ebe0a745f5 Mon Sep 17 00:00:00 2001 From: Pasha <19908565+gfuf@users.noreply.github.com> Date: Sun, 14 Jan 2018 20:29:59 +0300 Subject: [PATCH 08/10] fix --- test/unit/tree/test_intrusive_tree_nogc.cpp | 138 -------------------- 1 file changed, 138 deletions(-) diff --git a/test/unit/tree/test_intrusive_tree_nogc.cpp b/test/unit/tree/test_intrusive_tree_nogc.cpp index 34ad4fbf1..8b1378917 100644 --- a/test/unit/tree/test_intrusive_tree_nogc.cpp +++ b/test/unit/tree/test_intrusive_tree_nogc.cpp @@ -1,139 +1 @@ -// -// 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 "/home/pasha/Project/libcds/libcds/test/unit/tree/test_intrusive_tree.h" -#include "/home/pasha/Project/libcds/libcds/test/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 ) - { - std::cout< 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 From 055efc303f45282e0b9fd1f39455f926a652c081 Mon Sep 17 00:00:00 2001 From: Borsuk Pavel Date: Sun, 14 Jan 2018 20:31:19 +0300 Subject: [PATCH 09/10] fix --- test/unit/tree/test_intrusive_tree_nogc.cpp | 139 -------------------- 1 file changed, 139 deletions(-) delete mode 100644 test/unit/tree/test_intrusive_tree_nogc.cpp diff --git a/test/unit/tree/test_intrusive_tree_nogc.cpp b/test/unit/tree/test_intrusive_tree_nogc.cpp deleted file mode 100644 index 34ad4fbf1..000000000 --- a/test/unit/tree/test_intrusive_tree_nogc.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// -// 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 "/home/pasha/Project/libcds/libcds/test/unit/tree/test_intrusive_tree.h" -#include "/home/pasha/Project/libcds/libcds/test/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 ) - { - std::cout< 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 From 97e31243e86daa6f8d96d00d6beac48d602c6222 Mon Sep 17 00:00:00 2001 From: Pasha <19908565+gfuf@users.noreply.github.com> Date: Sun, 14 Jan 2018 22:43:45 +0300 Subject: [PATCH 10/10] Update ellen_bintree_map_nogc.h fix --- cds/container/ellen_bintree_map_nogc.h | 159 +------------------------ 1 file changed, 6 insertions(+), 153 deletions(-) diff --git a/cds/container/ellen_bintree_map_nogc.h b/cds/container/ellen_bintree_map_nogc.h index 7f2324a3f..623d3de5f 100644 --- a/cds/container/ellen_bintree_map_nogc.h +++ b/cds/container/ellen_bintree_map_nogc.h @@ -1,7 +1,3 @@ -// -// Created by pasha on 10.01.18. -// - #ifndef CDS_ELLEN_BINTREE_MAP_NOGC_H #define CDS_ELLEN_BINTREE_MAP_NOGC_H #include @@ -9,24 +5,6 @@ namespace cds { namespace container { - /*namespace details { - - template - struct make_ellen_bintree_map_nogc: public make_ellen_bintree_map - { - typedef make_ellen_bintree_map base_maker; - typedef typename base_maker::node_type node_type; - - struct intrusive_traits: public base_maker::intrusive_traits - { - typedef typename base_maker::node_deallocator disposer; - }; - - typedef intrusive::MichaelList type; - }; - - } // namespace details*/ - template < typename Key, typename T, @@ -83,17 +61,6 @@ namespace cds { namespace container { {} /// Inserts new node with key and default value - /** - The function creates a node with \p key and default value, and then inserts the node created into the map. - - Preconditions: - - The \p key_type should be constructible from a value of type \p K. - - The \p mapped_type should be default-constructible. - - RCU \p synchronize() can be called. RCU should not be locked. - - Returns \p true if inserting successful, \p false otherwise. - */ template bool insert( K const& key ) { @@ -101,18 +68,6 @@ namespace cds { namespace container { } /// Inserts new node - /** - The function creates a node with copy of \p val value - and then inserts the node created into the map. - - Preconditions: - - The \p key_type should be constructible from \p key of type \p K. - - The \p value_type should be constructible from \p val of type \p V. - - RCU \p synchronize() method can be called. RCU should not be locked. - - Returns \p true if \p val is inserted into the map, \p false otherwise. - */ template bool insert( K const& key, V const& val ) { @@ -126,32 +81,6 @@ namespace cds { namespace container { } // Inserts new node and initialize it by a functor - /** - This function inserts new node with key \p key and if inserting is successful then it calls - \p func functor with signature - \code - struct functor { - void operator()( value_type& item ); - }; - \endcode - - The argument \p item of user-defined functor \p func is the reference - to the map's item inserted: - - item.first is a const reference to item's key that cannot be changed. - - item.second is a reference to item's value that may be changed. - - The key_type should be constructible from value of type \p K. - - The function allows to split creating of new item into two part: - - create item from \p key; - - insert new item into the map; - - if inserting is successful, initialize the value of item by calling \p func functor - - This can be useful if complete initialization of object of \p value_type is heavyweight and - it is preferable that the initialization should be completed only if inserting is successful. - - RCU \p synchronize() method can be called. RCU should not be locked. - */ template bool insert_with( K const& key, Func func ) { @@ -164,11 +93,6 @@ namespace cds { namespace container { } /// For key \p key inserts data of type \p value_type created in-place from \p args - /** - Returns \p true if inserting successful, \p false otherwise. - - RCU \p synchronize() method can be called. RCU should not be locked. - */ template bool emplace( K&& key, Args&&... args ) { @@ -181,35 +105,6 @@ namespace cds { namespace container { } /// Updates the node - /** - The operation performs inserting or changing data with lock-free manner. - - If the item \p val is not found in the map, then \p val is inserted iff \p bAllowInsert is \p true. - Otherwise, the functor \p func is called with item found. - The functor \p func signature is: - \code - struct my_functor { - void operator()( bool bNew, value_type& item ); - }; - \endcode - - with arguments: - - \p bNew - \p true if the item has been inserted, \p false otherwise - - \p item - item of the map - - The functor may change any fields of the \p item.second that is \p mapped_type; - however, \p func must guarantee that during changing no any other modifications - could be made on this item by concurrent threads. - - RCU \p synchronize() method can be called. RCU should not be locked. - - 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( K const& key, Func func, bool bAllowInsert = true ) { @@ -232,12 +127,6 @@ namespace cds { namespace container { //@endcond /// Delete \p key from the map - /**\anchor cds_nonintrusive_EllenBinTreeMap_rcu_erase_val - - RCU \p synchronize() method can be called. RCU should not be locked. - - Return \p true if \p key is found and deleted, \p false otherwise - */ template bool find( K const& key, Func f ) { @@ -245,12 +134,6 @@ namespace cds { namespace container { } /// Finds the key \p val using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_EllenBinTreeMap_rcu_find_cfunc "find(K const&, Func)" - 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 map. - */ template bool find_with( K const& key, Less pred, Func f ) { @@ -258,14 +141,14 @@ namespace cds { namespace container { 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 - /** - The function searches the item with key equal to \p key - and returns \p true if it is found, and \p false otherwise. - - The function applies RCU lock internally. - */ template bool contains( K const& key ) { @@ -281,12 +164,6 @@ namespace cds { namespace container { //@endcond /// Checks whether the map 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 and should meet \ref cds_intrusive_EllenBinTree_rcu_less - "Predicate requirements". - \p Less must imply the same element order as the comparator used for building the set. - */ template bool contains( K const& key, Less pred ) { @@ -303,13 +180,6 @@ namespace cds { namespace container { //@endcond /// Finds \p key and return the item found - /** \anchor cds_nonintrusive_EllenBinTreeMap_rcu_get - The function searches the item with key equal to \p key and returns the pointer to item found. - If \p key is not found it returns \p nullptr. - - RCU should be locked before call the function. - Returned pointer is valid while RCU is locked. - */ template value_type * get( Q const& key ) const { @@ -318,14 +188,6 @@ namespace cds { namespace container { } /// Finds \p key with \p pred predicate and return the item found - /** - The function is an analog of \ref cds_nonintrusive_EllenBinTreeMap_rcu_get "get(Q const&)" - but \p pred is used for comparing the keys. - - \p Less functor has the semantics like \p std::less but should take arguments of type \p key_type - and \p Q in any order. - \p pred must imply the same element order as the comparator used for building the map. - */ template value_type * get_with( Q const& key, Less pred ) const { @@ -342,15 +204,6 @@ namespace cds { namespace container { } /// Returns item count in the map - /** - 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 base_class::size();