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