From: khizmax Date: Sat, 27 Sep 2014 18:52:17 +0000 (+0400) Subject: Rename cds/container/lazy_list_impl.h to cds/container/impl/lazy_list.h X-Git-Tag: v2.0.0~265 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=f0640d41ca56303a1dcb99ae6c11e51f05ef9898;p=libcds.git Rename cds/container/lazy_list_impl.h to cds/container/impl/lazy_list.h --- diff --git a/cds/container/impl/lazy_list.h b/cds/container/impl/lazy_list.h new file mode 100644 index 00000000..237f64b7 --- /dev/null +++ b/cds/container/impl/lazy_list.h @@ -0,0 +1,833 @@ +//$$CDS-header$$ + +#ifndef __CDS_CONTAINER_IMPL_LAZY_LIST_H +#define __CDS_CONTAINER_IMPL_LAZY_LIST_H + +#include +#include + +namespace cds { namespace container { + + /// Lazy ordered list + /** @ingroup cds_nonintrusive_list + \anchor cds_nonintrusive_LazyList_gc + + Usually, ordered single-linked list is used as a building block for the hash table implementation. + The complexity of searching is O(N). + + Source: + - [2005] Steve Heller, Maurice Herlihy, Victor Luchangco, Mark Moir, William N. Scherer III, and Nir Shavit + "A Lazy Concurrent List-Based Set Algorithm" + + The lazy list is based on an optimistic locking scheme for inserts and removes, + eliminating the need to use the equivalent of an atomically markable + reference. It also has a novel wait-free membership \p find operation + that does not need to perform cleanup operations and is more efficient. + + It is non-intrusive version of cds::intrusive::LazyList class. + + Template arguments: + - \p GC - garbage collector used + - \p T - type stored in the list. The type must be default- and copy-constructible. + - \p Traits - type traits, default is lazy_list::type_traits + + Unlike standard container, this implementation does not divide type \p T into key and value part and + may be used as main building block for hash set algorithms. + + The key is a function (or a part) of type \p T, and this function is specified by Traits::compare functor + or Traits::less predicate. + + You don't need to include . Instead, you should do: + - - for gc::HP-based lazy list + - - for gc::PTB-based lazy list + - - for @ref cds_urcu_desc "RCU" based lazy list + + LazyKVList is a key-value version of lazy non-intrusive list that is closer to the C++ std library approach. + + It is possible to declare option-based list with cds::container::lazy_list::make_traits metafunction istead of \p Traits template + argument. For example, the following traits-based declaration of gc::HP lazy list + \code + #include + // Declare comparator for the item + struct my_compare { + int operator ()( int i1, int i2 ) + { + return i1 - i2; + } + }; + + // Declare type_traits + struct my_traits: public cds::container::lazy_list::type_traits + { + typedef my_compare compare; + }; + + // Declare traits-based list + typedef cds::container::LazyList< cds::gc::HP, int, my_traits > traits_based_list; + \endcode + + is equivalent for the following option-based list + \code + #include + + // my_compare is the same + + // Declare option-based list + typedef cds::container::LazyList< cds::gc::HP, int, + typename cds::container::lazy_list::make_traits< + cds::container::opt::compare< my_compare > // item comparator option + >::type + > option_based_list; + \endcode + + Template argument list \p Options of cds::container::lazy_list::make_traits metafunction are: + - opt::lock_type - lock type for per-node locking. Default is cds::lock::Spin. Note that each node + of the list has member of type \p lock_type, therefore, heavy-weighted locking primitive is not + acceptable as candidate for \p lock_type. + - opt::compare - key compare functor. No default functor is provided. + If the option is not specified, the opt::less is used. + - opt::less - specifies binary predicate used for key compare. Default is \p std::less. + - opt::back_off - back-off strategy used. If the option is not specified, the cds::backoff::empty is used. + - opt::item_counter - the type of item counting feature. Default is \ref atomicity::empty_item_counter that is no item counting. + - opt::allocator - the allocator used for creating and freeing list's item. Default is \ref CDS_DEFAULT_ALLOCATOR macro. + - opt::memory_model - C++ memory ordering model. Can be opt::v::relaxed_ordering (relaxed memory model, the default) + or opt::v::sequential_consistent (sequentially consisnent memory model). + + \par Usage + There are different specializations of this template for each garbage collecting schema used. + You should include appropriate .h-file depending on GC you are using: + - for gc::HP: \code #include \endcode + - for gc::PTB: \code #include \endcode + - for gc::HRC: \code #include \endcode + - for \ref cds_urcu_desc "RCU": \code #include \endcode + - for gc::nogc: \code #include \endcode + */ + template < + typename GC, + typename T, +#ifdef CDS_DOXYGEN_INVOKED + typename Traits = lazy_list::type_traits +#else + typename Traits +#endif + > + class LazyList: +#ifdef CDS_DOXYGEN_INVOKED + protected intrusive::LazyList< GC, T, Traits > +#else + protected details::make_lazy_list< GC, T, Traits >::type +#endif + { + //@cond + typedef details::make_lazy_list< GC, T, Traits > options; + typedef typename options::type base_class; + //@endcond + + public: + typedef T value_type ; ///< Type of value stored in the list + typedef typename base_class::gc gc ; ///< Garbage collector used + typedef typename base_class::back_off back_off ; ///< Back-off strategy used + typedef typename options::allocator_type allocator_type ; ///< Allocator type used for allocate/deallocate the nodes + typedef typename base_class::item_counter item_counter ; ///< Item counting policy used + typedef typename options::key_comparator key_comparator ; ///< key comparison functor + typedef typename base_class::memory_model memory_model ; ///< Memory ordering. See cds::opt::memory_model option + + protected: + //@cond + typedef typename base_class::value_type node_type; + typedef typename options::cxx_allocator cxx_allocator; + typedef typename options::node_deallocator node_deallocator; + typedef typename options::type_traits::compare intrusive_key_comparator; + + typedef typename base_class::node_type head_type; + //@endcond + + public: + /// Guarded pointer + typedef cds::gc::guarded_ptr< gc, node_type, value_type, details::guarded_ptr_cast_set > guarded_ptr; + + private: + //@cond + static value_type& node_to_value( node_type& n ) + { + return n.m_Value; + } + static value_type const& node_to_value( node_type const& n ) + { + return n.m_Value; + } + //@endcond + + protected: + //@cond + template + static node_type * alloc_node( Q const& v ) + { + return cxx_allocator().New( v ); + } + + template + static node_type * alloc_node( Args&&... args ) + { + return cxx_allocator().MoveNew( std::forward(args)... ); + } + + static void free_node( node_type * pNode ) + { + cxx_allocator().Delete( pNode ); + } + + struct node_disposer { + void operator()( node_type * pNode ) + { + free_node( pNode ); + } + }; + typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr; + + head_type& head() + { + return *base_class::head(); + } + + head_type const& head() const + { + return *base_class::head(); + } + + head_type& tail() + { + return *base_class::tail(); + } + + head_type const& tail() const + { + return *base_class::tail(); + } + //@endcond + + protected: + //@cond + template + class iterator_type: protected base_class::template iterator_type + { + typedef typename base_class::template iterator_type iterator_base; + + iterator_type( head_type const& pNode ) + : iterator_base( const_cast( &pNode )) + {} + + iterator_type( head_type const * pNode ) + : iterator_base( const_cast( pNode )) + {} + + friend class LazyList; + + public: + typedef typename cds::details::make_const_type::pointer value_ptr; + typedef typename cds::details::make_const_type::reference value_ref; + + iterator_type() + {} + + iterator_type( const iterator_type& src ) + : iterator_base( src ) + {} + + value_ptr operator ->() const + { + typename iterator_base::value_ptr p = iterator_base::operator ->(); + return p ? &(p->m_Value) : nullptr; + } + + value_ref operator *() const + { + return (iterator_base::operator *()).m_Value; + } + + /// Pre-increment + iterator_type& operator ++() + { + iterator_base::operator ++(); + return *this; + } + + template + bool operator ==(iterator_type const& i ) const + { + return iterator_base::operator ==(i); + } + template + bool operator !=(iterator_type const& i ) const + { + return iterator_base::operator !=(i); + } + }; + //@endcond + + public: + /// Forward iterator + /** + The forward iterator for lazy list has some features: + - it has no post-increment operator + - to protect the value, the iterator contains a GC-specific guard + another guard is required locally for increment operator. + For some GC (gc::HP, gc::HRC), a guard is limited resource per thread, so an exception (or assertion) "no free guard" + may be thrown if a limit of guard count per thread is exceeded. + - The iterator cannot be moved across thread boundary since it contains GC's guard that is thread-private GC data. + - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent + deleting operations it is no guarantee that you iterate all item in the list. + + Therefore, the use of iterators in concurrent environment is not good idea. Use the iterator on the concurrent container + for debug purpose only. + */ + typedef iterator_type iterator; + + /// Const forward iterator + /** + For iterator's features and requirements see \ref iterator + */ + typedef iterator_type const_iterator; + + /// Returns a forward iterator addressing the first element in a list + /** + For empty list \code begin() == end() \endcode + */ + iterator begin() + { + iterator it( head() ); + ++it ; // skip dummy head node + return it; + } + + /// Returns an iterator that addresses the location succeeding the last element in a list + /** + Do not use the value returned by end function to access any item. + + The returned value can be used only to control reaching the end of the list. + For empty list \code begin() == end() \endcode + */ + iterator end() + { + return iterator( tail() ); + } + + /// Returns a forward const iterator addressing the first element in a list + //@{ + const_iterator begin() const + { + const_iterator it( head() ); + ++it ; // skip dummy head node + return it; + } + const_iterator cbegin() + { + const_iterator it( head() ); + ++it ; // skip dummy head node + return it; + } + //@} + + /// Returns an const iterator that addresses the location succeeding the last element in a list + //@{ + const_iterator end() const + { + return const_iterator( tail() ); + } + const_iterator cend() + { + return const_iterator( tail() ); + } + //@} + + public: + /// Default constructor + /** + Initializes empty list + */ + LazyList() + {} + + /// List desctructor + /** + Clears the list + */ + ~LazyList() + { + clear(); + } + + /// Inserts new node + /** + The function creates a node with copy of \p val value + and then inserts the node created into the list. + + The type \p Q should contain as minimum the complete key of the node. + The object of \ref value_type should be constructible from \p val of type \p Q. + In trivial case, \p Q is equal to \ref value_type. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert( Q const& val ) + { + return insert_at( head(), val ); + } + + /// Inserts new node + /** + This function inserts new node with default-constructed value and then it calls + \p func functor with signature + \code void func( value_type& itemValue ) ;\endcode + + The argument \p itemValue of user-defined functor \p func is the reference + to the list's item inserted. User-defined functor \p func should guarantee that during changing + item's value no any other changes could be made on this list's item by concurrent threads. + The user-defined functor can be passed by reference using boost::ref + and it is called only if the inserting is success. + + The type \p Q should contain the complete key of the node. + The object of \ref value_type should be constructible from \p key of type \p Q. + + The function allows to split creating of new item into two part: + - create item from \p key with initializing key-fields only; + - insert new item into the list; + - if inserting is successful, initialize non-key fields of item by calling \p f 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. + */ + template + bool insert( Q const& key, Func func ) + { + return insert_at( head(), key, func ); + } + + /// Inserts data of type \ref value_type constructed with std::forward(args)... + /** + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool emplace( Args&&... args ) + { + return emplace_at( head(), std::forward(args)... ); + } + + /// Ensures that the \p key exists in the list + /** + The operation performs inserting or changing data with lock-free manner. + + If the \p key not found in the list, then the new item created from \p key + is inserted into the list. Otherwise, the functor \p func is called with the item found. + The functor \p Func should be a function with signature: + \code + void func( bool bNew, value_type& item, const Q& val ); + \endcode + or a functor: + \code + struct my_functor { + void operator()( bool bNew, value_type& item, const Q& val ); + }; + \endcode + + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - item of the list + - \p val - argument \p key passed into the \p ensure function + + The functor may 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. + + You may pass \p func argument by reference using boost::ref. + + Returns std::pair where \p first is true if operation is successfull, + \p second is true if new item has been added or \p false if the item with \p key + already is in the list. + */ + template + std::pair ensure( Q const& key, Func f ) + { + return ensure_at( head(), key, f ); + } + + /// Deletes \p key from the list + /** \anchor cds_nonintrusive_LazyList_hp_erase_val + Since the key of LazyList's item type \p T is not explicitly specified, + template parameter \p Q defines the key type searching in the list. + The list item comparator should be able to compare the type \p T of list item + and the type \p Q. + + Return \p true if key is found and deleted, \p false otherwise + */ + template + bool erase( Q const& key ) + { + return erase_at( head(), key, intrusive_key_comparator(), [](value_type const&){} ); + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_LazyList_hp_erase_val "erase(Q const&)" + but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + bool erase_with( Q const& key, Less pred ) + { + return erase_at( head(), key, typename options::template less_wrapper::type(), [](value_type const&){} ); + } + + /// Deletes \p key from the list + /** \anchor cds_nonintrusive_LazyList_hp_erase_func + The function searches an item with key \p key, calls \p f functor with item found + and deletes the item. If \p key is not found, the functor is not called. + + The functor \p Func interface: + \code + struct extractor { + void operator()(const value_type& val) { ... } + }; + \endcode + The functor may be passed by reference with boost:ref + + Since the key of LazyList's item type \p T is not explicitly specified, + template parameter \p Q defines the key type searching in the list. + The list item comparator should be able to compare the type \p T of list item + and the type \p Q. + + Return \p true if key is found and deleted, \p false otherwise + + See also: \ref erase + */ + template + bool erase( Q const& key, Func f ) + { + return erase_at( head(), key, intrusive_key_comparator(), f ); + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_LazyList_hp_erase_func "erase(Q const&, Func)" + but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + bool erase_with( Q const& key, Less pred, Func f ) + { + return erase_at( head(), key, typename options::template less_wrapper::type(), f ); + } + + /// Extracts the item from the list with specified \p key + /** \anchor cds_nonintrusive_LazyList_hp_extract + The function searches an item with key equal to \p key, + unlinks it from the list, and returns it in \p dest parameter. + If the item with key equal to \p key is not found the function returns \p false. + + Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type. + + @note Each \p guarded_ptr object uses the GC's guard that can be limited resource. + + Usage: + \code + typedef cds::container::LazyList< cds::gc::HP, foo, my_traits > ord_list; + ord_list theList; + // ... + { + ord_list::guarded_ptr gp; + theList.extract( gp, 5 ); + // Deal with gp + // ... + + // Destructor of gp releases internal HP guard and frees the item + } + \endcode + */ + template + bool extract( guarded_ptr& dest, Q const& key ) + { + return extract_at( head(), dest.guard(), key, intrusive_key_comparator() ); + } + + /// Extracts the item from the list with comparing functor \p pred + /** + The function is an analog of \ref cds_nonintrusive_LazyList_hp_extract "extract(guarded_ptr&, Q const&)" + but \p pred predicate is used for key comparing. + + \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q + in any order. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + bool extract_with( guarded_ptr& dest, Q const& key, Less pred ) + { + return extract_at( head(), dest.guard(), key, typename options::template less_wrapper::type() ); + } + + /// Finds the key \p key + /** \anchor cds_nonintrusive_LazyList_hp_find_val + 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 find( Q const& key ) + { + return find_at( head(), key, intrusive_key_comparator() ); + } + + /// Finds the key \p val using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_LazyList_hp_find_val "find(Q const&)" + but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + bool find_with( Q const& key, Less pred ) + { + return find_at( head(), key, typename options::template less_wrapper::type() ); + } + + /// Finds the key \p val and performs an action with it + /** \anchor cds_nonintrusive_LazyList_hp_find_func + The function searches an item with key equal to \p val and calls the functor \p f for the item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item, Q& val ); + }; + \endcode + where \p item is the item found, \p val is the find function argument. + + You may pass \p f argument by reference using boost::ref or cds::ref. + + The functor may change non-key fields of \p item. Note that the function is only guarantee + that \p item cannot be deleted during functor is executing. + The function does not serialize simultaneous access to the list \p item. If such access is + possible you must provide your own synchronization schema to exclude unsafe item modifications. + + The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor + may modify both arguments. + + The function returns \p true if \p val is found, \p false otherwise. + */ + template + bool find( Q& val, Func f ) + { + return find_at( head(), val, intrusive_key_comparator(), f ); + } + + /// Finds the key \p val using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_LazyList_hp_find_func "find(Q&, Func)" + but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + bool find_with( Q& val, Less pred, Func f ) + { + return find_at( head(), val, typename options::template less_wrapper::type(), f ); + } + + /// Finds the key \p val and performs an action with it + /** \anchor cds_nonintrusive_LazyList_hp_find_cfunc + The function searches an item with key equal to \p val and calls the functor \p f for the item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item, Q const& val ); + }; + \endcode + where \p item is the item found, \p val is the find function argument. + + You may pass \p f argument by reference using boost::ref or cds::ref. + + The function does not serialize simultaneous access to the list \p item. If such access is + possible you must provide your own synchronization schema to exclude unsafe item modifications. + + The function returns \p true if \p val is found, \p false otherwise. + */ + template + bool find( Q const& val, Func f ) + { + return find_at( head(), val, intrusive_key_comparator(), f ); + } + + /// Finds the key \p val using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_LazyList_hp_find_cfunc "find(Q&, Func)" + but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + bool find_with( Q const& val, Less pred, Func f ) + { + return find_at( head(), val, typename options::template less_wrapper::type(), f ); + } + + /// Finds the key \p val and return the item found + /** \anchor cds_nonintrusive_LazyList_hp_get + The function searches the item with key equal to \p val + and assigns the item found to guarded pointer \p ptr. + The function returns \p true if \p val is found, and \p false otherwise. + If \p val is not found the \p ptr parameter is not changed. + + @note Each \p guarded_ptr object uses one GC's guard which can be limited resource. + + Usage: + \code + typedef cds::container::LazyList< cds::gc::HP, foo, my_traits > ord_list; + ord_list theList; + // ... + { + ord_list::guarded_ptr gp; + if ( theList.get( gp, 5 )) { + // Deal with gp + //... + } + // Destructor of guarded_ptr releases internal HP guard and frees the item + } + \endcode + + Note the compare functor specified for class \p Traits template parameter + should accept a parameter of type \p Q that can be not the same as \p value_type. + */ + template + bool get( guarded_ptr& ptr, Q const& val ) + { + return get_at( head(), ptr.guard(), val, intrusive_key_comparator() ); + } + + /// Finds the key \p val and return the item found + /** + The function is an analog of \ref cds_nonintrusive_LazyList_hp_get "get( guarded_ptr& ptr, 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 \ref value_type and \p Q + in any order. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + bool get_with( guarded_ptr& ptr, Q const& val, Less pred ) + { + return get_at( head(), ptr.guard(), val, typename options::template less_wrapper::type() ); + } + + /// Checks if the list is empty + bool empty() const + { + return base_class::empty(); + } + + /// Returns list's item count + /** + The value returned depends on opt::item_counter option. For atomicity::empty_item_counter, + this function always returns 0. + + Warning: even if you use real item counter and it returns 0, this fact is not mean that the list + is empty. To check list emptyness use \ref empty() method. + */ + size_t size() const + { + return base_class::size(); + } + + /// Clears the list + /** + Post-condition: the list is empty + */ + void clear() + { + base_class::clear(); + } + + protected: + //@cond + bool insert_node_at( head_type& refHead, node_type * pNode ) + { + assert( pNode != nullptr ); + scoped_node_ptr p( pNode ); + + if ( base_class::insert_at( &refHead, *pNode )) { + p.release(); + return true; + } + + return false; + } + + template + bool insert_at( head_type& refHead, const Q& val ) + { + return insert_node_at( refHead, alloc_node( val )); + } + + template + bool emplace_at( head_type& refHead, Args&&... args ) + { + return insert_node_at( refHead, alloc_node( std::forward(args)... )); + } + + template + bool insert_at( head_type& refHead, const Q& key, Func f ) + { + scoped_node_ptr pNode( alloc_node( key )); + + if ( base_class::insert_at( &refHead, *pNode, [&f](node_type& node){ cds::unref(f)( node_to_value(node) ); } )) { + pNode.release(); + return true; + } + return false; + } + + template + bool erase_at( head_type& refHead, const Q& key, Compare cmp, Func f ) + { + return base_class::erase_at( &refHead, key, cmp, [&f](node_type const& node){ cds::unref(f)( node_to_value(node) ); } ); + } + + template + bool extract_at( head_type& refHead, typename gc::Guard& dest, Q const& key, Compare cmp ) + { + return base_class::extract_at( &refHead, dest, key, cmp ); + } + + template + std::pair ensure_at( head_type& refHead, const Q& key, Func f ) + { + scoped_node_ptr pNode( alloc_node( key )); + + std::pair ret = base_class::ensure_at( &refHead, *pNode, + [&f, &key](bool bNew, node_type& node, node_type&){cds::unref(f)( bNew, node_to_value(node), key ); }); + if ( ret.first && ret.second ) + pNode.release(); + + return ret; + } + + template + bool find_at( head_type& refHead, Q const& key, Compare cmp ) + { + return base_class::find_at( &refHead, key, cmp ); + } + + template + bool find_at( head_type& refHead, Q& val, Compare cmp, Func f ) + { + return base_class::find_at( &refHead, val, cmp, [&f](node_type& node, Q& val){ cds::unref(f)( node_to_value(node), val ); }); + } + + template + bool get_at( head_type& refHead, typename gc::Guard& guard, Q const& key, Compare cmp ) + { + return base_class::get_at( &refHead, guard, key, cmp ); + } + + //@endcond + }; + +}} // namespace cds::container + +#endif // #ifndef __CDS_CONTAINER_IMPL_LAZY_LIST_H diff --git a/cds/container/lazy_list_hp.h b/cds/container/lazy_list_hp.h index cbb73cdc..d844fd27 100644 --- a/cds/container/lazy_list_hp.h +++ b/cds/container/lazy_list_hp.h @@ -6,6 +6,6 @@ #include #include #include -#include +#include #endif // #ifndef __CDS_CONTAINER_LAZY_LIST_HP_H diff --git a/cds/container/lazy_list_hrc.h b/cds/container/lazy_list_hrc.h index d466fd19..0a6b023a 100644 --- a/cds/container/lazy_list_hrc.h +++ b/cds/container/lazy_list_hrc.h @@ -6,6 +6,6 @@ #include #include #include -#include +#include #endif // #ifndef __CDS_CONTAINER_LAZY_LIST_HRC_H diff --git a/cds/container/lazy_list_impl.h b/cds/container/lazy_list_impl.h deleted file mode 100644 index 0b0b67cf..00000000 --- a/cds/container/lazy_list_impl.h +++ /dev/null @@ -1,828 +0,0 @@ -//$$CDS-header$$ - -#ifndef __CDS_CONTAINER_LAZY_LIST_IMPL_H -#define __CDS_CONTAINER_LAZY_LIST_IMPL_H - -#include -#include - -namespace cds { namespace container { - - /// Lazy ordered list - /** @ingroup cds_nonintrusive_list - \anchor cds_nonintrusive_LazyList_gc - - Usually, ordered single-linked list is used as a building block for the hash table implementation. - The complexity of searching is O(N). - - Source: - - [2005] Steve Heller, Maurice Herlihy, Victor Luchangco, Mark Moir, William N. Scherer III, and Nir Shavit - "A Lazy Concurrent List-Based Set Algorithm" - - The lazy list is based on an optimistic locking scheme for inserts and removes, - eliminating the need to use the equivalent of an atomically markable - reference. It also has a novel wait-free membership \p find operation - that does not need to perform cleanup operations and is more efficient. - - It is non-intrusive version of cds::intrusive::LazyList class - - Template arguments: - - \p GC - garbage collector used - - \p T - type stored in the list. The type must be default- and copy-constructible. - - \p Traits - type traits, default is lazy_list::type_traits - - Unlike standard container, this implementation does not divide type \p T into key and value part and - may be used as main building block for hash set algorithms. - - The key is a function (or a part) of type \p T, and this function is specified by Traits::compare functor - or Traits::less predicate - - LazyKVList is a key-value version of lazy non-intrusive list that is closer to the C++ std library approach. - - It is possible to declare option-based list with cds::container::lazy_list::make_traits metafunction istead of \p Traits template - argument. For example, the following traits-based declaration of gc::HP lazy list - \code - #include - // Declare comparator for the item - struct my_compare { - int operator ()( int i1, int i2 ) - { - return i1 - i2; - } - }; - - // Declare type_traits - struct my_traits: public cds::container::lazy_list::type_traits - { - typedef my_compare compare; - }; - - // Declare traits-based list - typedef cds::container::LazyList< cds::gc::HP, int, my_traits > traits_based_list; - \endcode - - is equivalent for the following option-based list - \code - #include - - // my_compare is the same - - // Declare option-based list - typedef cds::container::LazyList< cds::gc::HP, int, - typename cds::container::lazy_list::make_traits< - cds::container::opt::compare< my_compare > // item comparator option - >::type - > option_based_list; - \endcode - - Template argument list \p Options of cds::container::lazy_list::make_traits metafunction are: - - opt::lock_type - lock type for per-node locking. Default is cds::lock::Spin. Note that each node - of the list has member of type \p lock_type, therefore, heavy-weighted locking primitive is not - acceptable as candidate for \p lock_type. - - opt::compare - key compare functor. No default functor is provided. - If the option is not specified, the opt::less is used. - - opt::less - specifies binary predicate used for key compare. Default is \p std::less. - - opt::back_off - back-off strategy used. If the option is not specified, the cds::backoff::empty is used. - - opt::item_counter - the type of item counting feature. Default is \ref atomicity::empty_item_counter that is no item counting. - - opt::allocator - the allocator used for creating and freeing list's item. Default is \ref CDS_DEFAULT_ALLOCATOR macro. - - opt::memory_model - C++ memory ordering model. Can be opt::v::relaxed_ordering (relaxed memory model, the default) - or opt::v::sequential_consistent (sequentially consisnent memory model). - - \par Usage - There are different specializations of this template for each garbage collecting schema used. - You should include appropriate .h-file depending on GC you are using: - - for gc::HP: \code #include \endcode - - for gc::PTB: \code #include \endcode - - for gc::HRC: \code #include \endcode - - for \ref cds_urcu_desc "RCU": \code #include \endcode - - for gc::nogc: \code #include \endcode - */ - template < - typename GC, - typename T, -#ifdef CDS_DOXYGEN_INVOKED - typename Traits = lazy_list::type_traits -#else - typename Traits -#endif - > - class LazyList: -#ifdef CDS_DOXYGEN_INVOKED - protected intrusive::LazyList< GC, T, Traits > -#else - protected details::make_lazy_list< GC, T, Traits >::type -#endif - { - //@cond - typedef details::make_lazy_list< GC, T, Traits > options; - typedef typename options::type base_class; - //@endcond - - public: - typedef T value_type ; ///< Type of value stored in the list - typedef typename base_class::gc gc ; ///< Garbage collector used - typedef typename base_class::back_off back_off ; ///< Back-off strategy used - typedef typename options::allocator_type allocator_type ; ///< Allocator type used for allocate/deallocate the nodes - typedef typename base_class::item_counter item_counter ; ///< Item counting policy used - typedef typename options::key_comparator key_comparator ; ///< key comparison functor - typedef typename base_class::memory_model memory_model ; ///< Memory ordering. See cds::opt::memory_model option - - protected: - //@cond - typedef typename base_class::value_type node_type; - typedef typename options::cxx_allocator cxx_allocator; - typedef typename options::node_deallocator node_deallocator; - typedef typename options::type_traits::compare intrusive_key_comparator; - - typedef typename base_class::node_type head_type; - //@endcond - - public: - /// Guarded pointer - typedef cds::gc::guarded_ptr< gc, node_type, value_type, details::guarded_ptr_cast_set > guarded_ptr; - - private: - //@cond - static value_type& node_to_value( node_type& n ) - { - return n.m_Value; - } - static value_type const& node_to_value( node_type const& n ) - { - return n.m_Value; - } - //@endcond - - protected: - //@cond - template - static node_type * alloc_node( Q const& v ) - { - return cxx_allocator().New( v ); - } - - template - static node_type * alloc_node( Args&&... args ) - { - return cxx_allocator().MoveNew( std::forward(args)... ); - } - - static void free_node( node_type * pNode ) - { - cxx_allocator().Delete( pNode ); - } - - struct node_disposer { - void operator()( node_type * pNode ) - { - free_node( pNode ); - } - }; - typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr; - - head_type& head() - { - return *base_class::head(); - } - - head_type const& head() const - { - return *base_class::head(); - } - - head_type& tail() - { - return *base_class::tail(); - } - - head_type const& tail() const - { - return *base_class::tail(); - } - //@endcond - - protected: - //@cond - template - class iterator_type: protected base_class::template iterator_type - { - typedef typename base_class::template iterator_type iterator_base; - - iterator_type( head_type const& pNode ) - : iterator_base( const_cast( &pNode )) - {} - - iterator_type( head_type const * pNode ) - : iterator_base( const_cast( pNode )) - {} - - friend class LazyList; - - public: - typedef typename cds::details::make_const_type::pointer value_ptr; - typedef typename cds::details::make_const_type::reference value_ref; - - iterator_type() - {} - - iterator_type( const iterator_type& src ) - : iterator_base( src ) - {} - - value_ptr operator ->() const - { - typename iterator_base::value_ptr p = iterator_base::operator ->(); - return p ? &(p->m_Value) : nullptr; - } - - value_ref operator *() const - { - return (iterator_base::operator *()).m_Value; - } - - /// Pre-increment - iterator_type& operator ++() - { - iterator_base::operator ++(); - return *this; - } - - template - bool operator ==(iterator_type const& i ) const - { - return iterator_base::operator ==(i); - } - template - bool operator !=(iterator_type const& i ) const - { - return iterator_base::operator !=(i); - } - }; - //@endcond - - public: - /// Forward iterator - /** - The forward iterator for lazy list has some features: - - it has no post-increment operator - - to protect the value, the iterator contains a GC-specific guard + another guard is required locally for increment operator. - For some GC (gc::HP, gc::HRC), a guard is limited resource per thread, so an exception (or assertion) "no free guard" - may be thrown if a limit of guard count per thread is exceeded. - - The iterator cannot be moved across thread boundary since it contains GC's guard that is thread-private GC data. - - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent - deleting operations it is no guarantee that you iterate all item in the list. - - Therefore, the use of iterators in concurrent environment is not good idea. Use the iterator on the concurrent container - for debug purpose only. - */ - typedef iterator_type iterator; - - /// Const forward iterator - /** - For iterator's features and requirements see \ref iterator - */ - typedef iterator_type const_iterator; - - /// Returns a forward iterator addressing the first element in a list - /** - For empty list \code begin() == end() \endcode - */ - iterator begin() - { - iterator it( head() ); - ++it ; // skip dummy head node - return it; - } - - /// Returns an iterator that addresses the location succeeding the last element in a list - /** - Do not use the value returned by end function to access any item. - - The returned value can be used only to control reaching the end of the list. - For empty list \code begin() == end() \endcode - */ - iterator end() - { - return iterator( tail() ); - } - - /// Returns a forward const iterator addressing the first element in a list - //@{ - const_iterator begin() const - { - const_iterator it( head() ); - ++it ; // skip dummy head node - return it; - } - const_iterator cbegin() - { - const_iterator it( head() ); - ++it ; // skip dummy head node - return it; - } - //@} - - /// Returns an const iterator that addresses the location succeeding the last element in a list - //@{ - const_iterator end() const - { - return const_iterator( tail() ); - } - const_iterator cend() - { - return const_iterator( tail() ); - } - //@} - - public: - /// Default constructor - /** - Initializes empty list - */ - LazyList() - {} - - /// List desctructor - /** - Clears the list - */ - ~LazyList() - { - clear(); - } - - /// Inserts new node - /** - The function creates a node with copy of \p val value - and then inserts the node created into the list. - - The type \p Q should contain as minimum the complete key of the node. - The object of \ref value_type should be constructible from \p val of type \p Q. - In trivial case, \p Q is equal to \ref value_type. - - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool insert( Q const& val ) - { - return insert_at( head(), val ); - } - - /// Inserts new node - /** - This function inserts new node with default-constructed value and then it calls - \p func functor with signature - \code void func( value_type& itemValue ) ;\endcode - - The argument \p itemValue of user-defined functor \p func is the reference - to the list's item inserted. User-defined functor \p func should guarantee that during changing - item's value no any other changes could be made on this list's item by concurrent threads. - The user-defined functor can be passed by reference using boost::ref - and it is called only if the inserting is success. - - The type \p Q should contain the complete key of the node. - The object of \ref value_type should be constructible from \p key of type \p Q. - - The function allows to split creating of new item into two part: - - create item from \p key with initializing key-fields only; - - insert new item into the list; - - if inserting is successful, initialize non-key fields of item by calling \p f 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. - */ - template - bool insert( Q const& key, Func func ) - { - return insert_at( head(), key, func ); - } - - /// Inserts data of type \ref value_type constructed with std::forward(args)... - /** - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool emplace( Args&&... args ) - { - return emplace_at( head(), std::forward(args)... ); - } - - /// Ensures that the \p key exists in the list - /** - The operation performs inserting or changing data with lock-free manner. - - If the \p key not found in the list, then the new item created from \p key - is inserted into the list. Otherwise, the functor \p func is called with the item found. - The functor \p Func should be a function with signature: - \code - void func( bool bNew, value_type& item, const Q& val ); - \endcode - or a functor: - \code - struct my_functor { - void operator()( bool bNew, value_type& item, const Q& val ); - }; - \endcode - - with arguments: - - \p bNew - \p true if the item has been inserted, \p false otherwise - - \p item - item of the list - - \p val - argument \p key passed into the \p ensure function - - The functor may 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. - - You may pass \p func argument by reference using boost::ref. - - Returns std::pair where \p first is true if operation is successfull, - \p second is true if new item has been added or \p false if the item with \p key - already is in the list. - */ - template - std::pair ensure( Q const& key, Func f ) - { - return ensure_at( head(), key, f ); - } - - /// Deletes \p key from the list - /** \anchor cds_nonintrusive_LazyList_hp_erase_val - Since the key of LazyList's item type \p T is not explicitly specified, - template parameter \p Q defines the key type searching in the list. - The list item comparator should be able to compare the type \p T of list item - and the type \p Q. - - Return \p true if key is found and deleted, \p false otherwise - */ - template - bool erase( Q const& key ) - { - return erase_at( head(), key, intrusive_key_comparator(), [](value_type const&){} ); - } - - /// Deletes the item from the list using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_LazyList_hp_erase_val "erase(Q const&)" - but \p pred is used for key comparing. - \p Less functor has the interface like \p std::less. - \p pred must imply the same element order as the comparator used for building the list. - */ - template - bool erase_with( Q const& key, Less pred ) - { - return erase_at( head(), key, typename options::template less_wrapper::type(), [](value_type const&){} ); - } - - /// Deletes \p key from the list - /** \anchor cds_nonintrusive_LazyList_hp_erase_func - The function searches an item with key \p key, calls \p f functor with item found - and deletes the item. If \p key is not found, the functor is not called. - - The functor \p Func interface: - \code - struct extractor { - void operator()(const value_type& val) { ... } - }; - \endcode - The functor may be passed by reference with boost:ref - - Since the key of LazyList's item type \p T is not explicitly specified, - template parameter \p Q defines the key type searching in the list. - The list item comparator should be able to compare the type \p T of list item - and the type \p Q. - - Return \p true if key is found and deleted, \p false otherwise - - See also: \ref erase - */ - template - bool erase( Q const& key, Func f ) - { - return erase_at( head(), key, intrusive_key_comparator(), f ); - } - - /// Deletes the item from the list using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_LazyList_hp_erase_func "erase(Q const&, Func)" - but \p pred is used for key comparing. - \p Less functor has the interface like \p std::less. - \p pred must imply the same element order as the comparator used for building the list. - */ - template - bool erase_with( Q const& key, Less pred, Func f ) - { - return erase_at( head(), key, typename options::template less_wrapper::type(), f ); - } - - /// Extracts the item from the list with specified \p key - /** \anchor cds_nonintrusive_LazyList_hp_extract - The function searches an item with key equal to \p key, - unlinks it from the list, and returns it in \p dest parameter. - If the item with key equal to \p key is not found the function returns \p false. - - Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type. - - @note Each \p guarded_ptr object uses the GC's guard that can be limited resource. - - Usage: - \code - typedef cds::container::LazyList< cds::gc::HP, foo, my_traits > ord_list; - ord_list theList; - // ... - { - ord_list::guarded_ptr gp; - theList.extract( gp, 5 ); - // Deal with gp - // ... - - // Destructor of gp releases internal HP guard and frees the item - } - \endcode - */ - template - bool extract( guarded_ptr& dest, Q const& key ) - { - return extract_at( head(), dest.guard(), key, intrusive_key_comparator() ); - } - - /// Extracts the item from the list with comparing functor \p pred - /** - The function is an analog of \ref cds_nonintrusive_LazyList_hp_extract "extract(guarded_ptr&, Q const&)" - but \p pred predicate is used for key comparing. - - \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q - in any order. - \p pred must imply the same element order as the comparator used for building the list. - */ - template - bool extract_with( guarded_ptr& dest, Q const& key, Less pred ) - { - return extract_at( head(), dest.guard(), key, typename options::template less_wrapper::type() ); - } - - /// Finds the key \p key - /** \anchor cds_nonintrusive_LazyList_hp_find_val - 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 find( Q const& key ) - { - return find_at( head(), key, intrusive_key_comparator() ); - } - - /// Finds the key \p val using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_LazyList_hp_find_val "find(Q const&)" - but \p pred is used for key comparing. - \p Less functor has the interface like \p std::less. - \p pred must imply the same element order as the comparator used for building the list. - */ - template - bool find_with( Q const& key, Less pred ) - { - return find_at( head(), key, typename options::template less_wrapper::type() ); - } - - /// Finds the key \p val and performs an action with it - /** \anchor cds_nonintrusive_LazyList_hp_find_func - The function searches an item with key equal to \p val and calls the functor \p f for the item found. - The interface of \p Func functor is: - \code - struct functor { - void operator()( value_type& item, Q& val ); - }; - \endcode - where \p item is the item found, \p val is the find function argument. - - You may pass \p f argument by reference using boost::ref or cds::ref. - - The functor may change non-key fields of \p item. Note that the function is only guarantee - that \p item cannot be deleted during functor is executing. - The function does not serialize simultaneous access to the list \p item. If such access is - possible you must provide your own synchronization schema to exclude unsafe item modifications. - - The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor - may modify both arguments. - - The function returns \p true if \p val is found, \p false otherwise. - */ - template - bool find( Q& val, Func f ) - { - return find_at( head(), val, intrusive_key_comparator(), f ); - } - - /// Finds the key \p val using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_LazyList_hp_find_func "find(Q&, Func)" - but \p pred is used for key comparing. - \p Less functor has the interface like \p std::less. - \p pred must imply the same element order as the comparator used for building the list. - */ - template - bool find_with( Q& val, Less pred, Func f ) - { - return find_at( head(), val, typename options::template less_wrapper::type(), f ); - } - - /// Finds the key \p val and performs an action with it - /** \anchor cds_nonintrusive_LazyList_hp_find_cfunc - The function searches an item with key equal to \p val and calls the functor \p f for the item found. - The interface of \p Func functor is: - \code - struct functor { - void operator()( value_type& item, Q const& val ); - }; - \endcode - where \p item is the item found, \p val is the find function argument. - - You may pass \p f argument by reference using boost::ref or cds::ref. - - The function does not serialize simultaneous access to the list \p item. If such access is - possible you must provide your own synchronization schema to exclude unsafe item modifications. - - The function returns \p true if \p val is found, \p false otherwise. - */ - template - bool find( Q const& val, Func f ) - { - return find_at( head(), val, intrusive_key_comparator(), f ); - } - - /// Finds the key \p val using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_LazyList_hp_find_cfunc "find(Q&, Func)" - but \p pred is used for key comparing. - \p Less functor has the interface like \p std::less. - \p pred must imply the same element order as the comparator used for building the list. - */ - template - bool find_with( Q const& val, Less pred, Func f ) - { - return find_at( head(), val, typename options::template less_wrapper::type(), f ); - } - - /// Finds the key \p val and return the item found - /** \anchor cds_nonintrusive_LazyList_hp_get - The function searches the item with key equal to \p val - and assigns the item found to guarded pointer \p ptr. - The function returns \p true if \p val is found, and \p false otherwise. - If \p val is not found the \p ptr parameter is not changed. - - @note Each \p guarded_ptr object uses one GC's guard which can be limited resource. - - Usage: - \code - typedef cds::container::LazyList< cds::gc::HP, foo, my_traits > ord_list; - ord_list theList; - // ... - { - ord_list::guarded_ptr gp; - if ( theList.get( gp, 5 )) { - // Deal with gp - //... - } - // Destructor of guarded_ptr releases internal HP guard and frees the item - } - \endcode - - Note the compare functor specified for class \p Traits template parameter - should accept a parameter of type \p Q that can be not the same as \p value_type. - */ - template - bool get( guarded_ptr& ptr, Q const& val ) - { - return get_at( head(), ptr.guard(), val, intrusive_key_comparator() ); - } - - /// Finds the key \p val and return the item found - /** - The function is an analog of \ref cds_nonintrusive_LazyList_hp_get "get( guarded_ptr& ptr, 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 \ref value_type and \p Q - in any order. - \p pred must imply the same element order as the comparator used for building the list. - */ - template - bool get_with( guarded_ptr& ptr, Q const& val, Less pred ) - { - return get_at( head(), ptr.guard(), val, typename options::template less_wrapper::type() ); - } - - /// Checks if the list is empty - bool empty() const - { - return base_class::empty(); - } - - /// Returns list's item count - /** - The value returned depends on opt::item_counter option. For atomicity::empty_item_counter, - this function always returns 0. - - Warning: even if you use real item counter and it returns 0, this fact is not mean that the list - is empty. To check list emptyness use \ref empty() method. - */ - size_t size() const - { - return base_class::size(); - } - - /// Clears the list - /** - Post-condition: the list is empty - */ - void clear() - { - base_class::clear(); - } - - protected: - //@cond - bool insert_node_at( head_type& refHead, node_type * pNode ) - { - assert( pNode != nullptr ); - scoped_node_ptr p( pNode ); - - if ( base_class::insert_at( &refHead, *pNode )) { - p.release(); - return true; - } - - return false; - } - - template - bool insert_at( head_type& refHead, const Q& val ) - { - return insert_node_at( refHead, alloc_node( val )); - } - - template - bool emplace_at( head_type& refHead, Args&&... args ) - { - return insert_node_at( refHead, alloc_node( std::forward(args)... )); - } - - template - bool insert_at( head_type& refHead, const Q& key, Func f ) - { - scoped_node_ptr pNode( alloc_node( key )); - - if ( base_class::insert_at( &refHead, *pNode, [&f](node_type& node){ cds::unref(f)( node_to_value(node) ); } )) { - pNode.release(); - return true; - } - return false; - } - - template - bool erase_at( head_type& refHead, const Q& key, Compare cmp, Func f ) - { - return base_class::erase_at( &refHead, key, cmp, [&f](node_type const& node){ cds::unref(f)( node_to_value(node) ); } ); - } - - template - bool extract_at( head_type& refHead, typename gc::Guard& dest, Q const& key, Compare cmp ) - { - return base_class::extract_at( &refHead, dest, key, cmp ); - } - - template - std::pair ensure_at( head_type& refHead, const Q& key, Func f ) - { - scoped_node_ptr pNode( alloc_node( key )); - - std::pair ret = base_class::ensure_at( &refHead, *pNode, - [&f, &key](bool bNew, node_type& node, node_type&){cds::unref(f)( bNew, node_to_value(node), key ); }); - if ( ret.first && ret.second ) - pNode.release(); - - return ret; - } - - template - bool find_at( head_type& refHead, Q const& key, Compare cmp ) - { - return base_class::find_at( &refHead, key, cmp ); - } - - template - bool find_at( head_type& refHead, Q& val, Compare cmp, Func f ) - { - return base_class::find_at( &refHead, val, cmp, [&f](node_type& node, Q& val){ cds::unref(f)( node_to_value(node), val ); }); - } - - template - bool get_at( head_type& refHead, typename gc::Guard& guard, Q const& key, Compare cmp ) - { - return base_class::get_at( &refHead, guard, key, cmp ); - } - - //@endcond - }; - -}} // namespace cds::container - -#endif // #ifndef __CDS_CONTAINER_LAZY_LIST_IMPL_H diff --git a/cds/container/lazy_list_ptb.h b/cds/container/lazy_list_ptb.h index be535fe2..8bc2750a 100644 --- a/cds/container/lazy_list_ptb.h +++ b/cds/container/lazy_list_ptb.h @@ -6,6 +6,6 @@ #include #include #include -#include +#include #endif // #ifndef __CDS_CONTAINER_LAZY_LIST_PTB_H diff --git a/projects/Win/vc12/cds.vcxproj b/projects/Win/vc12/cds.vcxproj index 6379e67d..dffb311b 100644 --- a/projects/Win/vc12/cds.vcxproj +++ b/projects/Win/vc12/cds.vcxproj @@ -672,6 +672,7 @@ + @@ -958,7 +959,6 @@ - diff --git a/projects/Win/vc12/cds.vcxproj.filters b/projects/Win/vc12/cds.vcxproj.filters index d53ecdbd..82486eb2 100644 --- a/projects/Win/vc12/cds.vcxproj.filters +++ b/projects/Win/vc12/cds.vcxproj.filters @@ -641,9 +641,6 @@ Header Files\cds\container - - Header Files\cds\container - Header Files\cds\container @@ -1283,5 +1280,8 @@ Header Files\cds\container\details + + Header Files\cds\container\impl + \ No newline at end of file