Fixed iterator issues in set/map
[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         typedef typename base_class::stat           stat; ///< Internal statistics
190
191         typedef typename base_class::rcu_lock      rcu_lock   ; ///< RCU scoped lock
192         /// Group of \p extract_xxx functions require external locking if underlying ordered list requires that
193         static CDS_CONSTEXPR const bool c_bExtractLockExternal = base_class::c_bExtractLockExternal;
194
195     protected:
196         //@cond
197         typedef typename maker::cxx_node_allocator    cxx_node_allocator;
198         typedef typename maker::node_type             node_type;
199         //@endcond
200
201     public:
202         /// pointer to extracted node
203         typedef cds::urcu::exempt_ptr< gc, node_type, value_type, typename maker::ordered_list_traits::disposer > exempt_ptr;
204
205     protected:
206         //@cond
207         template <typename Q, typename Func>
208         bool find_( Q& val, Func f )
209         {
210             return base_class::find( val, [&f]( node_type& item, Q& val ) { f(item.m_Value, val) ; } );
211         }
212
213         template <typename Q, typename Less, typename Func>
214         bool find_with_( Q& val, Less pred, Func f )
215         {
216             return base_class::find_with( val, typename maker::template predicate_wrapper<Less>::type(),
217                 [&f]( node_type& item, Q& val ) { f(item.m_Value, val) ; } );
218         }
219
220         template <typename Q>
221         static node_type * alloc_node( Q const& v )
222         {
223             return cxx_node_allocator().New( v );
224         }
225
226         template <typename... Args>
227         static node_type * alloc_node( Args&&... args )
228         {
229             return cxx_node_allocator().MoveNew( std::forward<Args>(args)...);
230         }
231
232         static void free_node( node_type * pNode )
233         {
234             cxx_node_allocator().Delete( pNode );
235         }
236
237         struct node_disposer {
238             void operator()( node_type * pNode )
239             {
240                 free_node( pNode );
241             }
242         };
243         typedef std::unique_ptr< node_type, node_disposer >     scoped_node_ptr;
244
245         bool insert_node( node_type * pNode )
246         {
247             assert( pNode != nullptr );
248             scoped_node_ptr p(pNode);
249
250             if ( base_class::insert( *pNode ) ) {
251                 p.release();
252                 return true;
253             }
254
255             return false;
256         }
257         //@endcond
258
259     protected:
260         /// Forward iterator
261         /**
262             \p IsConst - constness boolean flag
263
264             The forward iterator for a split-list has the following features:
265             - it has no post-increment operator
266             - it depends on underlying ordered list iterator
267             - it is safe to iterate only inside RCU critical section
268             - deleting an item pointed by the iterator can cause to deadlock
269
270             Therefore, the use of iterators in concurrent environment is not good idea.
271             Use it for debug purpose only.
272         */
273         template <bool IsConst>
274         class iterator_type: protected base_class::template iterator_type<IsConst>
275         {
276             //@cond
277             typedef typename base_class::template iterator_type<IsConst> iterator_base_class;
278             friend class SplitListSet;
279             //@endcond
280         public:
281             /// Value pointer type (const for const iterator)
282             typedef typename cds::details::make_const_type<value_type, IsConst>::pointer   value_ptr;
283             /// Value reference type (const for const iterator)
284             typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
285
286         public:
287             /// Default ctor
288             iterator_type()
289             {}
290
291             /// Copy ctor
292             iterator_type( iterator_type const& src )
293                 : iterator_base_class( src )
294             {}
295
296         protected:
297             //@cond
298             explicit iterator_type( iterator_base_class const& src )
299                 : iterator_base_class( src )
300             {}
301             //@endcond
302
303         public:
304             /// Dereference operator
305             value_ptr operator ->() const
306             {
307                 return &(iterator_base_class::operator->()->m_Value);
308             }
309
310             /// Dereference operator
311             value_ref operator *() const
312             {
313                 return iterator_base_class::operator*().m_Value;
314             }
315
316             /// Pre-increment
317             iterator_type& operator ++()
318             {
319                 iterator_base_class::operator++();
320                 return *this;
321             }
322
323             /// Assignment operator
324             iterator_type& operator = (iterator_type const& src)
325             {
326                 iterator_base_class::operator=(src);
327                 return *this;
328             }
329
330             /// Equality operator
331             template <bool C>
332             bool operator ==(iterator_type<C> const& i ) const
333             {
334                 return iterator_base_class::operator==(i);
335             }
336
337             /// Equality operator
338             template <bool C>
339             bool operator !=(iterator_type<C> const& i ) const
340             {
341                 return iterator_base_class::operator!=(i);
342             }
343         };
344
345     public:
346         /// Initializes split-ordered list of default capacity
347         /**
348             The default capacity is defined in bucket table constructor.
349             See \p intrusive::split_list::expandable_bucket_table, \p intrusive::split_list::static_bucket_table
350             which selects by \p container::split_list::dynamic_bucket_table option.
351         */
352         SplitListSet()
353             : base_class()
354         {}
355
356         /// Initializes split-ordered list
357         SplitListSet(
358             size_t nItemCount           ///< estimated average of item count
359             , size_t nLoadFactor = 1    ///< load factor - average item count per bucket. Small integer up to 8, default is 1.
360             )
361             : base_class( nItemCount, nLoadFactor )
362         {}
363
364     public:
365         typedef iterator_type<false>  iterator        ; ///< Forward iterator
366         typedef iterator_type<true>   const_iterator  ; ///< Forward const iterator
367
368         /// Returns a forward iterator addressing the first element in a set
369         /**
370             For empty set \code begin() == end() \endcode
371         */
372         iterator begin()
373         {
374             return iterator( base_class::begin() );
375         }
376
377         /// Returns an iterator that addresses the location succeeding the last element in a set
378         /**
379             Do not use the value returned by <tt>end</tt> function to access any item.
380             The returned value can be used only to control reaching the end of the set.
381             For empty set \code begin() == end() \endcode
382         */
383         iterator end()
384         {
385             return iterator( base_class::end() );
386         }
387
388         /// Returns a forward const iterator addressing the first element in a set
389         const_iterator begin() const
390         {
391             return cbegin();
392         }
393         /// Returns a forward const iterator addressing the first element in a set
394         const_iterator cbegin() const
395         {
396             return const_iterator( base_class::cbegin() );
397         }
398
399         /// Returns an const iterator that addresses the location succeeding the last element in a set
400         const_iterator end() const
401         {
402             return cend();
403         }
404         /// Returns an const iterator that addresses the location succeeding the last element in a set
405         const_iterator cend() const
406         {
407             return const_iterator( base_class::cend() );
408         }
409
410     public:
411         /// Inserts new node
412         /**
413             The function creates a node with copy of \p val value
414             and then inserts the node created into the set.
415
416             The type \p Q should contain as minimum the complete key for the node.
417             The object of \p value_type should be constructible from a value of type \p Q.
418             In trivial case, \p Q is equal to \p value_type.
419
420             The function applies RCU lock internally.
421
422             Returns \p true if \p val is inserted into the set, \p false otherwise.
423         */
424         template <typename Q>
425         bool insert( Q const& val )
426         {
427             return insert_node( alloc_node( val ) );
428         }
429
430         /// Inserts new node
431         /**
432             The function allows to split creating of new item into two part:
433             - create item with key only
434             - insert new item into the set
435             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
436
437             The functor signature is:
438             \code
439                 void func( value_type& val );
440             \endcode
441             where \p val is the item inserted. User-defined functor \p f should guarantee that during changing
442             \p val no any other changes could be made on this set's item by concurrent threads.
443             The user-defined functor is called only if the inserting is success.
444
445             The function applies RCU lock internally.
446         */
447         template <typename Q, typename Func>
448         bool insert( Q const& key, Func f )
449         {
450             scoped_node_ptr pNode( alloc_node( key ));
451
452             if ( base_class::insert( *pNode, [&f](node_type& node) { f( node.m_Value ) ; } )) {
453                 pNode.release();
454                 return true;
455             }
456             return false;
457         }
458
459         /// Inserts data of type \p value_type created from \p args
460         /**
461             Returns \p true if inserting successful, \p false otherwise.
462
463             The function applies RCU lock internally.
464         */
465         template <typename... Args>
466         bool emplace( Args&&... args )
467         {
468             return insert_node( alloc_node( std::forward<Args>(args)...));
469         }
470
471         /// Ensures that the \p val exists in the set
472         /**
473             The operation performs inserting or changing data with lock-free manner.
474
475             If the \p val key not found in the set, then the new item created from \p val
476             is inserted into the set. Otherwise, the functor \p func is called with the item found.
477             The functor \p Func signature is:
478             \code
479                 struct my_functor {
480                     void operator()( bool bNew, value_type& item, const Q& val );
481                 };
482             \endcode
483
484             with arguments:
485             - \p bNew - \p true if the item has been inserted, \p false otherwise
486             - \p item - item of the set
487             - \p val - argument \p val passed into the \p %ensure() function
488
489             The functor may change non-key fields of the \p item; however, \p func must guarantee
490             that during changing no any other modifications could be made on this item by concurrent threads.
491
492             The function applies RCU lock internally.
493
494             Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
495             \p second is true if new item has been added or \p false if the item with \p key
496             already is in the set.
497         */
498         template <typename Q, typename Func>
499         std::pair<bool, bool> ensure( Q const& val, Func func )
500         {
501             scoped_node_ptr pNode( alloc_node( val ));
502
503             std::pair<bool, bool> bRet = base_class::ensure( *pNode,
504                 [&func, &val]( bool bNew, node_type& item,  node_type const& /*val*/ ) {
505                     func( bNew, item.m_Value, val );
506                 } );
507             if ( bRet.first && bRet.second )
508                 pNode.release();
509             return bRet;
510         }
511
512         /// Deletes \p key from the set
513         /** \anchor cds_nonintrusive_SplitListSet_rcu_erase_val
514
515             Template parameter of type \p Q defines the key type searching in the list.
516             The set item comparator should be able to compare the values of type \p value_type
517             and the type \p Q.
518
519             RCU \p synchronize method can be called. RCU should not be locked.
520
521             Return \p true if key is found and deleted, \p false otherwise
522         */
523         template <typename Q>
524         bool erase( Q const& key )
525         {
526             return base_class::erase( key );
527         }
528
529         /// Deletes the item from the set using \p pred predicate for searching
530         /**
531             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_erase_val "erase(Q const&)"
532             but \p pred is used for key comparing.
533             \p Less functor has the interface like \p std::less.
534             \p Less must imply the same element order as the comparator used for building the set.
535         */
536         template <typename Q, typename Less>
537         bool erase_with( Q const& key, Less pred )
538         {
539              return base_class::erase_with( key, typename maker::template predicate_wrapper<Less>::type() );
540         }
541
542         /// Deletes \p key from the set
543         /** \anchor cds_nonintrusive_SplitListSet_rcu_erase_func
544
545             The function searches an item with key \p key, calls \p f functor
546             and deletes the item. If \p key is not found, the functor is not called.
547
548             The functor \p Func interface:
549             \code
550             struct extractor {
551                 void operator()(value_type const& val);
552             };
553             \endcode
554
555             Template parameter of type \p Q defines the key type searching in the list.
556             The list item comparator should be able to compare the values of the type \p value_type
557             and the type \p Q.
558
559             RCU \p synchronize method can be called. RCU should not be locked.
560
561             Return \p true if key is found and deleted, \p false otherwise
562         */
563         template <typename Q, typename Func>
564         bool erase( Q const& key, Func f )
565         {
566             return base_class::erase( key, [&f](node_type& node) { f( node.m_Value ); } );
567         }
568
569         /// Deletes the item from the set using \p pred predicate for searching
570         /**
571             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_erase_func "erase(Q const&, Func)"
572             but \p pred is used for key comparing.
573             \p Less functor has the interface like \p std::less.
574             \p Less must imply the same element order as the comparator used for building the set.
575         */
576         template <typename Q, typename Less, typename Func>
577         bool erase_with( Q const& key, Less pred, Func f )
578         {
579             return base_class::erase_with( key, typename maker::template predicate_wrapper<Less>::type(),
580                 [&f](node_type& node) { f( node.m_Value ); } );
581         }
582
583         /// Extracts an item from the set
584         /** \anchor cds_nonintrusive_SplitListSet_rcu_extract
585             The function searches an item with key equal to \p key in the set,
586             unlinks it from the set, places item pointer into \p dest argument, and returns \p true.
587             If the item with the key equal to \p key is not found the function return \p false.
588
589             @note The function does NOT call RCU read-side lock or synchronization,
590             and does NOT dispose the item found. It just excludes the item from the set
591             and returns a pointer to item found.
592             You should lock RCU before calling of the function, and you should synchronize RCU
593             outside the RCU lock to free extracted item
594
595             \code
596             typedef cds::urcu::gc< general_buffered<> > rcu;
597             typedef cds::container::SplitListSet< rcu, Foo > splitlist_set;
598
599             splitlist_set theSet;
600             // ...
601
602             splitlist_set::exempt_ptr p;
603             {
604                 // first, we should lock RCU
605                 splitlist_set::rcu_lock lock;
606
607                 // Now, you can apply extract function
608                 // Note that you must not delete the item found inside the RCU lock
609                 if ( theSet.extract( p, 10 )) {
610                     // do something with p
611                     ...
612                 }
613             }
614
615             // We may safely release p here
616             // release() passes the pointer to RCU reclamation cycle
617             p.release();
618             \endcode
619         */
620         template <typename Q>
621         bool extract( exempt_ptr& dest, Q const& key )
622         {
623             node_type * pNode = base_class::extract_( key, key_comparator() );
624             if ( pNode ) {
625                 dest = pNode;
626                 return true;
627             }
628             return false;
629         }
630
631         /// Extracts an item from the set using \p pred predicate for searching
632         /**
633             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_extract "extract(exempt_ptr&, Q const&)"
634             but \p pred is used for key comparing.
635             \p Less functor has the interface like \p std::less.
636             \p pred must imply the same element order as the comparator used for building the set.
637         */
638         template <typename Q, typename Less>
639         bool extract_with( exempt_ptr& dest, Q const& key, Less pred )
640         {
641             node_type * pNode = base_class::extract_with_( key, typename maker::template predicate_wrapper<Less>::type());
642             if ( pNode ) {
643                 dest = pNode;
644                 return true;
645             }
646             return false;
647         }
648
649         /// Finds the key \p key
650         /** \anchor cds_nonintrusive_SplitListSet_rcu_find_func
651
652             The function searches the item with key equal to \p key and calls the functor \p f for item found.
653             The interface of \p Func functor is:
654             \code
655             struct functor {
656                 void operator()( value_type& item, Q& key );
657             };
658             \endcode
659             where \p item is the item found, \p key is the <tt>find</tt> function argument.
660
661             The functor may change non-key fields of \p item. Note that the functor is only guarantee
662             that \p item cannot be disposed during functor is executing.
663             The functor does not serialize simultaneous access to the set's \p item. If such access is
664             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
665
666             Note the hash functor specified for class \p Traits template parameter
667             should accept a parameter of type \p Q that can be not the same as \p value_type.
668
669             The function makes RCU lock internally.
670
671             The function returns \p true if \p key is found, \p false otherwise.
672         */
673         template <typename Q, typename Func>
674         bool find( Q& key, Func f )
675         {
676             return find_( key, f );
677         }
678
679         /// Finds the key \p key using \p pred predicate for searching
680         /**
681             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_find_func "find(Q&, Func)"
682             but \p pred is used for key comparing.
683             \p Less functor has the interface like \p std::less.
684             \p Less must imply the same element order as the comparator used for building the set.
685         */
686         template <typename Q, typename Less, typename Func>
687         bool find_with( Q& key, Less pred, Func f )
688         {
689             return find_with_( key, pred, f );
690         }
691
692         /// Finds the key \p key
693         /** \anchor cds_nonintrusive_SplitListSet_rcu_find_val
694
695             The function searches the item with key equal to \p key
696             and returns \p true if it is found, and \p false otherwise.
697
698             Note the hash functor specified for class \p Traits template parameter
699             should accept a parameter of type \p Q that can be not the same as \p value_type.
700
701             The function makes RCU lock internally.
702         */
703         template <typename Q>
704         bool find( Q const& key )
705         {
706             return base_class::find( key );
707         }
708
709         /// Finds the key \p key using \p pred predicate for searching
710         /**
711             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_find_val "find(Q const&)"
712             but \p pred is used for key comparing.
713             \p Less functor has the interface like \p std::less.
714             \p Less must imply the same element order as the comparator used for building the set.
715         */
716         template <typename Q, typename Less>
717         bool find_with( Q const& key, Less pred )
718         {
719             return base_class::find_with( key, typename maker::template predicate_wrapper<Less>::type() );
720         }
721
722         /// Finds the key \p key and return the item found
723         /** \anchor cds_nonintrusive_SplitListSet_rcu_get
724             The function searches the item with key equal to \p key and returns the pointer to item found.
725             If \p key is not found it returns \p nullptr.
726
727             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
728
729             RCU should be locked before call of this function.
730             Returned item is valid only while RCU is locked:
731             \code
732             typedef cds::urcu::gc< general_buffered<> > rcu;
733             typedef cds::container::SplitListSet< rcu, Foo > splitlist_set;
734             splitlist_set theSet;
735             // ...
736             {
737                 // Lock RCU
738                 splitlist_set::rcu_lock lock;
739
740                 foo * pVal = theSet.get( 5 );
741                 if ( pVal ) {
742                     // Deal with pVal
743                     //...
744                 }
745                 // Unlock RCU by rcu_lock destructor
746                 // pVal can be retired by disposer at any time after RCU has been unlocked
747             }
748             \endcode
749         */
750         template <typename Q>
751         value_type * get( Q const& key )
752         {
753             node_type * pNode = base_class::get( key );
754             return pNode ? &pNode->m_Value : nullptr;
755         }
756
757         /// Finds the key \p key and return the item found
758         /**
759             The function is an analog of \ref cds_nonintrusive_SplitListSet_rcu_get "get(Q const&)"
760             but \p pred is used for comparing the keys.
761
762             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
763             in any order.
764             \p pred must imply the same element order as the comparator used for building the set.
765         */
766         template <typename Q, typename Less>
767         value_type * get_with( Q const& key, Less pred )
768         {
769             node_type * pNode = base_class::get_with( key, typename maker::template predicate_wrapper<Less>::type());
770             return pNode ? &pNode->m_Value : nullptr;
771         }
772
773         /// Clears the set (not atomic)
774         void clear()
775         {
776             base_class::clear();
777         }
778
779         /// Checks if the set is empty
780         /**
781             Emptiness is checked by item counting: if item count is zero then assume that the set is empty.
782             Thus, the correct item counting feature is an important part of split-list set implementation.
783         */
784         bool empty() const
785         {
786             return base_class::empty();
787         }
788
789         /// Returns item count in the set
790         size_t size() const
791         {
792             return base_class::size();
793         }
794
795         /// Returns internal statistics
796         stat const& statistics() const
797         {
798             return base_class::statistics();
799         }
800     };
801 }}  // namespace cds::container
802
803 #endif // #ifndef __CDS_CONTAINER_SPLIT_LIST_SET_RCU_H