3 #ifndef CDSLIB_INTRUSIVE_DETAILS_SKIP_LIST_BASE_H
4 #define CDSLIB_INTRUSIVE_DETAILS_SKIP_LIST_BASE_H
6 #include <cds/intrusive/details/base.h>
7 #include <cds/details/marked_ptr.h>
8 #include <cds/algo/bitop.h>
9 #include <cds/os/timer.h>
10 #include <cds/urcu/options.h>
12 namespace cds { namespace intrusive {
13 /// SkipListSet related definitions
14 /** @ingroup cds_intrusive_helper
18 struct implementation_tag;
21 /// The maximum possible height of any skip-list
22 static unsigned int const c_nHeightLimit = 32;
27 - \p GC - garbage collector
28 - \p Tag - a \ref cds_intrusive_hook_tag "tag"
30 template <class GC, typename Tag = opt::none>
34 typedef GC gc; ///< Garbage collector
35 typedef Tag tag; ///< tag
37 typedef cds::details::marked_ptr<node, 1> marked_ptr; ///< marked pointer
38 typedef typename gc::template atomic_marked_ptr< marked_ptr> atomic_marked_ptr; ///< atomic marked pointer specific for GC
40 typedef atomic_marked_ptr tower_item_type;
44 atomic_marked_ptr m_pNext; ///< Next item in bottom-list (list at level 0)
45 unsigned int m_nHeight; ///< Node height (size of m_arrNext array). For node at level 0 the height is 1.
46 atomic_marked_ptr * m_arrNext; ///< Array of next items for levels 1 .. m_nHeight - 1. For node at level 0 \p m_arrNext is \p nullptr
49 /// Constructs a node of height 1 (a bottom-list node)
53 , m_arrNext( nullptr )
56 /// Constructs a node of height \p nHeight
57 void make_tower( unsigned int nHeight, atomic_marked_ptr * nextTower )
59 assert( nHeight > 0 );
60 assert( (nHeight == 1 && nextTower == nullptr) // bottom-list node
61 || (nHeight > 1 && nextTower != nullptr) // node at level of more than 0
64 m_arrNext = nextTower;
69 atomic_marked_ptr * release_tower()
71 atomic_marked_ptr * pTower = m_arrNext;
77 atomic_marked_ptr * get_tower() const
83 /// Access to element of next pointer array
84 atomic_marked_ptr& next( unsigned int nLevel )
86 assert( nLevel < height() );
87 assert( nLevel == 0 || (nLevel > 0 && m_arrNext != nullptr) );
89 return nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
92 /// Access to element of next pointer array (const version)
93 atomic_marked_ptr const& next( unsigned int nLevel ) const
95 assert( nLevel < height() );
96 assert( nLevel == 0 || nLevel > 0 && m_arrNext != nullptr );
98 return nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
101 /// Access to element of next pointer array (same as \ref next function)
102 atomic_marked_ptr& operator[]( unsigned int nLevel )
104 return next( nLevel );
107 /// Access to element of next pointer array (same as \ref next function)
108 atomic_marked_ptr const& operator[]( unsigned int nLevel ) const
110 return next( nLevel );
113 /// Height of the node
114 unsigned int height() const
119 /// Clears internal links
122 assert( m_arrNext == nullptr );
123 m_pNext.store( marked_ptr(), atomics::memory_order_release );
127 bool is_cleared() const
129 return m_pNext == atomic_marked_ptr()
130 && m_arrNext == nullptr
138 struct default_hook {
139 typedef undefined_gc gc;
140 typedef opt::none tag;
145 template < typename HookType, typename... Options>
148 typedef typename opt::make_options< default_hook, Options...>::type options;
149 typedef typename options::gc gc;
150 typedef typename options::tag tag;
151 typedef node<gc, tag> node_type;
152 typedef HookType hook_type;
159 - \p opt::gc - garbage collector
160 - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
162 template < typename... Options >
163 struct base_hook: public hook< opt::base_hook_tag, Options... >
168 \p MemberOffset defines offset in bytes of \ref node member into your structure.
169 Use \p offsetof macro to define \p MemberOffset
172 - \p opt::gc - garbage collector
173 - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
175 template < size_t MemberOffset, typename... Options >
176 struct member_hook: public hook< opt::member_hook_tag, Options... >
179 static const size_t c_nMemberOffset = MemberOffset;
185 \p NodeTraits defines type traits for node.
186 See \ref node_traits for \p NodeTraits interface description
189 - \p opt::gc - garbage collector
190 - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
192 template <typename NodeTraits, typename... Options >
193 struct traits_hook: public hook< opt::traits_hook_tag, Options... >
196 typedef NodeTraits node_traits;
200 /// Option specifying random level generator
202 The random level generator is an important part of skip-list algorithm.
203 The node height in the skip-list have a probabilistic distribution
204 where half of the nodes that have level \p i pointers also have level <tt>i+1</tt> pointers
206 The random level generator should provide such distribution.
208 The \p Type functor interface is:
210 struct random_generator {
211 static unsigned int const c_nUpperBound = 32;
213 unsigned int operator()();
218 - \p c_nUpperBound - constant that specifies the upper bound of random number generated.
219 The generator produces a number from range <tt>[0 .. c_nUpperBound)</tt> (upper bound excluded).
220 \p c_nUpperBound must be no more than 32.
221 - <tt>random_generator()</tt> - the constructor of generator object initialises the generator instance (its internal state).
222 - <tt>unsigned int operator()()</tt> - the main generating function. Returns random level from range 0..31.
224 Stateful generators are supported.
226 Available \p Type implementations:
227 - \p skip_list::xorshift
228 - \p skip_list::turbo_pascal
230 template <typename Type>
231 struct random_level_generator {
233 template <typename Base>
234 struct pack: public Base
236 typedef Type random_level_generator;
241 /// Xor-shift random level generator
243 The simplest of the generators described in George
244 Marsaglia's "Xorshift RNGs" paper. This is not a high-quality
245 generator but is acceptable for skip-list.
247 The random generator should return numbers from range [0..31].
249 From Doug Lea's ConcurrentSkipListMap.java.
253 atomics::atomic<unsigned int> m_nSeed;
256 /// The upper bound of generator's return value. The generator produces random number in range <tt>[0..c_nUpperBound)</tt>
257 static unsigned int const c_nUpperBound = c_nHeightLimit;
259 /// Initializes the generator instance
262 m_nSeed.store( (unsigned int) cds::OS::Timer::random_seed(), atomics::memory_order_relaxed );
265 /// Main generator function
266 unsigned int operator()()
268 /* ConcurrentSkipListMap.java
269 private int randomLevel() {
273 randomSeed = x ^= x << 5;
274 if ((x & 0x80000001) != 0) // test highest and lowest bits
277 while (((x >>>= 1) & 1) != 0) ++level;
281 unsigned int x = m_nSeed.load( atomics::memory_order_relaxed );
285 m_nSeed.store( x, atomics::memory_order_relaxed );
286 unsigned int nLevel = ((x & 0x00000001) != 0) ? 0 : cds::bitop::LSB( (~(x >> 1)) & 0x7FFFFFFF );
287 assert( nLevel < c_nUpperBound );
292 /// Turbo-pascal random level generator
294 This uses a cheap pseudo-random function that was used in Turbo Pascal.
296 The random generator should return numbers from range [0..31].
298 From Doug Lea's ConcurrentSkipListMap.java.
303 atomics::atomic<unsigned int> m_nSeed;
306 /// The upper bound of generator's return value. The generator produces random number in range <tt>[0..c_nUpperBound)</tt>
307 static unsigned int const c_nUpperBound = c_nHeightLimit;
309 /// Initializes the generator instance
312 m_nSeed.store( (unsigned int) cds::OS::Timer::random_seed(), atomics::memory_order_relaxed );
315 /// Main generator function
316 unsigned int operator()()
319 private int randomLevel() {
322 randomSeed = r * 134775813 + 1;
324 while ((r <<= 1) > 0)
331 The low bits are apparently not very random (the original used only
332 upper 16 bits) so we traverse from highest bit down (i.e., test
333 sign), thus hardly ever use lower bits.
335 unsigned int x = m_nSeed.load( atomics::memory_order_relaxed ) * 134775813 + 1;
336 m_nSeed.store( x, atomics::memory_order_relaxed );
337 unsigned int nLevel = ( x & 0x80000000 ) ? (31 - cds::bitop::MSBnz( (x & 0x7FFFFFFF) | 1 )) : 0;
338 assert( nLevel < c_nUpperBound );
343 /// \p SkipListSet internal statistics
344 template <typename EventCounter = cds::atomicity::event_counter>
346 typedef EventCounter event_counter ; ///< Event counter type
348 event_counter m_nNodeHeightAdd[c_nHeightLimit] ; ///< Count of added node of each height
349 event_counter m_nNodeHeightDel[c_nHeightLimit] ; ///< Count of deleted node of each height
350 event_counter m_nInsertSuccess ; ///< Count of success insertion
351 event_counter m_nInsertFailed ; ///< Count of failed insertion
352 event_counter m_nInsertRetries ; ///< Count of unsuccessful retries of insertion
353 event_counter m_nEnsureExist ; ///< Count of \p ensure call for existed node
354 event_counter m_nEnsureNew ; ///< Count of \p ensure call for new node
355 event_counter m_nUnlinkSuccess ; ///< Count of successful call of \p unlink
356 event_counter m_nUnlinkFailed ; ///< Count of failed call of \p unlink
357 event_counter m_nEraseSuccess ; ///< Count of successful call of \p erase
358 event_counter m_nEraseFailed ; ///< Count of failed call of \p erase
359 event_counter m_nFindFastSuccess ; ///< Count of successful call of \p find and all derivatives (via fast-path)
360 event_counter m_nFindFastFailed ; ///< Count of failed call of \p find and all derivatives (via fast-path)
361 event_counter m_nFindSlowSuccess ; ///< Count of successful call of \p find and all derivatives (via slow-path)
362 event_counter m_nFindSlowFailed ; ///< Count of failed call of \p find and all derivatives (via slow-path)
363 event_counter m_nRenewInsertPosition ; ///< Count of renewing position events while inserting
364 event_counter m_nLogicDeleteWhileInsert ; ///< Count of events "The node has been logically deleted while inserting"
365 event_counter m_nNotFoundWhileInsert ; ///< Count of events "Inserting node is not found"
366 event_counter m_nFastErase ; ///< Fast erase event counter
367 event_counter m_nFastExtract ; ///< Fast extract event counter
368 event_counter m_nSlowErase ; ///< Slow erase event counter
369 event_counter m_nSlowExtract ; ///< Slow extract event counter
370 event_counter m_nExtractSuccess ; ///< Count of successful call of \p extract
371 event_counter m_nExtractFailed ; ///< Count of failed call of \p extract
372 event_counter m_nExtractRetries ; ///< Count of retries of \p extract call
373 event_counter m_nExtractMinSuccess ; ///< Count of successful call of \p extract_min
374 event_counter m_nExtractMinFailed ; ///< Count of failed call of \p extract_min
375 event_counter m_nExtractMinRetries ; ///< Count of retries of \p extract_min call
376 event_counter m_nExtractMaxSuccess ; ///< Count of successful call of \p extract_max
377 event_counter m_nExtractMaxFailed ; ///< Count of failed call of \p extract_max
378 event_counter m_nExtractMaxRetries ; ///< Count of retries of \p extract_max call
379 event_counter m_nEraseWhileFind ; ///< Count of erased item while searching
380 event_counter m_nExtractWhileFind ; ///< Count of extracted item while searching (RCU only)
383 void onAddNode( unsigned int nHeight )
385 assert( nHeight > 0 && nHeight <= sizeof(m_nNodeHeightAdd) / sizeof(m_nNodeHeightAdd[0]));
386 ++m_nNodeHeightAdd[nHeight - 1];
388 void onRemoveNode( unsigned int nHeight )
390 assert( nHeight > 0 && nHeight <= sizeof(m_nNodeHeightDel) / sizeof(m_nNodeHeightDel[0]));
391 ++m_nNodeHeightDel[nHeight - 1];
394 void onInsertSuccess() { ++m_nInsertSuccess ; }
395 void onInsertFailed() { ++m_nInsertFailed ; }
396 void onInsertRetry() { ++m_nInsertRetries ; }
397 void onEnsureExist() { ++m_nEnsureExist ; }
398 void onEnsureNew() { ++m_nEnsureNew ; }
399 void onUnlinkSuccess() { ++m_nUnlinkSuccess ; }
400 void onUnlinkFailed() { ++m_nUnlinkFailed ; }
401 void onEraseSuccess() { ++m_nEraseSuccess ; }
402 void onEraseFailed() { ++m_nEraseFailed ; }
403 void onFindFastSuccess() { ++m_nFindFastSuccess ; }
404 void onFindFastFailed() { ++m_nFindFastFailed ; }
405 void onFindSlowSuccess() { ++m_nFindSlowSuccess ; }
406 void onFindSlowFailed() { ++m_nFindSlowFailed ; }
407 void onEraseWhileFind() { ++m_nEraseWhileFind ; }
408 void onExtractWhileFind() { ++m_nExtractWhileFind ; }
409 void onRenewInsertPosition() { ++m_nRenewInsertPosition; }
410 void onLogicDeleteWhileInsert() { ++m_nLogicDeleteWhileInsert; }
411 void onNotFoundWhileInsert() { ++m_nNotFoundWhileInsert; }
412 void onFastErase() { ++m_nFastErase; }
413 void onFastExtract() { ++m_nFastExtract; }
414 void onSlowErase() { ++m_nSlowErase; }
415 void onSlowExtract() { ++m_nSlowExtract; }
416 void onExtractSuccess() { ++m_nExtractSuccess; }
417 void onExtractFailed() { ++m_nExtractFailed; }
418 void onExtractRetry() { ++m_nExtractRetries; }
419 void onExtractMinSuccess() { ++m_nExtractMinSuccess; }
420 void onExtractMinFailed() { ++m_nExtractMinFailed; }
421 void onExtractMinRetry() { ++m_nExtractMinRetries; }
422 void onExtractMaxSuccess() { ++m_nExtractMaxSuccess; }
423 void onExtractMaxFailed() { ++m_nExtractMaxFailed; }
424 void onExtractMaxRetry() { ++m_nExtractMaxRetries; }
429 /// \p SkipListSet empty internal statistics
432 void onAddNode( unsigned int /*nHeight*/ ) const {}
433 void onRemoveNode( unsigned int /*nHeight*/ ) const {}
434 void onInsertSuccess() const {}
435 void onInsertFailed() const {}
436 void onInsertRetry() const {}
437 void onEnsureExist() const {}
438 void onEnsureNew() const {}
439 void onUnlinkSuccess() const {}
440 void onUnlinkFailed() const {}
441 void onEraseSuccess() const {}
442 void onEraseFailed() const {}
443 void onFindFastSuccess() const {}
444 void onFindFastFailed() const {}
445 void onFindSlowSuccess() const {}
446 void onFindSlowFailed() const {}
447 void onEraseWhileFind() const {}
448 void onExtractWhileFind() const {}
449 void onRenewInsertPosition() const {}
450 void onLogicDeleteWhileInsert() const {}
451 void onNotFoundWhileInsert() const {}
452 void onFastErase() const {}
453 void onFastExtract() const {}
454 void onSlowErase() const {}
455 void onSlowExtract() const {}
456 void onExtractSuccess() const {}
457 void onExtractFailed() const {}
458 void onExtractRetry() const {}
459 void onExtractMinSuccess() const {}
460 void onExtractMinFailed() const {}
461 void onExtractMinRetry() const {}
462 void onExtractMaxSuccess() const {}
463 void onExtractMaxFailed() const {}
464 void onExtractMaxRetry() const {}
470 // For internal use only!!!
471 template <typename Type>
472 struct internal_node_builder {
473 template <typename Base>
474 struct pack: public Base
476 typedef Type internal_node_builder;
481 /// \p SkipListSet traits
486 Possible values are: \p skip_list::base_hook, \p skip_list::member_hook, \p skip_list::traits_hook.
488 typedef base_hook<> hook;
490 /// Key comparison functor
492 No default functor is provided. If the option is not specified, the \p less is used.
494 typedef opt::none compare;
496 /// specifies binary predicate used for key compare.
498 Default is \p std::less<T>.
500 typedef opt::none less;
504 The functor used for dispose removed items. Default is \p opt::v::empty_disposer.
506 typedef opt::v::empty_disposer disposer;
510 The type for item counting feature.
511 By default, item counting is disabled (\p atomicity::empty_item_counter)
512 \p atomicity::item_counter enables it.
514 typedef atomicity::empty_item_counter item_counter;
516 /// C++ memory ordering model
518 List of available memory ordering see \p opt::memory_model
520 typedef opt::v::relaxed_ordering memory_model;
522 /// Random level generator
524 The random level generator is an important part of skip-list algorithm.
525 The node height in the skip-list have a probabilistic distribution
526 where half of the nodes that have level \p i pointers also have level <tt>i+1</tt> pointers
527 (i = 0..30). So, the height of a node is in range [0..31].
529 See \p skip_list::random_level_generator option setter.
531 typedef turbo_pascal random_level_generator;
535 Although the skip-list is an intrusive container,
536 an allocator should be provided to maintain variable randomly-calculated height of the node
537 since the node can contain up to 32 next pointers.
538 The allocator specified is used to allocate an array of next pointers
539 for nodes which height is more than 1.
541 typedef CDS_DEFAULT_ALLOCATOR allocator;
543 /// back-off strategy
545 If the option is not specified, the \p cds::backoff::Default is used.
547 typedef cds::backoff::Default back_off;
549 /// Internal statistics
551 By default, internal statistics is disabled (\p skip_list::empty_stat).
552 Use \p skip_list::stat to enable it.
554 typedef empty_stat stat;
556 /// RCU deadlock checking policy (only for \ref cds_intrusive_SkipListSet_rcu "RCU-based SkipListSet")
558 List of available options see \p opt::rcu_check_deadlock
560 typedef opt::v::rcu_throw_deadlock rcu_check_deadlock;
563 // For internal use only!!!
564 typedef opt::none internal_node_builder;
568 /// Metafunction converting option list to \p SkipListSet traits
571 - \p opt::hook - hook used. Possible values are: \p skip_list::base_hook, \p skip_list::member_hook, \p skip_list::traits_hook.
572 If the option is not specified, <tt>skip_list::base_hook<></tt> and \p gc::HP is used.
573 - \p opt::compare - key comparison functor. No default functor is provided.
574 If the option is not specified, the \p opt::less is used.
575 - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less<T>.
576 - \p opt::disposer - the functor used for dispose removed items. Default is \p opt::v::empty_disposer. Due the nature
577 of GC schema the disposer may be called asynchronously.
578 - \p opt::item_counter - the type of item counting feature. Default is disabled, i.e. \p atomicity::empty_item_counter.
579 To enable it use \p atomicity::item_counter
580 - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
581 or \p opt::v::sequential_consistent (sequentially consisnent memory model).
582 - \p skip_list::random_level_generator - random level generator. Can be \p skip_list::xorshift,
583 \p skip_list::turbo_pascal (the default) or
584 user-provided one. See \p skip_list::random_level_generator option description for explanation.
585 - \p opt::allocator - although the skip-list is an intrusive container,
586 an allocator should be provided to maintain variable randomly-calculated height of the node
587 since the node can contain up to 32 next pointers. The allocator option is used to allocate an array of next pointers
588 for nodes which height is more than 1. Default is \ref CDS_DEFAULT_ALLOCATOR.
589 - \p opt::back_off - back-off strategy, default is \รง cds::backoff::Default.
590 - \p opt::stat - internal statistics. By default, it is disabled (\p skip_list::empty_stat).
591 To enable it use \p skip_list::stat
593 template <typename... Options>
595 # ifdef CDS_DOXYGEN_INVOKED
596 typedef implementation_defined type ; ///< Metafunction result
598 typedef typename cds::opt::make_options<
599 typename cds::opt::find_type_traits< traits, Options... >::type
607 template <typename Node>
608 class head_node: public Node
610 typedef Node node_type;
611 typename node_type::atomic_marked_ptr m_Tower[skip_list::c_nHeightLimit];
614 head_node( unsigned int nHeight )
616 for ( size_t i = 0; i < sizeof(m_Tower) / sizeof(m_Tower[0]); ++i )
617 m_Tower[i].store( typename node_type::marked_ptr(), atomics::memory_order_relaxed );
619 node_type::make_tower( nHeight, m_Tower );
622 node_type * head() const
624 return const_cast<node_type *>( static_cast<node_type const *>(this));
628 template <typename NodeType, typename AtomicNodePtr, typename Alloc>
629 struct intrusive_node_builder
631 typedef NodeType node_type;
632 typedef AtomicNodePtr atomic_node_ptr;
633 typedef Alloc allocator_type;
635 typedef cds::details::Allocator< atomic_node_ptr, allocator_type > tower_allocator;
637 template <typename RandomGen>
638 static node_type * make_tower( node_type * pNode, RandomGen& gen )
640 return make_tower( pNode, gen() + 1 );
643 static node_type * make_tower( node_type * pNode, unsigned int nHeight )
646 pNode->make_tower( nHeight, tower_allocator().NewArray( nHeight - 1, nullptr ) );
650 static void dispose_tower( node_type * pNode )
652 unsigned int nHeight = pNode->height();
654 tower_allocator().Delete( pNode->release_tower(), nHeight );
657 struct node_disposer {
658 void operator()( node_type * pNode )
660 dispose_tower( pNode );
665 // Forward declaration
666 template <class GC, typename NodeTraits, typename BackOff, bool IsConst>
669 } // namespace details
672 } // namespace skip_list
674 // Forward declaration
675 template <class GC, typename T, typename Traits = skip_list::traits >
678 }} // namespace cds::intrusive
680 #endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_SKIP_LIST_BASE_H