container::SplitListSet refactoring
[libcds.git] / cds / container / split_list_set_rcu.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_CONTAINER_SPLIT_LIST_SET_RCU_H
4 #define __CDS_CONTAINER_SPLIT_LIST_SET_RCU_H
5
6 #include <cds/intrusive/split_list_rcu.h>
7 #include <cds/container/details/make_split_list_set.h>
8
9 namespace cds { namespace container {
10
11     /// Split-ordered list set (template specialization for \ref cds_urcu_desc "RCU")
12     /** @ingroup cds_nonintrusive_set
13         \anchor cds_nonintrusive_SplitListSet_rcu
14
15         Hash table implementation based on split-ordered list algorithm discovered by Ori Shalev and Nir Shavit, see
16         - [2003] Ori Shalev, Nir Shavit "Split-Ordered Lists - Lock-free Resizable Hash Tables"
17         - [2008] Nir Shavit "The Art of Multiprocessor Programming"
18
19         See \p intrusive::SplitListSet for a brief description of the split-list algorithm.
20
21         Template parameters:
22         - \p RCU - one of \ref cds_urcu_gc "RCU type"
23         - \p T - type of the value to be stored in the split-list.
24         - \p Traits - type traits, default is \p split_list::traits. Instead of declaring \p split_list::traits -based
25             struct you can apply option-based notation with \p split_list::make_traits metafunction.
26
27         <b>Iterators</b>
28
29         The class supports a forward iterator (\ref iterator and \ref const_iterator).
30         The iteration is unordered.
31
32         You may iterate over split-list set items only under RCU lock.
33         Only in this case the iterator is thread-safe since
34         while RCU is locked any set's item cannot be reclaimed.
35
36         @warning The iterator object cannot be passed between threads
37
38         \warning Due to concurrent nature of skip-list set it is not guarantee that you can iterate
39         all elements in the set: any concurrent deletion can exclude the element
40         pointed by the iterator from the set, and your iteration can be terminated
41         before end of the set. Therefore, such iteration is more suitable for debugging purposes
42
43         The iterator class supports the following minimalistic interface:
44         \code
45         struct iterator {
46             // Default ctor
47             iterator();
48
49             // Copy ctor
50             iterator( iterator const& s);
51
52             value_type * operator ->() const;
53             value_type& operator *() const;
54
55             // Pre-increment
56             iterator& operator ++();
57
58             // Copy assignment
59             iterator& operator = (const iterator& src);
60
61             bool operator ==(iterator const& i ) const;
62             bool operator !=(iterator const& i ) const;
63         };
64         \endcode
65         Note, the iterator object returned by \p end(), \p cend() member functions points to \p nullptr and should not be dereferenced.
66
67         \par Usage
68
69         You should decide what garbage collector you want, and what ordered list you want to use. Split-ordered list
70         is an original data structure based on an ordered list. Suppose, you want construct split-list set based on \p cds::urcu::general_buffered<> GC
71         and \p LazyList as ordered list implementation. So, you beginning your program with following include:
72         \code
73         #include <cds/urcu/general_buffered.h>
74         #include <cds/container/lazy_list_rcu.h>
75         #include <cds/container/split_list_set_rcu.h>
76
77         namespace cc = cds::container;
78
79         // The data belonged to split-ordered list
80         sturuct foo {
81             int     nKey;   // key field
82             std::string strValue    ;   // value field
83         };
84         \endcode
85         The inclusion order is important:
86         - first, include one of \ref cds_urcu_gc "RCU implementation" (<tt>cds/urcu/general_buffered.h</tt> in our case)
87         - second, include file for ordered-list implementation (for this example, <tt>cds/container/lazy_list_rcu.h</tt>),
88         - then, the header for RCU-based split-list set <tt>cds/container/split_list_set_rcu.h</tt>.
89
90         Now, you should declare traits for split-list set. The main parts of traits are a hash functor for the set and a comparing functor for ordered list.
91         Note that we define several function in \p foo_hash and \p foo_less functors for different argument types since we want call our \p %SplitListSet
92         object by the key of type \p int and by the value of type \p foo.
93
94         The second attention: instead of using \p %LazyList in \p %SplitListSet traits we use \p cds::contaner::lazy_list_tag tag for the lazy list.
95         The split-list requires significant support from underlying ordered list class and it is not good idea to dive you
96         into deep implementation details of split-list and ordered list interrelations. The tag paradigm simplifies split-list interface.
97
98         \code
99         // foo hash functor
100         struct foo_hash {
101             size_t operator()( int key ) const { return std::hash( key ) ; }
102             size_t operator()( foo const& item ) const { return std::hash( item.nKey ) ; }
103         };
104
105         // foo comparator
106         struct foo_less {
107             bool operator()(int i, foo const& f ) const { return i < f.nKey ; }
108             bool operator()(foo const& f, int i ) const { return f.nKey < i ; }
109             bool operator()(foo const& f1, foo const& f2) const { return f1.nKey < f2.nKey; }
110         };
111
112         // SplitListSet traits
113         struct foo_set_traits: public cc::split_list::traits
114         {
115             typedef cc::lazy_list_tag   ordered_list    ;   // what type of ordered list we want to use
116             typedef foo_hash            hash            ;   // hash functor for our data stored in split-list set
117
118             // Type traits for our LazyList class
119             struct ordered_list_traits: public cc::lazy_list::traits
120             {
121                 typedef foo_less less   ;   // use our foo_less as comparator to order list nodes
122             };
123         };
124         \endcode
125
126         Now you are ready to declare our set class based on \p %SplitListSet:
127         \code
128         typedef cc::SplitListSet< cds::urcu::gc<cds::urcu::general_buffered<> >, foo, foo_set_traits > foo_set;
129         \endcode
130
131         You may use the modern option-based declaration instead of classic type-traits-based one:
132         \code
133         typedef cc:SplitListSet<
134             cds::urcu::gc<cds::urcu::general_buffered<> >   // RCU type used
135             ,foo                                            // type of data stored
136             ,cc::split_list::make_traits<      // metafunction to build split-list traits
137                 cc::split_list::ordered_list<cc::lazy_list_tag>     // tag for underlying ordered list implementation
138                 ,cc::opt::hash< foo_hash >              // hash functor
139                 ,cc::split_list::ordered_list_traits<   // ordered list traits 
140                     cc::lazy_list::make_traits<         // metafunction to build lazy list traits
141                         cc::opt::less< foo_less >       // less-based compare functor
142                     >::type
143                 >
144             >::type
145         >  foo_set;
146         \endcode
147         In case of option-based declaration using \p split_list::make_traits metafunction
148         the struct \p foo_set_traits is not required.
149
150         Now, the set of type \p foo_set is ready to use in your program.
151
152         Note that in this example we show only mandatory \p traits parts, optional ones is the default and they are inherited
153         from \p container::split_list::traits.
154         There are many other options for deep tuning of the split-list and ordered-list containers.
155     */
156     template <
157         class RCU,
158         class T,
159 #ifdef CDS_DOXYGEN_INVOKED
160         class Traits = split_list::traits
161 #else
162         class Traits
163 #endif
164     >
165     class SplitListSet< cds::urcu::gc< RCU >, T, Traits >:
166 #ifdef CDS_DOXYGEN_INVOKED
167         protected intrusive::SplitListSet< cds::urcu::gc< RCU >, typename Traits::ordered_list, Traits >
168 #else
169         protected details::make_split_list_set< cds::urcu::gc< RCU >, T, typename Traits::ordered_list, split_list::details::wrap_set_traits<T, Traits> >::type
170 #endif
171     {
172     protected:
173         //@cond
174         typedef details::make_split_list_set< cds::urcu::gc< RCU >, T, typename Traits::ordered_list, split_list::details::wrap_set_traits<T, Traits> > maker;
175         typedef typename maker::type  base_class;
176         //@endcond
177
178     public:
179         typedef cds::urcu::gc< RCU >  gc; ///< RCU-based garbage collector
180         typedef T       value_type; ///< Type of value to be storedin the set
181         typedef Traits  traits;    ///< \p Traits template argument
182
183         typedef typename maker::ordered_list        ordered_list;   ///< Underlying ordered list class
184         typedef typename base_class::key_comparator key_comparator; ///< key compare functor
185
186         /// Hash functor for \ref value_type and all its derivatives that you use
187         typedef typename base_class::hash           hash;
188         typedef typename base_class::item_counter   item_counter; ///< Item counter type
189
190         typedef typename base_class::rcu_lock      rcu_lock   ; ///< RCU scoped lock
191         /// Group of \p extract_xxx functions require external locking if underlying ordered list requires that
192         static CDS_CONSTEXPR const bool c_bExtractLockExternal = base_class::c_bExtractLockExternal;
193
194     protected:
195         //@cond
196         typedef typename maker::cxx_node_allocator    cxx_node_allocator;
197         typedef typename maker::node_type             node_type;
198         //@endcond
199
200     public:
201         /// pointer to extracted node
202         typedef cds::urcu::exempt_ptr< gc, node_type, value_type, typename maker::ordered_list_traits::disposer > exempt_ptr;
203
204     protected:
205         //@cond
206         template <typename Q, typename Func>
207         bool find_( Q& val, Func f )
208         {
209             return base_class::find( val, [&f]( node_type& item, Q& val ) { f(item.m_Value, val) ; } );
210         }
211
212         template <typename Q, typename Less, typename Func>
213         bool find_with_( Q& val, Less pred, Func f )
214         {
215             return base_class::find_with( val, typename maker::template predicate_wrapper<Less>::type(),
216                 [&f]( node_type& item, Q& val ) { f(item.m_Value, val) ; } );
217         }
218
219         template <typename Q>
220         static node_type * alloc_node( Q const& v )
221         {
222             return cxx_node_allocator().New( v );
223         }
224
225         template <typename... Args>
226         static node_type * alloc_node( Args&&... args )
227         {
228             return cxx_node_allocator().MoveNew( std::forward<Args>(args)...);
229         }
230
231         static void free_node( node_type * pNode )
232         {
233             cxx_node_allocator().Delete( pNode );
234         }
235
236         struct node_disposer {
237             void operator()( node_type * pNode )
238             {
239                 free_node( pNode );
240             }
241         };
242         typedef std::unique_ptr< node_type, node_disposer >     scoped_node_ptr;
243
244         bool insert_node( node_type * pNode )
245         {
246             assert( pNode != nullptr );
247             scoped_node_ptr p(pNode);
248
249             if ( base_class::insert( *pNode ) ) {
250                 p.release();
251                 return true;
252             }
253
254             return false;
255         }
256         //@endcond
257
258     protected:
259         /// Forward iterator
260         /**
261             \p IsConst - constness boolean flag
262
263             The forward iterator for a split-list has the following features:
264             - it has no post-increment operator
265             - it depends on underlying ordered list iterator
266             - it is safe to iterate only inside RCU critical section
267             - deleting an item pointed by the iterator can cause to deadlock
268
269             Therefore, the use of iterators in concurrent environment is not good idea.
270             Use it for debug purpose only.
271         */
272         template <bool IsConst>
273         class iterator_type: protected base_class::template iterator_type<IsConst>
274         {
275             //@cond
276             typedef typename base_class::template iterator_type<IsConst> iterator_base_class;
277             friend class SplitListSet;
278             //@endcond
279         public:
280             /// Value pointer type (const for const iterator)
281             typedef typename cds::details::make_const_type<value_type, IsConst>::pointer   value_ptr;
282             /// Value reference type (const for const iterator)
283             typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
284
285         public:
286             /// Default ctor
287             iterator_type()
288             {}
289
290             /// Copy ctor
291             iterator_type( iterator_type const& src )
292                 : iterator_base_class( src )
293             {}
294
295         protected:
296             //@cond
297             explicit iterator_type( iterator_base_class const& src )
298                 : iterator_base_class( src )
299             {}
300             //@endcond
301
302         public:
303             /// Dereference operator
304             value_ptr operator ->() const
305             {
306                 return &(iterator_base_class::operator->()->m_Value);
307             }
308
309             /// Dereference operator
310             value_ref operator *() const
311             {
312                 return iterator_base_class::operator*().m_Value;
313             }
314
315             /// Pre-increment
316             iterator_type& operator ++()
317             {
318                 iterator_base_class::operator++();
319                 return *this;
320             }
321
322             /// Assignment operator
323             iterator_type& operator = (iterator_type const& src)
324             {
325                 iterator_base_class::operator=(src);
326                 return *this;
327             }
328
329             /// Equality operator
330             template <bool C>
331             bool operator ==(iterator_type<C> const& i ) const
332             {
333                 return iterator_base_class::operator==(i);
334             }
335
336             /// Equality operator
337             template <bool C>
338             bool operator !=(iterator_type<C> const& i ) const
339             {
340                 return iterator_base_class::operator!=(i);
341             }
342         };
343
344     public:
345         /// Initializes split-ordered list of default capacity
346         /**
347             The default capacity is defined in bucket table constructor.
348             See \p intrusive::split_list::expandable_bucket_table, \p intrusive::split_list::static_bucket_table
349             which selects by \p container::split_list::dynamic_bucket_table option.
350         */
351         SplitListSet()
352             : base_class()
353         {}
354
355         /// Initializes split-ordered list
356         SplitListSet(
357             size_t nItemCount           ///< estimated average of item count
358             , size_t nLoadFactor = 1    ///< load factor - average item count per bucket. Small integer up to 8, default is 1.
359             )
360             : base_class( nItemCount, nLoadFactor )
361         {}
362
363     public:
364         typedef iterator_type<false>  iterator        ; ///< Forward iterator
365         typedef iterator_type<true>   const_iterator  ; ///< Forward const iterator
366
367         /// Returns a forward iterator addressing the first element in a set
368         /**
369             For empty set \code begin() == end() \endcode
370         */
371         iterator begin()
372         {
373             return iterator( base_class::begin() );
374         }
375
376         /// Returns an iterator that addresses the location succeeding the last element in a set
377         /**
378             Do not use the value returned by <tt>end</tt> function to access any item.
379             The returned value can be used only to control reaching the end of the set.
380             For empty set \code begin() == end() \endcode
381         */
382         iterator end()
383         {
384             return iterator( base_class::end() );
385         }
386
387         /// Returns a forward const iterator addressing the first element in a set
388         const_iterator begin() const
389         {
390             return const_iterator( base_class::begin() );
391         }
392
393         /// Returns an const iterator that addresses the location succeeding the last element in a set
394         const_iterator end() const
395         {
396             return const_iterator( base_class::end() );
397         }
398
399     public:
400         /// Inserts new node
401         /**
402             The function creates a node with copy of \p val value
403             and then inserts the node created into the set.
404
405             The type \p Q should contain as minimum the complete key for the node.
406             The object of \p value_type should be constructible from a value of type \p Q.
407             In trivial case, \p Q is equal to \p value_type.
408
409             The function applies RCU lock internally.
410
411             Returns \p true if \p val is inserted into the set, \p false otherwise.
412         */
413         template <typename Q>
414         bool insert( Q const& val )
415         {
416             return insert_node( alloc_node( val ) );
417         }
418
419         /// Inserts new node
420         /**
421             The function allows to split creating of new item into two part:
422             - create item with key only
423             - insert new item into the set
424             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
425
426             The functor signature is:
427             \code
428                 void func( value_type& val );
429             \endcode
430             where \p val is the item inserted. User-defined functor \p f should guarantee that during changing
431             \p val no any other changes could be made on this set's item by concurrent threads.
432             The user-defined functor is called only if the inserting is success.
433
434             The function applies RCU lock internally.
435         */
436         template <typename Q, typename Func>
437         bool insert( Q const& key, Func f )
438         {
439             scoped_node_ptr pNode( alloc_node( key ));
440
441             if ( base_class::insert( *pNode, [&f](node_type& node) { f( node.m_Value ) ; } )) {
442                 pNode.release();
443                 return true;
444             }
445             return false;
446         }
447
448         /// Inserts data of type \p value_type created from \p args
449         /**
450             Returns \p true if inserting successful, \p false otherwise.
451
452             The function applies RCU lock internally.
453         */
454         template <typename... Args>
455         bool emplace( Args&&... args )
456         {
457             return insert_node( alloc_node( std::forward<Args>(args)...));
458         }
459
460         /// Ensures that the \p val exists in the set
461         /**
462             The operation performs inserting or changing data with lock-free manner.
463
464             If the \p val key not found in the set, then the new item created from \p val
465             is inserted into the set. Otherwise, the functor \p func is called with the item found.
466             The functor \p Func signature is:
467             \code
468                 struct my_functor {
469                     void operator()( bool bNew, value_type& item, const Q& val );
470                 };
471             \endcode
472
473             with arguments:
474             - \p bNew - \p true if the item has been inserted, \p false otherwise
475             - \p item - item of the set
476             - \p val - argument \p val passed into the \p %ensure() function
477
478             The functor may change non-key fields of the \p item; however, \p func must guarantee
479             that during changing no any other modifications could be made on this item by concurrent threads.
480
481             The function applies RCU lock internally.
482
483             Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
484             \p second is true if new item has been added or \p false if the item with \p key
485             already is in the set.
486         */
487         template <typename Q, typename Func>
488         std::pair<bool, bool> ensure( Q const& val, Func func )
489         {
490             scoped_node_ptr pNode( alloc_node( val ));
491
492             std::pair<bool, bool> bRet = base_class::ensure( *pNode,
493                 [&func, &val]( bool bNew, node_type& item,  node_type const& /*val*/ ) {
494                     func( bNew, item.m_Value, val );
495                 } );
496             if ( bRet.first && bRet.second )
497                 pNode.release();
498             return bRet;
499         }
500
501         /// Deletes \p key from the set
502         /** \anchor cds_nonintrusive_SplitListSet_rcu_erase_val
503
504             Template parameter of type \p Q defines the key type searching in the list.
505             The set item comparator should be able to compare the values of type \p value_type
506             and the type \p Q.
507
508             RCU \p synchronize method can be called. RCU should not be locked.
509
510             Return \p true if key is found and deleted, \p false otherwise
511         */
512         template <typename Q>
513         bool erase( Q const& key )
514         {
515             return base_class::erase( key );
516         }
517
518         /// Deletes the item from the set using \p pred predicate for searching
519         /**
520             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_erase_val "erase(Q const&)"
521             but \p pred is used for key comparing.
522             \p Less functor has the interface like \p std::less.
523             \p Less must imply the same element order as the comparator used for building the set.
524         */
525         template <typename Q, typename Less>
526         bool erase_with( Q const& key, Less pred )
527         {
528              return base_class::erase_with( key, typename maker::template predicate_wrapper<Less>::type() );
529         }
530
531         /// Deletes \p key from the set
532         /** \anchor cds_nonintrusive_SplitListSet_rcu_erase_func
533
534             The function searches an item with key \p key, calls \p f functor
535             and deletes the item. If \p key is not found, the functor is not called.
536
537             The functor \p Func interface:
538             \code
539             struct extractor {
540                 void operator()(value_type const& val);
541             };
542             \endcode
543
544             Template parameter of type \p Q defines the key type searching in the list.
545             The list item comparator should be able to compare the values of the type \p value_type
546             and the type \p Q.
547
548             RCU \p synchronize method can be called. RCU should not be locked.
549
550             Return \p true if key is found and deleted, \p false otherwise
551         */
552         template <typename Q, typename Func>
553         bool erase( Q const& key, Func f )
554         {
555             return base_class::erase( key, [&f](node_type& node) { f( node.m_Value ); } );
556         }
557
558         /// Deletes the item from the set using \p pred predicate for searching
559         /**
560             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_erase_func "erase(Q const&, Func)"
561             but \p pred is used for key comparing.
562             \p Less functor has the interface like \p std::less.
563             \p Less must imply the same element order as the comparator used for building the set.
564         */
565         template <typename Q, typename Less, typename Func>
566         bool erase_with( Q const& key, Less pred, Func f )
567         {
568             return base_class::erase_with( key, typename maker::template predicate_wrapper<Less>::type(),
569                 [&f](node_type& node) { f( node.m_Value ); } );
570         }
571
572         /// Extracts an item from the set
573         /** \anchor cds_nonintrusive_SplitListSet_rcu_extract
574             The function searches an item with key equal to \p key in the set,
575             unlinks it from the set, places item pointer into \p dest argument, and returns \p true.
576             If the item with the key equal to \p key is not found the function return \p false.
577
578             @note The function does NOT call RCU read-side lock or synchronization,
579             and does NOT dispose the item found. It just excludes the item from the set
580             and returns a pointer to item found.
581             You should lock RCU before calling of the function, and you should synchronize RCU
582             outside the RCU lock to free extracted item
583
584             \code
585             typedef cds::urcu::gc< general_buffered<> > rcu;
586             typedef cds::container::SplitListSet< rcu, Foo > splitlist_set;
587
588             splitlist_set theSet;
589             // ...
590
591             splitlist_set::exempt_ptr p;
592             {
593                 // first, we should lock RCU
594                 splitlist_set::rcu_lock lock;
595
596                 // Now, you can apply extract function
597                 // Note that you must not delete the item found inside the RCU lock
598                 if ( theSet.extract( p, 10 )) {
599                     // do something with p
600                     ...
601                 }
602             }
603
604             // We may safely release p here
605             // release() passes the pointer to RCU reclamation cycle
606             p.release();
607             \endcode
608         */
609         template <typename Q>
610         bool extract( exempt_ptr& dest, Q const& key )
611         {
612             node_type * pNode = base_class::extract_( key, key_comparator() );
613             if ( pNode ) {
614                 dest = pNode;
615                 return true;
616             }
617             return false;
618         }
619
620         /// Extracts an item from the set using \p pred predicate for searching
621         /**
622             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_extract "extract(exempt_ptr&, Q const&)"
623             but \p pred is used for key comparing.
624             \p Less functor has the interface like \p std::less.
625             \p pred must imply the same element order as the comparator used for building the set.
626         */
627         template <typename Q, typename Less>
628         bool extract_with( exempt_ptr& dest, Q const& key, Less pred )
629         {
630             node_type * pNode = base_class::extract_with_( key, typename maker::template predicate_wrapper<Less>::type());
631             if ( pNode ) {
632                 dest = pNode;
633                 return true;
634             }
635             return false;
636         }
637
638         /// Finds the key \p key
639         /** \anchor cds_nonintrusive_SplitListSet_rcu_find_func
640
641             The function searches the item with key equal to \p key and calls the functor \p f for item found.
642             The interface of \p Func functor is:
643             \code
644             struct functor {
645                 void operator()( value_type& item, Q& key );
646             };
647             \endcode
648             where \p item is the item found, \p key is the <tt>find</tt> function argument.
649
650             The functor may change non-key fields of \p item. Note that the functor is only guarantee
651             that \p item cannot be disposed during functor is executing.
652             The functor does not serialize simultaneous access to the set's \p item. If such access is
653             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
654
655             Note the hash functor specified for class \p Traits template parameter
656             should accept a parameter of type \p Q that can be not the same as \p value_type.
657
658             The function makes RCU lock internally.
659
660             The function returns \p true if \p key is found, \p false otherwise.
661         */
662         template <typename Q, typename Func>
663         bool find( Q& key, Func f )
664         {
665             return find_( key, f );
666         }
667
668         /// Finds the key \p key using \p pred predicate for searching
669         /**
670             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_find_func "find(Q&, Func)"
671             but \p pred is used for key comparing.
672             \p Less functor has the interface like \p std::less.
673             \p Less must imply the same element order as the comparator used for building the set.
674         */
675         template <typename Q, typename Less, typename Func>
676         bool find_with( Q& key, Less pred, Func f )
677         {
678             return find_with_( key, pred, f );
679         }
680
681         /// Finds the key \p key
682         /** \anchor cds_nonintrusive_SplitListSet_rcu_find_val
683
684             The function searches the item with key equal to \p key
685             and returns \p true if it is found, and \p false otherwise.
686
687             Note the hash functor specified for class \p Traits template parameter
688             should accept a parameter of type \p Q that can be not the same as \p value_type.
689
690             The function makes RCU lock internally.
691         */
692         template <typename Q>
693         bool find( Q const& key )
694         {
695             return base_class::find( key );
696         }
697
698         /// Finds the key \p key using \p pred predicate for searching
699         /**
700             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_find_val "find(Q const&)"
701             but \p pred is used for key comparing.
702             \p Less functor has the interface like \p std::less.
703             \p Less must imply the same element order as the comparator used for building the set.
704         */
705         template <typename Q, typename Less>
706         bool find_with( Q const& key, Less pred )
707         {
708             return base_class::find_with( key, typename maker::template predicate_wrapper<Less>::type() );
709         }
710
711         /// Finds the key \p key and return the item found
712         /** \anchor cds_nonintrusive_SplitListSet_rcu_get
713             The function searches the item with key equal to \p key and returns the pointer to item found.
714             If \p key is not found it returns \p nullptr.
715
716             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
717
718             RCU should be locked before call of this function.
719             Returned item is valid only while RCU is locked:
720             \code
721             typedef cds::urcu::gc< general_buffered<> > rcu;
722             typedef cds::container::SplitListSet< rcu, Foo > splitlist_set;
723             splitlist_set theSet;
724             // ...
725             {
726                 // Lock RCU
727                 splitlist_set::rcu_lock lock;
728
729                 foo * pVal = theSet.get( 5 );
730                 if ( pVal ) {
731                     // Deal with pVal
732                     //...
733                 }
734                 // Unlock RCU by rcu_lock destructor
735                 // pVal can be retired by disposer at any time after RCU has been unlocked
736             }
737             \endcode
738         */
739         template <typename Q>
740         value_type * get( Q const& key )
741         {
742             node_type * pNode = base_class::get( key );
743             return pNode ? &pNode->m_Value : nullptr;
744         }
745
746         /// Finds the key \p key and return the item found
747         /**
748             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_get "get(Q const&)"
749             but \p pred is used for comparing the keys.
750
751             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
752             in any order.
753             \p pred must imply the same element order as the comparator used for building the set.
754         */
755         template <typename Q, typename Less>
756         value_type * get_with( Q const& key, Less pred )
757         {
758             node_type * pNode = base_class::get_with( key, typename maker::template predicate_wrapper<Less>::type());
759             return pNode ? &pNode->m_Value : nullptr;
760         }
761
762         /// Clears the set (not atomic)
763         void clear()
764         {
765             base_class::clear();
766         }
767
768         /// Checks if the set is empty
769         /**
770             Emptiness is checked by item counting: if item count is zero then assume that the set is empty.
771             Thus, the correct item counting feature is an important part of split-list set implementation.
772         */
773         bool empty() const
774         {
775             return base_class::empty();
776         }
777
778         /// Returns item count in the set
779         size_t size() const
780         {
781             return base_class::size();
782         }
783     };
784 }}  // namespace cds::container
785
786 #endif // #ifndef __CDS_CONTAINER_SPLIT_LIST_SET_RCU_H