diff --git a/.gitignore b/.gitignore index a4086f135..2287e4fc4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ /.cproject /.settings/ /tools/change_license.pl +.DS_Store diff --git a/cds/container/ellen_bintree_map_nogc.h b/cds/container/ellen_bintree_map_nogc.h new file mode 100644 index 000000000..623d3de5f --- /dev/null +++ b/cds/container/ellen_bintree_map_nogc.h @@ -0,0 +1,220 @@ +#ifndef CDS_ELLEN_BINTREE_MAP_NOGC_H +#define CDS_ELLEN_BINTREE_MAP_NOGC_H +#include +#include + +namespace cds { namespace container { + + template < + typename Key, + typename T, +#ifdef CDS_DOXYGEN_INVOKED + class Traits = ellen_bintree::traits +#else + class Traits +#endif + > + class EllenBinTreeMap + : public ellen_bintree::details::make_ellen_bintree_map< gc::nogc, Key, T, Traits >::type + { + typedef ellen_bintree::details::make_ellen_bintree_map< gc::nogc,Key, T, Traits > maker; + typedef typename maker::type base_class; + //@endcond + + public: + typedef cds::gc::nogc gc; ///< Garbage collector + typedef Key key_type; ///< type of a key to be stored in internal nodes; key is a part of \p value_type + typedef T value_type; ///< type of value to be stored in the binary tree + typedef Traits traits; ///< Traits template parameter + +# ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined key_comparator ; ///< key compare functor based on opt::compare and opt::less option setter. +# else + typedef typename maker::intrusive_traits::compare key_comparator; +# endif + typedef typename base_class::item_counter item_counter; ///< Item counting policy used + typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename traits::key_extractor key_extractor; ///< key extracting functor + typedef typename traits::back_off back_off; ///< Back-off strategy + + typedef typename traits::allocator allocator_type; ///< Allocator for leaf nodes + typedef typename base_class::node_allocator node_allocator; ///< Internal node allocator + typedef typename base_class::update_desc_allocator update_desc_allocator; ///< Update descriptor allocator + + protected: + //@cond + + typedef typename maker::cxx_leaf_node_allocator cxx_leaf_node_allocator; + typedef typename base_class::value_type leaf_node; + typedef typename base_class::internal_node internal_node; + + typedef std::unique_ptr< leaf_node, typename maker::leaf_deallocator > scoped_node_ptr; + + public: + /// Default constructor + EllenBinTreeMap() + : base_class() + {} + + /// Clears the map + ~EllenBinTreeMap() + {} + + /// Inserts new node with key and default value + template + bool insert( K const& key ) + { + return insert_with( key, [](value_type&){} ); + } + + /// Inserts new node + template + bool insert( K const& key, V const& val ) + { + scoped_node_ptr pNode( cxx_leaf_node_allocator().New( key, val )); + if ( base_class::insert( *pNode )) + { + pNode.release(); + return true; + } + return false; + } + + // Inserts new node and initialize it by a functor + template + bool insert_with( K const& key, Func func ) + { + scoped_node_ptr pNode( cxx_leaf_node_allocator().New( key )); + if ( base_class::insert( *pNode, [&func]( leaf_node& item ) { func( item.m_Value ); } )) { + pNode.release(); + return true; + } + return false; + } + + /// For key \p key inserts data of type \p value_type created in-place from \p args + template + bool emplace( K&& key, Args&&... args ) + { + scoped_node_ptr pNode( cxx_leaf_node_allocator().MoveNew( key_type( std::forward(key)), mapped_type( std::forward(args)... ))); + if ( base_class::insert( *pNode )) { + pNode.release(); + return true; + } + return false; + } + + /// Updates the node + template + std::pair update( K const& key, Func func, bool bAllowInsert = true ) + { + scoped_node_ptr pNode( cxx_leaf_node_allocator().New( key )); + std::pair res = base_class::update( *pNode, + [&func](bool bNew, leaf_node& item, leaf_node const& ){ func( bNew, item.m_Value ); }, + bAllowInsert + ); + if ( res.first && res.second ) + pNode.release(); + return res; + } + //@cond + template + CDS_DEPRECATED("ensure() is deprecated, use update()") + std::pair ensure( K const& key, Func func ) + { + return update( key, func, true ); + } + //@endcond + + /// Delete \p key from the map + template + bool find( K const& key, Func f ) + { + return base_class::find( key, [&f](leaf_node& item, K const& ) { f( item.m_Value );}); + } + + /// Finds the key \p val using \p pred predicate for searching + template + bool find_with( K const& key, Less pred, Func f ) + { + CDS_UNUSED( pred ); + return base_class::find_with( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::key_accessor >(), + [&f](leaf_node& item, K const& ) { f( item.m_Value );}); + } + + /// Clears the map + void clear() + { + base_class::clear(); + } + + /// Checks whether the map contains \p key + template + bool contains( K const& key ) + { + return base_class::contains( key ); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find( K const& key ) + { + return contains( key ); + } + //@endcond + + /// Checks whether the map contains \p key using \p pred predicate for searching + template + bool contains( K const& key, Less pred ) + { + CDS_UNUSED( pred ); + return base_class::contains( key, cds::details::predicate_wrapper< leaf_node, Less, typename maker::key_accessor >()); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find_with( K const& key, Less pred ) + { + return contains( key, pred ); + } + //@endcond + + /// Finds \p key and return the item found + template + value_type * get( Q const& key ) const + { + leaf_node * pNode = base_class::get( key ); + return pNode ? &pNode->m_Value : nullptr; + } + + /// Finds \p key with \p pred predicate and return the item found + template + value_type * get_with( Q const& key, Less pred ) const + { + CDS_UNUSED( pred ); + leaf_node * pNode = base_class::get_with( key, + cds::details::predicate_wrapper< leaf_node, Less, typename maker::key_accessor >()); + return pNode ? &pNode->m_Value : nullptr; + } + + /// Checks if the map is empty + bool empty() const + { + return base_class::empty(); + } + + /// Returns item count in the map + size_t size() const + { + return base_class::size(); + } + + + + + }; + } +} + + +#endif //CDS_ELLEN_BINTREE_MAP_NOGC_H diff --git a/cds/intrusive/ellen_bintree_nogc.h b/cds/intrusive/ellen_bintree_nogc.h new file mode 100644 index 000000000..96da9350c --- /dev/null +++ b/cds/intrusive/ellen_bintree_nogc.h @@ -0,0 +1,642 @@ +#ifndef CDSLIB_INTRUSIVE_ELLEN_BINTREE_NOGC_H +#define CDSLIB_INTRUSIVE_ELLEN_BINTREE_NOGC_H +#include +#include +#include +namespace cds +{ +namespace intrusive +{ +template +class EllenBinTree +{ + public: + typedef gc::nogc gc; ///< Garbage collector + typedef Key key_type; ///< type of a key to be stored in internal nodes; key is a part of \p value_type + typedef T value_type; ///< type of value stored in the binary tree + typedef Traits traits; ///< Traits template parameter + + typedef typename traits::hook hook; ///< hook type + typedef typename hook::node_type node_type; ///< node type + typedef typename traits::disposer disposer; ///< leaf node disposer + typedef typename traits::back_off back_off; ///< back-off strategy + protected: + typedef ellen_bintree::base_node tree_node; + typedef node_type leaf_node; + typedef ellen_bintree::node_types node_factory; + typedef typename node_factory::internal_node_type internal_node; + typedef ellen_bintree::update_desc update_desc; + typedef typename update_desc::update_ptr update_ptr; + + public: +#ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined key_comparator; ///< key compare functor based on \p Traits::compare and \p Traits::less + typedef typename get_node_traits::type node_traits; ///< Node traits +#else + typedef typename opt::details::make_comparator::type key_comparator; + struct node_traits : public get_node_traits::type + { + static internal_node const &to_internal_node(tree_node const &n) + { + assert(n.is_internal()); + return static_cast(n); + } + + static leaf_node const &to_leaf_node(tree_node const &n) + { + assert(n.is_leaf()); + return static_cast(n); + } + }; +#endif + + typedef typename traits::node_allocator node_allocator; + typedef typename traits::item_counter item_counter; + typedef typename traits::memory_model memory_model; + typedef typename traits::stat stat; ///< internal statistics type + typedef typename traits::key_extractor key_extractor; + typedef typename traits::update_desc_allocator update_desc_allocator; + + protected: + typedef ellen_bintree::details::compare node_compare; + + typedef cds::details::Allocator cxx_node_allocator; + typedef cds::details::Allocator cxx_update_desc_allocator; + struct search_result + { + internal_node *pParent; + leaf_node *pLeaf; + bool bRightLeaf; + update_ptr updParent; + + search_result() + : pParent(nullptr), pLeaf(nullptr), bRightLeaf(false) + { + } + }; + + protected: + //@cond + internal_node m_Root; ///< Tree root node (key= Infinite2) + leaf_node m_LeafInf1; ///< Infinite leaf 1 (key= Infinite1) + leaf_node m_LeafInf2; ///< Infinite leaf 2 (key= Infinite2) + + item_counter m_ItemCounter; + mutable stat m_Stat; ///< internal statistics + + protected: + static void free_leaf_node(void *p) + { + disposer()(reinterpret_cast(p)); + } + + static void free_internal_node(void *pNode) + { + cxx_node_allocator().Delete(reinterpret_cast(pNode)); + } + + struct internal_node_deleter + { + void operator()(internal_node *p) const + { + cxx_node_allocator().Delete(p); + } + }; + + typedef std::unique_ptr unique_internal_node_ptr; + + internal_node *alloc_internal_node() const + { + m_Stat.onInternalNodeCreated(); + internal_node *pNode = cxx_node_allocator().New(); + return pNode; + } + update_desc *alloc_update_desc() const + { + m_Stat.onUpdateDescCreated(); + return cxx_update_desc_allocator().New(); + } + + static void free_update_desc(void *pDesc) + { + cxx_update_desc_allocator().Delete(reinterpret_cast(pDesc)); + } + + public: + EllenBinTree() + { + make_empty_tree(); + } + + ~EllenBinTree() + { + clear(); + } + + /// Inserts new node + /** + The function inserts \p val in the tree if it does not contain + an item with key equal to \p val. + + Returns \p true if \p val is placed into the tree, \p false otherwise. + */ + bool insert(value_type &val) + { + return insert(val, [](value_type &) {}); + } + + /// Inserts new node + /** + This function is intended for derived non-intrusive containers. + + The function allows to split creating of new item into two part: + - create item with key only + - insert new item into the tree + - if inserting is success, calls \p f functor to initialize value-field of \p val. + + The functor signature is: + \code + void func( value_type& val ); + \endcode + where \p val is the item inserted. User-defined functor \p f should guarantee that during changing + \p val no any other changes could be made on this tree's item by concurrent threads. + The user-defined functor is called only if the inserting is success. + */ + template + bool insert(value_type &val, Func f) + { + search_result res; + back_off bkoff; + unique_internal_node_ptr pNewInternal; + + while (true) + { + if (search(res, val, node_compare())) + { + m_Stat.onInsertFailed(); + return false; // uniq value + } + + if (res.updParent.bits() == update_desc::Clean) + { + if (!pNewInternal.get()) + { + pNewInternal.reset(alloc_internal_node()); + } + + if (try_insert(val, pNewInternal.get(), res)) + { + f(val); + pNewInternal.release(); + break; + } + } + bkoff(); + m_Stat.onInsertRetry(); + } + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; + } + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If the item \p val is not found in the set, then \p val is inserted into the set + iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + void func( bool bNew, value_type& item, value_type& val ); + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - item of the set + - \p val - argument \p val passed into the \p %update() function + If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments + refer to the same thing. + + The functor can change non-key fields of the \p item; however, \p func must guarantee + that during changing no any other modifications could be made on this item by concurrent threads. + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + + @warning See \ref cds_intrusive_item_creating "insert item troubleshooting" + */ + template + std::pair update(value_type &val, Func func, bool bAllowInsert = true) + { + + unique_internal_node_ptr pNewInternal; + search_result res; + back_off bkoff; + + for (;;) + { + if (search(res, val, node_compare())) + { + func(false, *node_traits::to_value_ptr(res.pLeaf), val); + m_Stat.onUpdateExist(); + return std::make_pair(true, false); + } + + if (res.updParent.bits() == update_desc::Clean) + { + if (!bAllowInsert) + return std::make_pair(false, false); + + if (!pNewInternal.get()) + pNewInternal.reset(alloc_internal_node()); + + if (try_insert(val, pNewInternal.get(), res)) + { + func(true, val, val); + pNewInternal.release(); // internal node has been linked into the tree and should not be deleted + break; + } + } + bkoff(); + m_Stat.onUpdateRetry(); + } + + ++m_ItemCounter; + m_Stat.onUpdateNew(); + return std::make_pair(true, true); + } + + /// Checks whether the set contains \p key + /** + The function searches the item with key equal to \p key + and returns \p true if it is found, and \p false otherwise. + */ + template + bool contains(Q const &key) const + { + search_result res; + if (search(res, key, node_compare())) + { + m_Stat.onFindSuccess(); + return true; + } + m_Stat.onFindFailed(); + return false; + } + + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find(Q const &key) + { + return contains(key); + } + //@endcond + + /// Checks whether the set contains \p key using \p pred predicate for searching + /** + The function is similar to contains( key ) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p Less must imply the same element order as the comparator used for building the set. + */ + template + bool contains(Q const &key, Less pred) const + { + typedef ellen_bintree::details::compare< + key_type, + value_type, + opt::details::make_comparator_from_less, + node_traits> + compare_functor; + + search_result res; + if (search(res, key, compare_functor())) + { + m_Stat.onFindSuccess(); + return true; + } + m_Stat.onFindFailed(); + return false; + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find_with(Q const &key, Less pred) const + { + return contains(key, pred); + } + //@endcond + + /// Finds the key \p key + /** @anchor cds_intrusive_EllenBinTree_find_func + The function searches the item with key equal to \p key and calls the functor \p f for item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item, Q& key ); + }; + \endcode + where \p item is the item found, \p key is the find function argument. + + The functor can change non-key fields of \p item. Note that the functor is only guarantee + that \p item cannot be disposed during functor is executing. + The functor does not serialize simultaneous access to the tree \p item. If such access is + possible you must provide your own synchronization schema on item level to exclude unsafe item modifications. + + The function returns \p true if \p key is found, \p false otherwise. + */ + template + bool find(Q &key, Func f) const + { + return find_(key, f); + } + //@cond + template + bool find(Q const &key, Func f) const + { + return find_(key, f); + } + //@endcond + /// Finds the key \p key with comparing functor \p pred + /** + The function is an analog of \ref cds_intrusive_EllenBinTree_find_func "find(Q&, Func)" + but \p pred is used for key comparison. + \p Less functor has the interface like \p std::less and should meet \ref cds_intrusive_EllenBinTree_less + "Predicate requirements". + \p pred must imply the same element order as the comparator used for building the tree. + */ + //@cond + template + bool find_with(Q const &key, Less pred, Func f) const + { + return find_with_(key, pred, f); + } + //@endcond + + /// Checks if the tree is empty + bool empty() const + { + return m_Root.m_pLeft.load(memory_model::memory_order_relaxed)->is_leaf(); + } + + /// Clears the tree + void clear() + { + while (true) + { + internal_node *pParent = nullptr; + internal_node *pGrandParent = nullptr; + tree_node *pLeaf = const_cast(&m_Root); + + // Get leftmost leaf + while (pLeaf->is_internal()) + { + pGrandParent = pParent; + pParent = static_cast(pLeaf); + pLeaf = pParent->m_pLeft.load(memory_model::memory_order_relaxed); + } + + if (pLeaf->infinite_key()) + { + m_ItemCounter.reset(); + return; + } + + // Remove leftmost leaf and its parent node + assert(pGrandParent); + assert(pParent); + assert(pLeaf->is_leaf()); + + pGrandParent->m_pLeft.store(pParent->m_pRight.load(memory_model::memory_order_relaxed), memory_model::memory_order_relaxed); + free_leaf_node(node_traits::to_value_ptr(static_cast(pLeaf))); + free_internal_node(pParent); + m_ItemCounter--; + } + } + + /// Returns item count in the tree + /** + Only leaf nodes containing user data are counted. + + The value returned depends on item counter type provided by \p Traits template parameter. + If it is \p atomicity::empty_item_counter this function always returns 0. + The function is not suitable for checking the tree emptiness, use \p empty() + member function for this purpose. + */ + size_t size() const + { + return m_ItemCounter; + } + + /// Returns const reference to internal statistics + stat const &statistics() const + { + return m_Stat; + } + + /// Checks internal consistency (not atomic, not thread-safe) + /** + The debugging function to check internal consistency of the tree. + */ + bool check_consistency() const + { + return check_consistency(&m_Root); + } + + protected: + //@cond + + bool check_consistency(internal_node const *pRoot) const + { + tree_node *pLeft = pRoot->m_pLeft.load(atomics::memory_order_relaxed); + tree_node *pRight = pRoot->m_pRight.load(atomics::memory_order_relaxed); + assert(pLeft); + assert(pRight); + + if (node_compare()(*pLeft, *pRoot) < 0 && node_compare()(*pRoot, *pRight) <= 0 && node_compare()(*pLeft, *pRight) < 0) + { + bool bRet = true; + if (pLeft->is_internal()) + bRet = check_consistency(static_cast(pLeft)); + assert(bRet); + + if (bRet && pRight->is_internal()) + bRet = bRet && check_consistency(static_cast(pRight)); + assert(bRet); + + return bRet; + } + return false; + } + + template + bool search(search_result &res, KeyValue const &key, Compare cmp) const + { + internal_node *pParent; + tree_node *pLeaf; + update_ptr updParent; + + bool bRightLeaf; + bool bRightParent = false; + + int nCmp = 0; + + pParent = nullptr; + pLeaf = const_cast(&m_Root); + updParent = nullptr; + bRightLeaf = false; + while (pLeaf->is_internal()) + { + pParent = static_cast(pLeaf); + bRightParent = bRightLeaf; + updParent = pParent->m_pUpdate.load(memory_model::memory_order_acquire); + nCmp = cmp(key, *pParent); + bRightLeaf = nCmp >= 0; + pLeaf = pParent->get_child(nCmp >= 0, memory_model::memory_order_acquire); + }; + assert(pLeaf->is_leaf()); + + leaf_node *keyValue = static_cast(pLeaf); + nCmp = cmp(key, *static_cast(pLeaf)); + + res.pParent = pParent; + res.pLeaf = static_cast(pLeaf); + res.updParent = updParent; + res.bRightLeaf = bRightLeaf; + return nCmp == 0; + } + + void help_insert(update_desc *pOp) + { + tree_node *pLeaf = static_cast(pOp->iInfo.pLeaf); + if (pOp->iInfo.bRightLeaf) + { + pOp->iInfo.pParent->m_pRight.compare_exchange_strong(pLeaf, static_cast(pOp->iInfo.pNew), + memory_model::memory_order_release, atomics::memory_order_relaxed); + } + else + { + pOp->iInfo.pParent->m_pLeft.compare_exchange_strong(pLeaf, static_cast(pOp->iInfo.pNew), + memory_model::memory_order_release, atomics::memory_order_relaxed); + } + + // Unflag parent + update_ptr cur(pOp, update_desc::IFlag); + CDS_VERIFY(pOp->iInfo.pParent->m_pUpdate.compare_exchange_strong(cur, pOp->iInfo.pParent->null_update_desc(), + memory_model::memory_order_release, atomics::memory_order_relaxed)); + } + + bool try_insert(value_type &val, internal_node *pNewInternal, search_result &res) + { + assert(res.updParent.bits() == update_desc::Clean); + assert(res.pLeaf->is_leaf()); + + if (static_cast(res.pParent->get_child(res.bRightLeaf, memory_model::memory_order_relaxed)) == res.pLeaf) + { + leaf_node *pNewLeaf = node_traits::to_node_ptr(val); + + int nCmp = node_compare()(val, *res.pLeaf); + if (nCmp < 0) + { + if (res.pLeaf->infinite_key()) + { + pNewInternal->infinite_key(1); + } + else + { + pNewInternal->infinite_key(0); + key_extractor()(pNewInternal->m_Key, *node_traits::to_value_ptr(res.pLeaf)); + } + pNewInternal->m_pLeft.store(static_cast(pNewLeaf), memory_model::memory_order_relaxed); + pNewInternal->m_pRight.store(static_cast(res.pLeaf), memory_model::memory_order_relaxed); + } + else + { + pNewInternal->infinite_key(0); + key_extractor()(pNewInternal->m_Key, val); + pNewInternal->m_pLeft.store(static_cast(res.pLeaf), memory_model::memory_order_relaxed); + pNewInternal->m_pRight.store(static_cast(pNewLeaf), memory_model::memory_order_relaxed); + } + update_desc *pOp = alloc_update_desc(); + + pOp->iInfo.pParent = res.pParent; + pOp->iInfo.pNew = pNewInternal; + pOp->iInfo.pLeaf = res.pLeaf; + pOp->iInfo.bRightLeaf = res.bRightLeaf; + + update_ptr updCur(res.updParent.ptr()); + + if (res.pParent->m_pUpdate.compare_exchange_strong(updCur, update_ptr(pOp, update_desc::IFlag), + memory_model::memory_order_acq_rel, atomics::memory_order_acquire)) + { + + // do insert + help_insert(pOp); + return true; + } + else + { + free_update_desc(pOp); + } + } + return false; + } + + template + bool find_with(Q &key, Less pred, Func f) const + { + return find_with_(key, pred, f); + } + + template + bool find_with_(Q &val, Less pred, Func f) const + { + typedef ellen_bintree::details::compare< + key_type, + value_type, + opt::details::make_comparator_from_less, + node_traits> + compare_functor; + + search_result res; + if (search(res, val, compare_functor())) + { + assert(res.pLeaf); + f(*node_traits::to_value_ptr(res.pLeaf), val); + m_Stat.onFindSuccess(); + return true; + } + m_Stat.onFindFailed(); + return false; + } + + template + bool find_(Q &val, Func f) const + { + search_result res; + if (search(res, val, node_compare())) + { + f(*node_traits::to_value_ptr(res.pLeaf), val); + m_Stat.onFindSuccess(); + return true; + } + m_Stat.onFindFailed(); + return false; + } + + void make_empty_tree() + { + m_Root.infinite_key(2); + m_LeafInf1.infinite_key(1); + m_LeafInf2.infinite_key(2); + m_Root.m_pLeft.store(&m_LeafInf1, memory_model::memory_order_relaxed); + m_Root.m_pRight.store(&m_LeafInf2, memory_model::memory_order_release); + } +}; +} // namespace intrusive +} // namespace cds +#endif 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 0792c86cc..43e5371b2 100644 --- a/test/stress/map/find_string/map_find_string_ellentree.cpp +++ b/test/stress/map/find_string/map_find_string_ellentree.cpp @@ -1,7 +1,32 @@ -// Copyright (c) 2006-2018 Maxim Khizhinsky -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ #include "map_find_string.h" #include "map_type_ellen_bintree.h" @@ -9,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 c926fab38..da55f1724 100644 --- a/test/stress/map/map_type_ellen_bintree.h +++ b/test/stress/map/map_type_ellen_bintree.h @@ -1,7 +1,32 @@ -// Copyright (c) 2006-2018 Maxim Khizhinsky -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ #ifndef CDSUNIT_MAP_TYPE_ELLEN_BINTREE_H #define CDSUNIT_MAP_TYPE_ELLEN_BINTREE_H @@ -11,6 +36,7 @@ #include #include #include +#include #include #include "framework/ellen_bintree_update_desc_pool.h" @@ -24,7 +50,7 @@ namespace map { public: template EllenBinTreeMap( Config const& /*cfg*/) - : base_class() + : base_class() {} std::pair extract_min_key() @@ -84,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; @@ -97,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; @@ -124,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; @@ -154,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 @@ -192,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 { @@ -200,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 ) { @@ -242,6 +285,7 @@ namespace map { { EXPECT_TRUE( m.check_consistency()); } + } // namespace map @@ -292,8 +336,15 @@ namespace map { CDSSTRESS_EllenBinTreeMap_case( fixture, test_case, EllenBinTreeMap_rcu_gpt_stat, key_type, value_type ) \ CDSSTRESS_EllenBinTreeMap_RCU_1( fixture, test_case, key_type, value_type ) \ +#define CDSSTRESS_EllenBinTreeMap_NOGC( fixture, test_case, key_type, value_type ) \ + CDSSTRESS_EllenBinTreeMap_case( fixture, test_case, EllenBinTreeMap_nogc, key_type, value_type ) \ + + #define CDSSTRESS_EllenBinTreeMap( fixture, test_case, key_type, value_type ) \ CDSSTRESS_EllenBinTreeMap_HP( fixture, test_case, key_type, value_type ) \ CDSSTRESS_EllenBinTreeMap_RCU( fixture, test_case, key_type, value_type ) \ +#define CDSSTRESS_EllenBinTreeMapNOGC( fixture, test_case, key_type, value_type ) \ + CDSSTRESS_EllenBinTreeMap_NOGC( fixture, test_case, key_type, value_type ) \ + #endif // ifndef CDSUNIT_MAP_TYPE_ELLEN_BINTREE_H diff --git a/test/unit/tree/CMakeLists.txt b/test/unit/tree/CMakeLists.txt index 3f0e473ee..f52c511e8 100644 --- a/test/unit/tree/CMakeLists.txt +++ b/test/unit/tree/CMakeLists.txt @@ -1,7 +1,7 @@ set(PACKAGE_NAME unit-tree) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof") - + set(CDSGTEST_TREE_SOURCES ../main.cpp bronson_avltree_map_rcu_gpb.cpp @@ -31,6 +31,8 @@ set(CDSGTEST_TREE_SOURCES intrusive_ellenbintree_rcu_gpi.cpp intrusive_ellenbintree_rcu_gpt.cpp intrusive_ellenbintree_rcu_shb.cpp + intrusive_ellen_bintree_nogc.cpp + ) include_directories( diff --git a/test/unit/tree/intrusive_ellen_bintree_nogc.cpp b/test/unit/tree/intrusive_ellen_bintree_nogc.cpp new file mode 100644 index 000000000..c3354941a --- /dev/null +++ b/test/unit/tree/intrusive_ellen_bintree_nogc.cpp @@ -0,0 +1,37 @@ +#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> base_item_type; + typedef typename base_class::member_int_item> 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::opt::compare>>::type> + tree_type; + + tree_type t; + test(t); +} + +} // namespace + + 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..c150d8f5d --- /dev/null +++ b/test/unit/tree/test_intrusive_tree_nogc.h @@ -0,0 +1,139 @@ +#ifndef CDSUNIT_TREE_TEST_INTRUSIVE_TREE_NOGC_H +#define CDSUNIT_TREE_TEST_INTRUSIVE_TREE_NOGC_H + +#include "test_intrusive_tree.h" +#include "cds_test/fixture.h" +#include +namespace cds +{ +namespace intrusive +{ +} +} // namespace cds + +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 data; + std::vector 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(); }); + } +}; +} // namespace cds_test + +#endif