From: khizmax Date: Sat, 27 Sep 2014 16:12:32 +0000 (+0400) Subject: Rename cds/container/lazy_kvlist_impl.h to cds/container/impl/lazy_kvlist.h X-Git-Tag: v2.0.0~267 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=e33422e2b0645f3449639e2fa580a784ab3d1eb7;p=libcds.git Rename cds/container/lazy_kvlist_impl.h to cds/container/impl/lazy_kvlist.h --- diff --git a/cds/container/impl/lazy_kvlist.h b/cds/container/impl/lazy_kvlist.h new file mode 100644 index 00000000..f4265bde --- /dev/null +++ b/cds/container/impl/lazy_kvlist.h @@ -0,0 +1,830 @@ +//$$CDS-header$$ + +#ifndef __CDS_CONTAINER_IMPL_LAZY_KVLIST_H +#define __CDS_CONTAINER_IMPL_LAZY_KVLIST_H + +#include +#include +#include +#include + +namespace cds { namespace container { + + /// Lazy ordered list (key-value pair) + /** @ingroup cds_nonintrusive_list + \anchor cds_nonintrusive_LazyKVList_gc + + This is key-value variation of non-intrusive LazyList. + Like standard container, this implementation split a value stored into two part - + constant key and alterable value. + + Usually, ordered single-linked list is used as a building block for the hash table implementation. + The complexity of searching is O(N). + + Template arguments: + - \p GC - garbage collector used + - \p Key - key type of an item stored in the list. It should be copy-constructible + - \p Value - value type stored in the list + - \p Traits - type traits, default is lazy_list::type_traits + + 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::LazyKVList< cds::gc::HP, int, 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::LazyKVList< cds::gc::HP, int, 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::compare - key comparison 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 comparison. 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 Key, + typename Value, +#ifdef CDS_DOXYGEN_INVOKED + typename Traits = lazy_list::type_traits +#else + typename Traits +#endif + > + class LazyKVList: +#ifdef CDS_DOXYGEN_INVOKED + protected intrusive::LazyList< GC, implementation_defined, Traits > +#else + protected details::make_lazy_kvlist< GC, Key, Value, Traits >::type +#endif + { + //@cond + typedef details::make_lazy_kvlist< GC, Key, Value, Traits > options; + typedef typename options::type base_class; + //@endcond + + public: +#ifdef CDS_DOXYGEN_INVOKED + typedef Key key_type ; ///< Key type + typedef Value mapped_type ; ///< Type of value stored in the list + typedef std::pair value_type ; ///< key/value pair stored in the list +#else + typedef typename options::key_type key_type; + typedef typename options::value_type mapped_type; + typedef typename options::pair_type value_type; +#endif + + 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_map > guarded_ptr; + + protected: + //@cond + template + static node_type * alloc_node(const K& key) + { + return cxx_allocator().New( key ); + } + + template + static node_type * alloc_node( const K& key, const V& val ) + { + return cxx_allocator().New( key, val ); + } + + 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 LazyKVList; + + public: + typedef typename cds::details::make_const_type::reference value_ref; + typedef typename cds::details::make_const_type::pointer value_ptr; + + typedef typename cds::details::make_const_type::reference pair_ref; + typedef typename cds::details::make_const_type::pointer pair_ptr; + + iterator_type() + {} + + iterator_type( iterator_type const& src ) + : iterator_base( src ) + {} + + key_type const& key() const + { + typename iterator_base::value_ptr p = iterator_base::operator ->(); + assert( p != nullptr ); + return p->m_Data.first; + } + + value_ref val() const + { + typename iterator_base::value_ptr p = iterator_base::operator ->(); + assert( p != nullptr ); + return p->m_Data.second; + } + + pair_ptr operator ->() const + { + typename iterator_base::value_ptr p = iterator_base::operator ->(); + return p ? &(p->m_Data) : nullptr; + } + + pair_ref operator *() const + { + typename iterator_base::value_ref p = iterator_base::operator *(); + return p.m_Data; + } + + /// 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. + + The iterator interface to access item data: + - operator -> - returns a pointer to \ref value_type for iterator + - operator * - returns a reference (a const reference for \p const_iterator) to \ref value_type for iterator + - const key_type& key() - returns a key reference for iterator + - mapped_type& val() - retuns a value reference for iterator (const reference for \p const_iterator) + + For both functions the iterator should not be equal to end() + */ + 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 + 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. + Internally, end returning value equals to \p nullptr. + + 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 + return it; + } + const_iterator cbegin() + { + const_iterator it( head() ); + ++it; // skip dummy head + 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 + */ + LazyKVList() + {} + + /// List destructor + /** + Clears the list + */ + ~LazyKVList() + { + clear(); + } + + /// Inserts new node with key and default value + /** + The function creates a node with \p key and default value, and then inserts the node created into the list. + + Preconditions: + - The \ref key_type should be constructible from value of type \p K. + In trivial case, \p K is equal to \ref key_type. + - The \ref mapped_type should be default-constructible. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert( const K& key ) + { + return insert_at( head(), key ); + } + + /// Inserts new node with a key and a value + /** + The function creates a node with \p key and value \p val, and then inserts the node created into the list. + + Preconditions: + - The \ref key_type should be constructible from \p key of type \p K. + - The \ref mapped_type should be constructible from \p val of type \p V. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert( const K& key, const V& val ) + { + // We cannot use insert with functor here + // because we cannot lock inserted node for updating + // Therefore, we use separate function + return insert_at( head(), key, val ); + } + + /// Inserts new node and initializes it by a functor + /** + This function inserts new node with key \p key and if inserting is successful then it calls + \p func functor with signature + \code + struct functor { + void operator()( value_type& item ); + }; + \endcode + + The argument \p item of user-defined functor \p func is the reference + to the list's item inserted. item.second is a reference to item's value that may be changed. + 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 inserting is successful. + + The key_type should be constructible from value of type \p K. + + The function allows to split creating of new item into two part: + - create item from \p key; + - insert new item into the list; + - if inserting is successful, initialize the value of item by calling \p func functor + + This can be useful if complete initialization of object of \p mapped_type is heavyweight and + it is preferable that the initialization should be completed only if inserting is successful. + */ + template + bool insert_key( const K& key, Func func ) + { + return insert_key_at( head(), key, func ); + } + + /// Inserts data of type \ref mapped_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 (note that in this case the \ref key_type should be + copy-constructible from type \p K). + Otherwise, the functor \p func is called with item found. + The functor \p Func may be a function with signature: + \code + void func( bool bNew, value_type& item ); + \endcode + or a functor: + \code + struct my_functor { + void operator()( bool bNew, value_type& item ); + }; + \endcode + + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - item of the list + + The functor may change any fields of the \p item.second that is \ref mapped_type; + 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( const K& key, Func f ) + { + return ensure_at( head(), key, f ); + } + + /// Deletes \p key from the list + /** \anchor cds_nonintrusive_LazyKVList_hp_erase_val + + Returns \p true if \p key is found and has been deleted, \p false otherwise + */ + template + bool erase( K const& key ) + { + return erase_at( head(), key, intrusive_key_comparator() ); + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_erase_val "erase(K 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( K const& key, Less pred ) + { + return erase_at( head(), key, typename options::template less_wrapper::type() ); + } + + /// Deletes \p key from the list + /** \anchor cds_nonintrusive_LazyKVList_hp_erase_func + The function searches an item with key \p key, calls \p f functor with item found + and deletes it. If \p key is not found, the functor is not called. + + The functor \p Func interface: + \code + struct extractor { + void operator()(value_type& val) { ... } + }; + \endcode + The functor may be passed by reference with boost:ref + + Returns \p true if key is found and deleted, \p false otherwise + */ + template + bool erase( K 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_LazyKVList_hp_erase_func "erase(K 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( K 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_LazyKVList_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 K that can be not the same as \p key_type. + + @note Each \p guarded_ptr object uses the GC's guard that can be limited resource. + + Usage: + \code + typedef cds::container::LazyKVList< cds::gc::HP, int, 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, K 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_LazyKVList_hp_extract "extract(guarded_ptr&, K 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 key_type and \p K + 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, K 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_LazyKVList_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_LazyKVList_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 key and performs an action with it + /** \anchor cds_nonintrusive_LazyKVList_hp_find_func + The function searches an item with key equal to \p key 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 ); + }; + \endcode + where \p item is the item found. + + You may pass \p f argument by reference using boost::ref or cds::ref. + + The functor may change item.second that is reference to value of node. + 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 function returns \p true if \p key is found, \p false otherwise. + */ + template + bool find( Q const& key, Func f ) + { + return find_at( head(), key, intrusive_key_comparator(), f ); + } + + /// Finds the key \p val using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_LazyKVList_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 const& key, Less pred, Func f ) + { + return find_at( head(), key, typename options::template less_wrapper::type(), f ); + } + + /// Finds \p key and return the item found + /** \anchor cds_nonintrusive_LazyKVList_hp_get + The function searches the item with key equal to \p key + and assigns the item found to guarded pointer \p ptr. + The function returns \p true if \p key is found, and \p false otherwise. + If \p key 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::LazyKVList< cds::gc::HP, int, 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 K that can be not the same as \p key_type. + */ + template + bool get( guarded_ptr& ptr, K const& key ) + { + return get_at( head(), ptr.guard(), key, intrusive_key_comparator() ); + } + + /// Finds the key \p val and return the item found + /** + The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_get "get(guarded_ptr& ptr, K 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 key_type and \p K + 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, K const& key, Less pred ) + { + return get_at( head(), ptr.guard(), key, 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, *p )) { + p.release(); + return true; + } + + return false; + } + + template + bool insert_at( head_type& refHead, const K& key ) + { + return insert_node_at( refHead, alloc_node( key )); + } + + template + bool insert_at( head_type& refHead, const K& key, const V& val ) + { + return insert_node_at( refHead, alloc_node( key, val )); + } + + template + bool insert_key_at( head_type& refHead, const K& 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.m_Data ); } )) { + pNode.release(); + return true; + } + return false; + } + + template + bool emplace_at( head_type& refHead, Args&&... args ) + { + return insert_node_at( refHead, alloc_node( std::forward(args)... )); + } + + template + bool erase_at( head_type& refHead, K const& key, Compare cmp ) + { + return base_class::erase_at( &refHead, key, cmp ); + } + + template + bool erase_at( head_type& refHead, K const& key, Compare cmp, Func f ) + { + return base_class::erase_at( &refHead, key, cmp, [&f](node_type const & node){cds::unref(f)( const_cast(node.m_Data)); }); + } + + template + bool extract_at( head_type& refHead, typename gc::Guard& dest, K const& key, Compare cmp ) + { + return base_class::extract_at( &refHead, dest, key, cmp ); + } + + template + std::pair ensure_at( head_type& refHead, const K& key, Func f ) + { + scoped_node_ptr pNode( alloc_node( key )); + + std::pair ret = base_class::ensure_at( &refHead, *pNode, + [&f]( bool bNew, node_type& node, node_type& ){ cds::unref(f)( bNew, node.m_Data ); }); + if ( ret.first && ret.second ) + pNode.release(); + + return ret; + } + + template + bool find_at( head_type& refHead, K const& key, Compare cmp ) + { + return base_class::find_at( &refHead, key, cmp ); + } + + template + bool find_at( head_type& refHead, K& key, Compare cmp, Func f ) + { + return base_class::find_at( &refHead, key, cmp, [&f]( node_type& node, K& ){ cds::unref(f)( node.m_Data ); }); + } + + template + bool get_at( head_type& refHead, typename gc::Guard& guard, K const& key, Compare cmp ) + { + return base_class::get_at( &refHead, guard, key, cmp ); + } + + //@endcond + }; + +}} // namespace cds::container + +#endif // #ifndef __CDS_CONTAINER_IMPL_LAZY_KVLIST_H diff --git a/cds/container/lazy_kvlist_hp.h b/cds/container/lazy_kvlist_hp.h index 4e6a76ff..d29b3c9d 100644 --- a/cds/container/lazy_kvlist_hp.h +++ b/cds/container/lazy_kvlist_hp.h @@ -6,6 +6,6 @@ #include #include #include -#include +#include #endif // #ifndef __CDS_CONTAINER_LAZY_KVLIST_HP_H diff --git a/cds/container/lazy_kvlist_hrc.h b/cds/container/lazy_kvlist_hrc.h index 22a9d828..d7a49a51 100644 --- a/cds/container/lazy_kvlist_hrc.h +++ b/cds/container/lazy_kvlist_hrc.h @@ -6,6 +6,6 @@ #include #include #include -#include +#include #endif // #ifndef __CDS_CONTAINER_LAZY_KVLIST_HRC_H diff --git a/cds/container/lazy_kvlist_impl.h b/cds/container/lazy_kvlist_impl.h deleted file mode 100644 index 30b8b9b3..00000000 --- a/cds/container/lazy_kvlist_impl.h +++ /dev/null @@ -1,830 +0,0 @@ -//$$CDS-header$$ - -#ifndef __CDS_CONTAINER_LAZY_KVLIST_IMPL_H -#define __CDS_CONTAINER_LAZY_KVLIST_IMPL_H - -#include -#include -#include -#include - -namespace cds { namespace container { - - /// Lazy ordered list (key-value pair) - /** @ingroup cds_nonintrusive_list - \anchor cds_nonintrusive_LazyKVList_gc - - This is key-value variation of non-intrusive LazyList. - Like standard container, this implementation split a value stored into two part - - constant key and alterable value. - - Usually, ordered single-linked list is used as a building block for the hash table implementation. - The complexity of searching is O(N). - - Template arguments: - - \p GC - garbage collector used - - \p Key - key type of an item stored in the list. It should be copy-constructible - - \p Value - value type stored in the list - - \p Traits - type traits, default is lazy_list::type_traits - - 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::LazyKVList< cds::gc::HP, int, 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::LazyKVList< cds::gc::HP, int, 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::compare - key comparison 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 comparison. 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 Key, - typename Value, -#ifdef CDS_DOXYGEN_INVOKED - typename Traits = lazy_list::type_traits -#else - typename Traits -#endif - > - class LazyKVList: -#ifdef CDS_DOXYGEN_INVOKED - protected intrusive::LazyList< GC, implementation_defined, Traits > -#else - protected details::make_lazy_kvlist< GC, Key, Value, Traits >::type -#endif - { - //@cond - typedef details::make_lazy_kvlist< GC, Key, Value, Traits > options; - typedef typename options::type base_class; - //@endcond - - public: -#ifdef CDS_DOXYGEN_INVOKED - typedef Key key_type ; ///< Key type - typedef Value mapped_type ; ///< Type of value stored in the list - typedef std::pair value_type ; ///< key/value pair stored in the list -#else - typedef typename options::key_type key_type; - typedef typename options::value_type mapped_type; - typedef typename options::pair_type value_type; -#endif - - 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_map > guarded_ptr; - - protected: - //@cond - template - static node_type * alloc_node(const K& key) - { - return cxx_allocator().New( key ); - } - - template - static node_type * alloc_node( const K& key, const V& val ) - { - return cxx_allocator().New( key, val ); - } - - 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 LazyKVList; - - public: - typedef typename cds::details::make_const_type::reference value_ref; - typedef typename cds::details::make_const_type::pointer value_ptr; - - typedef typename cds::details::make_const_type::reference pair_ref; - typedef typename cds::details::make_const_type::pointer pair_ptr; - - iterator_type() - {} - - iterator_type( iterator_type const& src ) - : iterator_base( src ) - {} - - key_type const& key() const - { - typename iterator_base::value_ptr p = iterator_base::operator ->(); - assert( p != nullptr ); - return p->m_Data.first; - } - - value_ref val() const - { - typename iterator_base::value_ptr p = iterator_base::operator ->(); - assert( p != nullptr ); - return p->m_Data.second; - } - - pair_ptr operator ->() const - { - typename iterator_base::value_ptr p = iterator_base::operator ->(); - return p ? &(p->m_Data) : nullptr; - } - - pair_ref operator *() const - { - typename iterator_base::value_ref p = iterator_base::operator *(); - return p.m_Data; - } - - /// 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. - - The iterator interface to access item data: - - operator -> - returns a pointer to \ref value_type for iterator - - operator * - returns a reference (a const reference for \p const_iterator) to \ref value_type for iterator - - const key_type& key() - returns a key reference for iterator - - mapped_type& val() - retuns a value reference for iterator (const reference for \p const_iterator) - - For both functions the iterator should not be equal to end() - */ - 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 - 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. - Internally, end returning value equals to \p nullptr. - - 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 - return it; - } - const_iterator cbegin() - { - const_iterator it( head() ); - ++it; // skip dummy head - 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 - */ - LazyKVList() - {} - - /// List destructor - /** - Clears the list - */ - ~LazyKVList() - { - clear(); - } - - /// Inserts new node with key and default value - /** - The function creates a node with \p key and default value, and then inserts the node created into the list. - - Preconditions: - - The \ref key_type should be constructible from value of type \p K. - In trivial case, \p K is equal to \ref key_type. - - The \ref mapped_type should be default-constructible. - - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool insert( const K& key ) - { - return insert_at( head(), key ); - } - - /// Inserts new node with a key and a value - /** - The function creates a node with \p key and value \p val, and then inserts the node created into the list. - - Preconditions: - - The \ref key_type should be constructible from \p key of type \p K. - - The \ref mapped_type should be constructible from \p val of type \p V. - - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool insert( const K& key, const V& val ) - { - // We cannot use insert with functor here - // because we cannot lock inserted node for updating - // Therefore, we use separate function - return insert_at( head(), key, val ); - } - - /// Inserts new node and initializes it by a functor - /** - This function inserts new node with key \p key and if inserting is successful then it calls - \p func functor with signature - \code - struct functor { - void operator()( value_type& item ); - }; - \endcode - - The argument \p item of user-defined functor \p func is the reference - to the list's item inserted. item.second is a reference to item's value that may be changed. - 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 inserting is successful. - - The key_type should be constructible from value of type \p K. - - The function allows to split creating of new item into two part: - - create item from \p key; - - insert new item into the list; - - if inserting is successful, initialize the value of item by calling \p func functor - - This can be useful if complete initialization of object of \p mapped_type is heavyweight and - it is preferable that the initialization should be completed only if inserting is successful. - */ - template - bool insert_key( const K& key, Func func ) - { - return insert_key_at( head(), key, func ); - } - - /// Inserts data of type \ref mapped_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 (note that in this case the \ref key_type should be - copy-constructible from type \p K). - Otherwise, the functor \p func is called with item found. - The functor \p Func may be a function with signature: - \code - void func( bool bNew, value_type& item ); - \endcode - or a functor: - \code - struct my_functor { - void operator()( bool bNew, value_type& item ); - }; - \endcode - - with arguments: - - \p bNew - \p true if the item has been inserted, \p false otherwise - - \p item - item of the list - - The functor may change any fields of the \p item.second that is \ref mapped_type; - 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( const K& key, Func f ) - { - return ensure_at( head(), key, f ); - } - - /// Deletes \p key from the list - /** \anchor cds_nonintrusive_LazyKVList_hp_erase_val - - Returns \p true if \p key is found and has been deleted, \p false otherwise - */ - template - bool erase( K const& key ) - { - return erase_at( head(), key, intrusive_key_comparator() ); - } - - /// Deletes the item from the list using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_erase_val "erase(K 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( K const& key, Less pred ) - { - return erase_at( head(), key, typename options::template less_wrapper::type() ); - } - - /// Deletes \p key from the list - /** \anchor cds_nonintrusive_LazyKVList_hp_erase_func - The function searches an item with key \p key, calls \p f functor with item found - and deletes it. If \p key is not found, the functor is not called. - - The functor \p Func interface: - \code - struct extractor { - void operator()(value_type& val) { ... } - }; - \endcode - The functor may be passed by reference with boost:ref - - Returns \p true if key is found and deleted, \p false otherwise - */ - template - bool erase( K 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_LazyKVList_hp_erase_func "erase(K 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( K 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_LazyKVList_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 K that can be not the same as \p key_type. - - @note Each \p guarded_ptr object uses the GC's guard that can be limited resource. - - Usage: - \code - typedef cds::container::LazyKVList< cds::gc::HP, int, 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, K 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_LazyKVList_hp_extract "extract(guarded_ptr&, K 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 key_type and \p K - 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, K 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_LazyKVList_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_LazyKVList_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 key and performs an action with it - /** \anchor cds_nonintrusive_LazyKVList_hp_find_func - The function searches an item with key equal to \p key 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 ); - }; - \endcode - where \p item is the item found. - - You may pass \p f argument by reference using boost::ref or cds::ref. - - The functor may change item.second that is reference to value of node. - 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 function returns \p true if \p key is found, \p false otherwise. - */ - template - bool find( Q const& key, Func f ) - { - return find_at( head(), key, intrusive_key_comparator(), f ); - } - - /// Finds the key \p val using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_LazyKVList_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 const& key, Less pred, Func f ) - { - return find_at( head(), key, typename options::template less_wrapper::type(), f ); - } - - /// Finds \p key and return the item found - /** \anchor cds_nonintrusive_LazyKVList_hp_get - The function searches the item with key equal to \p key - and assigns the item found to guarded pointer \p ptr. - The function returns \p true if \p key is found, and \p false otherwise. - If \p key 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::LazyKVList< cds::gc::HP, int, 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 K that can be not the same as \p key_type. - */ - template - bool get( guarded_ptr& ptr, K const& key ) - { - return get_at( head(), ptr.guard(), key, intrusive_key_comparator() ); - } - - /// Finds the key \p val and return the item found - /** - The function is an analog of \ref cds_nonintrusive_LazyKVList_hp_get "get(guarded_ptr& ptr, K 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 key_type and \p K - 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, K const& key, Less pred ) - { - return get_at( head(), ptr.guard(), key, 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, *p )) { - p.release(); - return true; - } - - return false; - } - - template - bool insert_at( head_type& refHead, const K& key ) - { - return insert_node_at( refHead, alloc_node( key )); - } - - template - bool insert_at( head_type& refHead, const K& key, const V& val ) - { - return insert_node_at( refHead, alloc_node( key, val )); - } - - template - bool insert_key_at( head_type& refHead, const K& 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.m_Data ); } )) { - pNode.release(); - return true; - } - return false; - } - - template - bool emplace_at( head_type& refHead, Args&&... args ) - { - return insert_node_at( refHead, alloc_node( std::forward(args)... )); - } - - template - bool erase_at( head_type& refHead, K const& key, Compare cmp ) - { - return base_class::erase_at( &refHead, key, cmp ); - } - - template - bool erase_at( head_type& refHead, K const& key, Compare cmp, Func f ) - { - return base_class::erase_at( &refHead, key, cmp, [&f](node_type const & node){cds::unref(f)( const_cast(node.m_Data)); }); - } - - template - bool extract_at( head_type& refHead, typename gc::Guard& dest, K const& key, Compare cmp ) - { - return base_class::extract_at( &refHead, dest, key, cmp ); - } - - template - std::pair ensure_at( head_type& refHead, const K& key, Func f ) - { - scoped_node_ptr pNode( alloc_node( key )); - - std::pair ret = base_class::ensure_at( &refHead, *pNode, - [&f]( bool bNew, node_type& node, node_type& ){ cds::unref(f)( bNew, node.m_Data ); }); - if ( ret.first && ret.second ) - pNode.release(); - - return ret; - } - - template - bool find_at( head_type& refHead, K const& key, Compare cmp ) - { - return base_class::find_at( &refHead, key, cmp ); - } - - template - bool find_at( head_type& refHead, K& key, Compare cmp, Func f ) - { - return base_class::find_at( &refHead, key, cmp, [&f]( node_type& node, K& ){ cds::unref(f)( node.m_Data ); }); - } - - template - bool get_at( head_type& refHead, typename gc::Guard& guard, K const& key, Compare cmp ) - { - return base_class::get_at( &refHead, guard, key, cmp ); - } - - //@endcond - }; - -}} // namespace cds::container - -#endif // #ifndef __CDS_CONTAINER_LAZY_KVLIST_IMPL_H diff --git a/cds/container/lazy_kvlist_ptb.h b/cds/container/lazy_kvlist_ptb.h index cdfd1c19..6fe26378 100644 --- a/cds/container/lazy_kvlist_ptb.h +++ b/cds/container/lazy_kvlist_ptb.h @@ -6,6 +6,6 @@ #include #include #include -#include +#include #endif // #ifndef __CDS_CONTAINER_LAZY_KVLIST_PTB_H diff --git a/projects/Win/vc12/cds.vcxproj b/projects/Win/vc12/cds.vcxproj index 27070c96..10a917ec 100644 --- a/projects/Win/vc12/cds.vcxproj +++ b/projects/Win/vc12/cds.vcxproj @@ -670,6 +670,7 @@ + @@ -952,7 +953,6 @@ - diff --git a/projects/Win/vc12/cds.vcxproj.filters b/projects/Win/vc12/cds.vcxproj.filters index 915f4568..27a9dcca 100644 --- a/projects/Win/vc12/cds.vcxproj.filters +++ b/projects/Win/vc12/cds.vcxproj.filters @@ -629,9 +629,6 @@ Header Files\cds\container - - Header Files\cds\container - Header Files\cds\container @@ -1283,5 +1280,8 @@ Header Files\cds\container\impl + + Header Files\cds\container\impl + \ No newline at end of file