issue#11: cds: changed __CDS_ guard prefix to CDSLIB_ for all .h files
[libcds.git] / cds / container / impl / skip_list_set.h
1 //$$CDS-header$$
2
3 #ifndef CDSLIB_CONTAINER_IMPL_SKIP_LIST_SET_H
4 #define CDSLIB_CONTAINER_IMPL_SKIP_LIST_SET_H
5
6 #include <cds/details/binary_functor_wrapper.h>
7 #include <cds/container/details/guarded_ptr_cast.h>
8
9 namespace cds { namespace container {
10
11     /// Lock-free skip-list set
12     /** @ingroup cds_nonintrusive_set
13         \anchor cds_nonintrusive_SkipListSet_hp
14
15         The implementation of well-known probabilistic data structure called skip-list
16         invented by W.Pugh in his papers:
17             - [1989] W.Pugh Skip Lists: A Probabilistic Alternative to Balanced Trees
18             - [1990] W.Pugh A Skip List Cookbook
19
20         A skip-list is a probabilistic data structure that provides expected logarithmic
21         time search without the need of rebalance. The skip-list is a collection of sorted
22         linked list. Nodes are ordered by key. Each node is linked into a subset of the lists.
23         Each list has a level, ranging from 0 to 32. The bottom-level list contains
24         all the nodes, and each higher-level list is a sublist of the lower-level lists.
25         Each node is created with a random top level (with a random height), and belongs
26         to all lists up to that level. The probability that a node has the height 1 is 1/2.
27         The probability that a node has the height N is 1/2 ** N (more precisely,
28         the distribution depends on an random generator provided, but our generators
29         have this property).
30
31         The lock-free variant of skip-list is implemented according to book
32             - [2008] M.Herlihy, N.Shavit "The Art of Multiprocessor Programming",
33                 chapter 14.4 "A Lock-Free Concurrent Skiplist"
34
35         Template arguments:
36         - \p GC - Garbage collector used.
37         - \p T - type to be stored in the list.
38         - \p Traits - set traits, default is \p skip_list::traits.
39             It is possible to declare option-based list with \p cds::container::skip_list::make_traits metafunction
40             istead of \p Traits template argument.
41
42         @warning The skip-list requires up to 67 hazard pointers that may be critical for some GCs for which
43             the guard count is limited (like as \p gc::HP). Those GCs should be explicitly initialized with
44             hazard pointer enough: \code cds::gc::HP myhp( 67 ) \endcode. Otherwise an run-time exception may be raised
45             when you try to create skip-list object.
46
47         @note There are several specializations of \p %SkipListSet for each \p GC. You should include:
48         - <tt><cds/container/skip_list_set_hp.h></tt> for \p gc::HP garbage collector
49         - <tt><cds/container/skip_list_set_dhp.h></tt> for \p gc::DHP garbage collector
50         - <tt><cds/container/skip_list_set_rcu.h></tt> for \ref cds_nonintrusive_SkipListSet_rcu "RCU type"
51         - <tt><cds/container/skip_list_set_nogc.h></tt> for \ref cds_nonintrusive_SkipListSet_nogc "non-deletable SkipListSet"
52
53         <b>Iterators</b>
54
55         The class supports a forward iterator (\ref iterator and \ref const_iterator).
56         The iteration is ordered.
57         The iterator object is thread-safe: the element pointed by the iterator object is guarded,
58         so, the element cannot be reclaimed while the iterator object is alive.
59         However, passing an iterator object between threads is dangerous.
60
61         \warning Due to concurrent nature of skip-list set it is not guarantee that you can iterate
62         all elements in the set: any concurrent deletion can exclude the element
63         pointed by the iterator from the set, and your iteration can be terminated
64         before end of the set. Therefore, such iteration is more suitable for debugging purpose only
65
66         Remember, each iterator object requires 2 additional hazard pointers, that may be
67         a limited resource for \p GC like \p gc::HP (for \p gc::DHP the count of
68         guards is unlimited).
69
70         The iterator class supports the following minimalistic interface:
71         \code
72         struct iterator {
73             // Default ctor
74             iterator();
75
76             // Copy ctor
77             iterator( iterator const& s);
78
79             value_type * operator ->() const;
80             value_type& operator *() const;
81
82             // Pre-increment
83             iterator& operator ++();
84
85             // Copy assignment
86             iterator& operator = (const iterator& src);
87
88             bool operator ==(iterator const& i ) const;
89             bool operator !=(iterator const& i ) const;
90         };
91         \endcode
92         Note, the iterator object returned by \p end(), \p cend() member functions points to \p nullptr and should not be dereferenced.
93     */
94     template <
95         typename GC,
96         typename T,
97 #ifdef CDS_DOXYGEN_INVOKED
98         typename Traits = skip_list::traits
99 #else
100         typename Traits
101 #endif
102     >
103     class SkipListSet:
104 #ifdef CDS_DOXYGEN_INVOKED
105         protected intrusive::SkipListSet< GC, T, Traits >
106 #else
107         protected details::make_skip_list_set< GC, T, Traits >::type
108 #endif
109     {
110         //@cond
111         typedef details::make_skip_list_set< GC, T, Traits > maker;
112         typedef typename maker::type base_class;
113         //@endcond
114     public:
115         typedef GC     gc;          ///< Garbage collector used
116         typedef T      value_type;  ///< @anchor cds_containewr_SkipListSet_value_type Value type to be stored in the set
117         typedef Traits traits;      ///< Options specified
118
119         typedef typename base_class::back_off     back_off;       ///< Back-off strategy
120         typedef typename traits::allocator        allocator_type; ///< Allocator type used for allocate/deallocate the skip-list nodes
121         typedef typename base_class::item_counter item_counter;   ///< Item counting policy used
122         typedef typename maker::key_comparator    key_comparator; ///< key comparison functor
123         typedef typename base_class::memory_model memory_model;   ///< Memory ordering. See cds::opt::memory_model option
124         typedef typename traits::random_level_generator random_level_generator; ///< random level generator
125         typedef typename traits::stat             stat;           ///< internal statistics type
126
127     protected:
128         //@cond
129         typedef typename maker::node_type           node_type;
130         typedef typename maker::node_allocator      node_allocator;
131
132         typedef std::unique_ptr< node_type, typename maker::node_deallocator >    scoped_node_ptr;
133         //@endcond
134
135     public:
136         /// Guarded pointer
137         typedef typename gc::template guarded_ptr< node_type, value_type, details::guarded_ptr_cast_set<node_type, value_type> > guarded_ptr;
138
139     protected:
140         //@cond
141         unsigned int random_level()
142         {
143             return base_class::random_level();
144         }
145         //@endcond
146
147     public:
148         /// Default ctor
149         SkipListSet()
150             : base_class()
151         {}
152
153         /// Destructor destroys the set object
154         ~SkipListSet()
155         {}
156
157     public:
158         /// Iterator type
159         typedef skip_list::details::iterator< typename base_class::iterator >  iterator;
160
161         /// Const iterator type
162         typedef skip_list::details::iterator< typename base_class::const_iterator >   const_iterator;
163
164         /// Returns a forward iterator addressing the first element in a set
165         iterator begin()
166         {
167             return iterator( base_class::begin() );
168         }
169
170         /// Returns a forward const iterator addressing the first element in a set
171         const_iterator begin() const
172         {
173             return const_iterator( base_class::begin() );
174         }
175
176         /// Returns a forward const iterator addressing the first element in a set
177         const_iterator cbegin() const
178         {
179             return const_iterator( base_class::cbegin() );
180         }
181
182         /// Returns a forward iterator that addresses the location succeeding the last element in a set.
183         iterator end()
184         {
185             return iterator( base_class::end() );
186         }
187
188         /// Returns a forward const iterator that addresses the location succeeding the last element in a set.
189         const_iterator end() const
190         {
191             return const_iterator( base_class::end() );
192         }
193
194         /// Returns a forward const iterator that addresses the location succeeding the last element in a set.
195         const_iterator cend() const
196         {
197             return const_iterator( base_class::cend() );
198         }
199
200     public:
201         /// Inserts new node
202         /**
203             The function creates a node with copy of \p val value
204             and then inserts the node created into the set.
205
206             The type \p Q should contain as minimum the complete key for the node.
207             The object of \ref value_type should be constructible from a value of type \p Q.
208             In trivial case, \p Q is equal to \ref value_type.
209
210             Returns \p true if \p val is inserted into the set, \p false otherwise.
211         */
212         template <typename Q>
213         bool insert( Q const& val )
214         {
215             scoped_node_ptr sp( node_allocator().New( random_level(), val ));
216             if ( base_class::insert( *sp.get() )) {
217                 sp.release();
218                 return true;
219             }
220             return false;
221         }
222
223         /// Inserts new node
224         /**
225             The function allows to split creating of new item into two part:
226             - create item with key only
227             - insert new item into the set
228             - if inserting is success, calls  \p f functor to initialize value-fields of \p val.
229
230             The functor signature is:
231             \code
232                 void func( value_type& val );
233             \endcode
234             where \p val is the item inserted. User-defined functor \p f should guarantee that during changing
235             \p val no any other changes could be made on this set's item by concurrent threads.
236             The user-defined functor is called only if the inserting is success.
237         */
238         template <typename Q, typename Func>
239         bool insert( Q const& val, Func f )
240         {
241             scoped_node_ptr sp( node_allocator().New( random_level(), val ));
242             if ( base_class::insert( *sp.get(), [&f]( node_type& val ) { f( val.m_Value ); } )) {
243                 sp.release();
244                 return true;
245             }
246             return false;
247         }
248
249         /// Ensures that the item exists in the set
250         /**
251             The operation performs inserting or changing data with lock-free manner.
252
253             If the \p val key not found in the set, then the new item created from \p val
254             is inserted into the set. Otherwise, the functor \p func is called with the item found.
255             The functor \p Func should be a function with signature:
256             \code
257                 void func( bool bNew, value_type& item, const Q& val );
258             \endcode
259             or a functor:
260             \code
261                 struct my_functor {
262                     void operator()( bool bNew, value_type& item, const Q& val );
263                 };
264             \endcode
265
266             with arguments:
267             - \p bNew - \p true if the item has been inserted, \p false otherwise
268             - \p item - item of the set
269             - \p val - argument \p key passed into the \p %ensure() function
270
271             The functor may change non-key fields of the \p item; however, \p func must guarantee
272             that during changing no any other modifications could be made on this item by concurrent threads.
273
274             Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
275             \p second is true if new item has been added or \p false if the item with \p key
276             already is in the set.
277
278             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
279         */
280         template <typename Q, typename Func>
281         std::pair<bool, bool> ensure( const Q& val, Func func )
282         {
283             scoped_node_ptr sp( node_allocator().New( random_level(), val ));
284             std::pair<bool, bool> bRes = base_class::ensure( *sp,
285                 [&func, &val](bool bNew, node_type& node, node_type&){ func( bNew, node.m_Value, val ); });
286             if ( bRes.first && bRes.second )
287                 sp.release();
288             return bRes;
289         }
290
291         /// Inserts data of type \p value_type created in-place from <tt>std::forward<Args>(args)...</tt>
292         /**
293             Returns \p true if inserting successful, \p false otherwise.
294         */
295         template <typename... Args>
296         bool emplace( Args&&... args )
297         {
298             scoped_node_ptr sp( node_allocator().New( random_level(), std::forward<Args>(args)... ));
299             if ( base_class::insert( *sp.get() )) {
300                 sp.release();
301                 return true;
302             }
303             return false;
304         }
305
306         /// Delete \p key from the set
307         /** \anchor cds_nonintrusive_SkipListSet_erase_val
308
309             The set item comparator should be able to compare the type \p value_type
310             and the type \p Q.
311
312             Return \p true if key is found and deleted, \p false otherwise
313         */
314         template <typename Q>
315         bool erase( Q const& key )
316         {
317             return base_class::erase( key );
318         }
319
320         /// Deletes the item from the set using \p pred predicate for searching
321         /**
322             The function is an analog of \ref cds_nonintrusive_SkipListSet_erase_val "erase(Q const&)"
323             but \p pred is used for key comparing.
324             \p Less functor has the interface like \p std::less.
325             \p Less must imply the same element order as the comparator used for building the set.
326         */
327         template <typename Q, typename Less>
328         bool erase_with( Q const& key, Less pred )
329         {
330             CDS_UNUSED( pred );
331             return base_class::erase_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >() );
332         }
333
334         /// Delete \p key from the set
335         /** \anchor cds_nonintrusive_SkipListSet_erase_func
336
337             The function searches an item with key \p key, calls \p f functor
338             and deletes the item. If \p key is not found, the functor is not called.
339
340             The functor \p Func interface:
341             \code
342             struct extractor {
343                 void operator()(value_type const& val);
344             };
345             \endcode
346
347             Since the key of \p value_type is not explicitly specified,
348             template parameter \p Q defines the key type to search in the list.
349             The list item comparator should be able to compare the type \p T of list item
350             and the type \p Q.
351
352             Return \p true if key is found and deleted, \p false otherwise
353         */
354         template <typename Q, typename Func>
355         bool erase( Q const& key, Func f )
356         {
357             return base_class::erase( key, [&f]( node_type const& node) { f( node.m_Value ); } );
358         }
359
360         /// Deletes the item from the set using \p pred predicate for searching
361         /**
362             The function is an analog of \ref cds_nonintrusive_SkipListSet_erase_func "erase(Q const&, Func)"
363             but \p pred is used for key comparing.
364             \p Less functor has the interface like \p std::less.
365             \p Less must imply the same element order as the comparator used for building the set.
366         */
367         template <typename Q, typename Less, typename Func>
368         bool erase_with( Q const& key, Less pred, Func f )
369         {
370             CDS_UNUSED( pred );
371             return base_class::erase_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >(),
372                 [&f]( node_type const& node) { f( node.m_Value ); } );
373         }
374
375         /// Extracts the item from the set with specified \p key
376         /** \anchor cds_nonintrusive_SkipListSet_hp_extract
377             The function searches an item with key equal to \p key in the set,
378             unlinks it from the set, and returns it as \p guarded_ptr.
379             If \p key is not found the function returns an empty guarded pointer.
380
381             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
382
383             The item extracted is freed automatically by garbage collector \p GC
384             when returned \p guarded_ptr object will be destroyed or released.
385             @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
386
387             Usage:
388             \code
389             typedef cds::container::SkipListSet< cds::gc::HP, foo, my_traits >  skip_list;
390             skip_list theList;
391             // ...
392             {
393                 skip_list::guarded_ptr gp(theList.extract( 5 ))
394                 if (  gp ) {
395                     // Deal with gp
396                     // ...
397                 }
398                 // Destructor of gp releases internal HP guard and frees the pointer
399             }
400             \endcode
401         */
402         template <typename Q>
403         guarded_ptr extract( Q const& key )
404         {
405             guarded_ptr gp;
406             base_class::extract_( gp.guard(), key, typename base_class::key_comparator() );
407             return gp;
408         }
409
410         /// Extracts the item from the set with comparing functor \p pred
411         /**
412             The function is an analog of \ref cds_nonintrusive_SkipListSet_hp_extract "extract(Q const&)"
413             but \p pred predicate is used for key comparing.
414
415             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
416             in any order.
417             \p pred must imply the same element order as the comparator used for building the set.
418         */
419         template <typename Q, typename Less>
420         guarded_ptr extract_with( Q const& key, Less pred )
421         {
422             CDS_UNUSED( pred );
423             typedef cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >  wrapped_less;
424             guarded_ptr gp;
425             base_class::extract_( gp.guard(), key, cds::opt::details::make_comparator_from_less<wrapped_less>() );
426             return gp;
427         }
428
429         /// Extracts an item with minimal key from the set
430         /**
431             The function searches an item with minimal key, unlinks it, and returns pointer to the item found as \p guarded_ptr.
432             If the skip-list is empty the function returns an empty guarded pointer.
433
434             The item extracted is freed automatically by garbage collector \p GC
435             when returned \p guarded_ptr object will be destroyed or released.
436             @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
437
438             Usage:
439             \code
440             typedef cds::continer::SkipListSet< cds::gc::HP, foo, my_traits >  skip_list;
441             skip_list theList;
442             // ...
443             {
444                 skip_list::guarded_ptr gp( theList.extract_min());
445                 if ( gp ) {
446                     // Deal with gp
447                     //...
448                 }
449                 // Destructor of gp releases internal HP guard and then frees the pointer
450             }
451             \endcode
452         */
453         guarded_ptr extract_min()
454         {
455             guarded_ptr gp;
456             base_class::extract_min_( gp.guard() );
457             return gp;
458         }
459
460         /// Extracts an item with maximal key from the set
461         /**
462             The function searches an item with maximal key, unlinks it, and returns the pointer to item found as \p guarded_ptr.
463             If the skip-list is empty the function returns an empty guarded pointer.
464
465             The item found is freed by garbage collector \p GC automatically
466             when returned \p guarded_ptr object will be destroyed or released.
467             @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
468
469             Usage:
470             \code
471             typedef cds::container::SkipListSet< cds::gc::HP, foo, my_traits >  skip_list;
472             skip_list theList;
473             // ...
474             {
475                 skip_list::guarded_ptr gp( theList.extract_max());
476                 if ( gp ) {
477                     // Deal with gp
478                     //...
479                 }
480                 // Destructor of gp releases internal HP guard and then frees the pointer
481             }
482             \endcode
483         */
484         guarded_ptr extract_max()
485         {
486             guarded_ptr gp;
487             base_class::extract_max_( gp.guard() );
488             return gp;
489         }
490
491         /// Find the \p key
492         /** \anchor cds_nonintrusive_SkipListSet_find_func
493
494             The function searches the item with key equal to \p key and calls the functor \p f for item found.
495             The interface of \p Func functor is:
496             \code
497             struct functor {
498                 void operator()( value_type& item, Q& key );
499             };
500             \endcode
501             where \p item is the item found, \p key is the <tt>find</tt> function argument.
502
503             The functor may change non-key fields of \p item. Note that the functor is only guarantee
504             that \p item cannot be disposed during functor is executing.
505             The functor does not serialize simultaneous access to the set's \p item. If such access is
506             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
507
508             Note the hash functor specified for class \p Traits template parameter
509             should accept a parameter of type \p Q that may be not the same as \p value_type.
510
511             The function returns \p true if \p key is found, \p false otherwise.
512         */
513         template <typename Q, typename Func>
514         bool find( Q& key, Func f )
515         {
516             return base_class::find( key, [&f]( node_type& node, Q& v ) { f( node.m_Value, v ); });
517         }
518         //@cond
519         template <typename Q, typename Func>
520         bool find( Q const& key, Func f )
521         {
522             return base_class::find( key, [&f]( node_type& node, Q& v ) { f( node.m_Value, v ); } );
523         }
524         //@endcond
525
526         /// Finds \p key using \p pred predicate for searching
527         /**
528             The function is an analog of \ref cds_nonintrusive_SkipListSet_find_func "find(Q&, Func)"
529             but \p pred is used for key comparing.
530             \p Less functor has the interface like \p std::less.
531             \p Less must imply the same element order as the comparator used for building the set.
532         */
533         template <typename Q, typename Less, typename Func>
534         bool find_with( Q& key, Less pred, Func f )
535         {
536             CDS_UNUSED( pred );
537             return base_class::find_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >(),
538                 [&f]( node_type& node, Q& v ) { f( node.m_Value, v ); } );
539         }
540         //@cond
541         template <typename Q, typename Less, typename Func>
542         bool find_with( Q const& key, Less pred, Func f )
543         {
544             CDS_UNUSED( pred );
545             return base_class::find_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >(),
546                                           [&f]( node_type& node, Q& v ) { f( node.m_Value, v ); } );
547         }
548         //@endcond
549
550         /// Find \p key
551         /** \anchor cds_nonintrusive_SkipListSet_find_val
552
553             The function searches the item with key equal to \p key
554             and returns \p true if it is found, and \p false otherwise.
555
556             Note the hash functor specified for class \p Traits template parameter
557             should accept a parameter of type \p Q that may be not the same as \ref value_type.
558         */
559         template <typename Q>
560         bool find( Q const& key )
561         {
562             return base_class::find( key );
563         }
564
565         /// Finds \p key using \p pred predicate for searching
566         /**
567             The function is an analog of \ref cds_nonintrusive_SkipListSet_find_val "find(Q const&)"
568             but \p pred is used for key comparing.
569             \p Less functor has the interface like \p std::less.
570             \p Less must imply the same element order as the comparator used for building the set.
571         */
572         template <typename Q, typename Less>
573         bool find_with( Q const& key, Less pred )
574         {
575             CDS_UNUSED( pred );
576             return base_class::find_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >());
577         }
578
579         /// Finds \p key and return the item found
580         /** \anchor cds_nonintrusive_SkipListSet_hp_get
581             The function searches the item with key equal to \p key
582             and returns a guarded pointer to the item found.
583             If \p key is not found the function returns an empty guarded pointer.
584
585             It is safe when a concurrent thread erases the item returned in \p result guarded pointer.
586             In this case the item will be freed later by garbage collector \p GC automatically
587             when \p guarded_ptr object will be destroyed or released.
588             @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
589
590             Usage:
591             \code
592             typedef cds::container::SkipListSet< cds::gc::HP, foo, my_traits >  skip_list;
593             skip_list theList;
594             // ...
595             {
596                 skip_list::guarded_ptr gp( theList.get( 5 ));
597                 if ( theList.get( 5 )) {
598                     // Deal with gp
599                     //...
600                 }
601                 // Destructor of guarded_ptr releases internal HP guard
602             }
603             \endcode
604
605             Note the compare functor specified for class \p Traits template parameter
606             should accept a parameter of type \p Q that can be not the same as \p value_type.
607         */
608         template <typename Q>
609         guarded_ptr get( Q const& key )
610         {
611             guarded_ptr gp;
612             base_class::get_with_( gp.guard(), key, typename base_class::key_comparator() );
613             return gp;
614         }
615
616         /// Finds \p key and return the item found
617         /**
618             The function is an analog of \ref cds_nonintrusive_SkipListSet_hp_get "get(Q const&)"
619             but \p pred is used for comparing the keys.
620
621             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
622             in any order.
623             \p pred must imply the same element order as the comparator used for building the set.
624         */
625         template <typename Q, typename Less>
626         guarded_ptr get_with( Q const& key, Less pred )
627         {
628             CDS_UNUSED( pred );
629             typedef cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >  wrapped_less;
630             guarded_ptr gp;
631             base_class::get_with_( gp.guard(), key, cds::opt::details::make_comparator_from_less< wrapped_less >());
632             return gp;
633         }
634
635         /// Clears the set (not atomic).
636         /**
637             The function deletes all items from the set.
638             The function is not atomic, thus, in multi-threaded environment with parallel insertions
639             this sequence
640             \code
641             set.clear();
642             assert( set.empty() );
643             \endcode
644             the assertion could be raised.
645
646             For each item the \ref disposer provided by \p Traits template parameter will be called.
647         */
648         void clear()
649         {
650             base_class::clear();
651         }
652
653         /// Checks if the set is empty
654         bool empty() const
655         {
656             return base_class::empty();
657         }
658
659         /// Returns item count in the set
660         /**
661             The value returned depends on item counter type provided by \p Traits template parameter.
662             If it is \p atomicity::empty_item_counter this function always returns 0.
663             Therefore, the function is not suitable for checking the set emptiness, use \p empty()
664             member function for this purpose.
665         */
666         size_t size() const
667         {
668             return base_class::size();
669         }
670
671         /// Returns const reference to internal statistics
672         stat const& statistics() const
673         {
674             return base_class::statistics();
675         }
676     };
677
678 }} // namespace cds::container
679
680 #endif // #ifndef CDSLIB_CONTAINER_IMPL_SKIP_LIST_SET_H