From: khizmax Date: Sat, 27 Sep 2014 15:45:04 +0000 (+0400) Subject: Moce skip_list_base.h from cds/intrusive tocds/intrusive/details X-Git-Tag: v2.0.0~275 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=6f0566a4d0f7d7de00dbac445cb2d0fd01ebef37;p=libcds.git Moce skip_list_base.h from cds/intrusive tocds/intrusive/details --- diff --git a/cds/container/skip_list_base.h b/cds/container/skip_list_base.h index bf33f0cc..3cc591b5 100644 --- a/cds/container/skip_list_base.h +++ b/cds/container/skip_list_base.h @@ -3,7 +3,7 @@ #ifndef __CDS_CONTAINER_SKIP_LIST_BASE_H #define __CDS_CONTAINER_SKIP_LIST_BASE_H -#include +#include #include namespace cds { namespace container { diff --git a/cds/intrusive/details/skip_list_base.h b/cds/intrusive/details/skip_list_base.h new file mode 100644 index 00000000..257e2bba --- /dev/null +++ b/cds/intrusive/details/skip_list_base.h @@ -0,0 +1,654 @@ +//$$CDS-header$$ + +#ifndef __CDS_INTRUSIVE_DETAILS_SKIP_LIST_BASE_H +#define __CDS_INTRUSIVE_DETAILS_SKIP_LIST_BASE_H + +#include +#include +#include +#include +#include + + +namespace cds { namespace intrusive { + /// SkipListSet related definitions + /** @ingroup cds_intrusive_helper + */ + namespace skip_list { + + /// The maximum possible height of any skip-list + static unsigned int const c_nHeightLimit = 32; + + /// Skip list node + /** + Template parameters: + - GC - garbage collector + - Tag - a tag used to distinguish between different implementation. An incomplete type may be used as a tag. + */ + template + class node { + public: + typedef GC gc ; ///< Garbage collector + typedef Tag tag ; ///< tag + + typedef cds::details::marked_ptr marked_ptr ; ///< marked pointer + typedef typename gc::template atomic_marked_ptr< marked_ptr> atomic_marked_ptr ; ///< atomic marked pointer specific for GC + //@cond + typedef atomic_marked_ptr tower_item_type; + //@endcond + + protected: + atomic_marked_ptr m_pNext ; ///< Next item in bottom-list (list at level 0) + unsigned int m_nHeight ; ///< Node height (size of m_arrNext array). For node at level 0 the height is 1. + 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 + + public: + /// Constructs a node of height 1 (a bottom-list node) + node() + : m_pNext( nullptr ) + , m_nHeight(1) + , m_arrNext( nullptr ) + {} + + /// Constructs a node of height \p nHeight + void make_tower( unsigned int nHeight, atomic_marked_ptr * nextTower ) + { + assert( nHeight > 0 ); + assert( (nHeight == 1 && nextTower == nullptr) // bottom-list node + || (nHeight > 1 && nextTower != nullptr) // node at level of more than 0 + ); + + m_arrNext = nextTower; + m_nHeight = nHeight; + } + + //@cond + atomic_marked_ptr * release_tower() + { + atomic_marked_ptr * pTower = m_arrNext; + m_arrNext = nullptr; + m_nHeight = 1; + return pTower; + } + + atomic_marked_ptr * get_tower() const + { + return m_arrNext; + } + //@endcond + + /// Access to element of next pointer array + atomic_marked_ptr& next( unsigned int nLevel ) + { + assert( nLevel < height() ); + assert( nLevel == 0 || (nLevel > 0 && m_arrNext != nullptr) ); + + return nLevel ? m_arrNext[ nLevel - 1] : m_pNext; + } + + /// Access to element of next pointer array (const version) + atomic_marked_ptr const& next( unsigned int nLevel ) const + { + assert( nLevel < height() ); + assert( nLevel == 0 || nLevel > 0 && m_arrNext != nullptr ); + + return nLevel ? m_arrNext[ nLevel - 1] : m_pNext; + } + + /// Access to element of next pointer array (same as \ref next function) + atomic_marked_ptr& operator[]( unsigned int nLevel ) + { + return next( nLevel ); + } + + /// Access to element of next pointer array (same as \ref next function) + atomic_marked_ptr const& operator[]( unsigned int nLevel ) const + { + return next( nLevel ); + } + + /// Height of the node + unsigned int height() const + { + return m_nHeight; + } + + /// Clears internal links + void clear() + { + assert( m_arrNext == nullptr ); + m_pNext.store( marked_ptr(), atomics::memory_order_release ); + } + + //@cond + bool is_cleared() const + { + return m_pNext == atomic_marked_ptr() + && m_arrNext == nullptr + && m_nHeight <= 1 +; + } + //@endcond + }; + + //@cond + struct undefined_gc; + struct default_hook { + typedef undefined_gc gc; + typedef opt::none tag; + }; + //@endcond + + //@cond + template < typename HookType, typename... Options> + struct hook + { + typedef typename opt::make_options< default_hook, Options...>::type options; + typedef typename options::gc gc; + typedef typename options::tag tag; + typedef node node_type; + typedef HookType hook_type; + }; + //@endcond + + /// Base hook + /** + \p Options are: + - opt::gc - garbage collector used. + - opt::tag - a tag + */ + template < typename... Options > + struct base_hook: public hook< opt::base_hook_tag, Options... > + {}; + + /// Member hook + /** + \p MemberOffset defines offset in bytes of \ref node member into your structure. + Use \p offsetof macro to define \p MemberOffset + + \p Options are: + - opt::gc - garbage collector used. + - opt::tag - a tag + */ + template < size_t MemberOffset, typename... Options > + struct member_hook: public hook< opt::member_hook_tag, Options... > + { + //@cond + static const size_t c_nMemberOffset = MemberOffset; + //@endcond + }; + + /// Traits hook + /** + \p NodeTraits defines type traits for node. + See \ref node_traits for \p NodeTraits interface description + + \p Options are: + - opt::gc - garbage collector used. + - opt::tag - a tag + */ + template + struct traits_hook: public hook< opt::traits_hook_tag, Options... > + { + //@cond + typedef NodeTraits node_traits; + //@endcond + }; + + /// Option specifying random level generator + /** + The random level generator is an important part of skip-list algorithm. + The node height in the skip-list have a probabilistic distribution + where half of the nodes that have level \p i pointers also have level i+1 pointers + (i = 0..30). + The random level generator should provide such distribution. + + The \p Type functor interface is: + \code + struct random_generator { + static unsigned int const c_nUpperBound = 32; + random_generator(); + unsigned int operator()(); + }; + \endcode + + where + - \p c_nUpperBound - constant that specifies the upper bound of random number generated. + The generator produces a number from range [0 .. c_nUpperBound) (upper bound excluded). + \p c_nUpperBound must be no more than 32. + - random_generator() - the constructor of generator object initialises the generator instance (its internal state). + - unsigned int operator()() - the main generating function. Returns random level from range 0..31. + + Stateful generators are supported. + + Available \p Type implementations: + - \ref xorshift + - \ref turbo_pascal + */ + template + struct random_level_generator { + //@cond + template + struct pack: public Base + { + typedef Type random_level_generator; + }; + //@endcond + }; + + /// Xor-shift random level generator + /** + The simplest of the generators described in George + Marsaglia's "Xorshift RNGs" paper. This is not a high-quality + generator but is acceptable for skip-list. + + The random generator should return numbers from range [0..31]. + + From Doug Lea's ConcurrentSkipListMap.java. + */ + class xorshift { + //@cond + atomics::atomic m_nSeed; + //@endcond + public: + /// The upper bound of generator's return value. The generator produces random number in range [0..c_nUpperBound) + static unsigned int const c_nUpperBound = c_nHeightLimit; + + /// Initializes the generator instance + xorshift() + { + m_nSeed.store( (unsigned int) cds::OS::Timer::random_seed(), atomics::memory_order_relaxed ); + } + + /// Main generator function + unsigned int operator()() + { + /* ConcurrentSkipListMap.java + private int randomLevel() { + int x = randomSeed; + x ^= x << 13; + x ^= x >>> 17; + randomSeed = x ^= x << 5; + if ((x & 0x80000001) != 0) // test highest and lowest bits + return 0; + int level = 1; + while (((x >>>= 1) & 1) != 0) ++level; + return level; + } + */ + unsigned int x = m_nSeed.load( atomics::memory_order_relaxed ); + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + m_nSeed.store( x, atomics::memory_order_relaxed ); + unsigned int nLevel = ((x & 0x00000001) != 0) ? 0 : cds::bitop::LSB( (~(x >> 1)) & 0x7FFFFFFF ); + assert( nLevel < c_nUpperBound ); + return nLevel; + } + }; + + /// Turbo-pascal random level generator + /** + This uses a cheap pseudo-random function that was used in Turbo Pascal. + + The random generator should return numbers from range [0..31]. + + From Doug Lea's ConcurrentSkipListMap.java. + */ + class turbo_pascal + { + //@cond + atomics::atomic m_nSeed; + //@endcond + public: + /// The upper bound of generator's return value. The generator produces random number in range [0..c_nUpperBound) + static unsigned int const c_nUpperBound = c_nHeightLimit; + + /// Initializes the generator instance + turbo_pascal() + { + m_nSeed.store( (unsigned int) cds::OS::Timer::random_seed(), atomics::memory_order_relaxed ); + } + + /// Main generator function + unsigned int operator()() + { + /* + private int randomLevel() { + int level = 0; + int r = randomSeed; + randomSeed = r * 134775813 + 1; + if (r < 0) { + while ((r <<= 1) > 0) + ++level; + } + return level; + } + */ + /* + The low bits are apparently not very random (the original used only + upper 16 bits) so we traverse from highest bit down (i.e., test + sign), thus hardly ever use lower bits. + */ + unsigned int x = m_nSeed.load( atomics::memory_order_relaxed ) * 134775813 + 1; + m_nSeed.store( x, atomics::memory_order_relaxed ); + unsigned int nLevel = ( x & 0x80000000 ) ? (31 - cds::bitop::MSBnz( (x & 0x7FFFFFFF) | 1 )) : 0; + assert( nLevel < c_nUpperBound ); + return nLevel; + } + }; + + /// SkipListSet internal statistics + template + struct stat { + typedef EventCounter event_counter ; ///< Event counter type + + event_counter m_nNodeHeightAdd[c_nHeightLimit] ; ///< Count of added node of each height + event_counter m_nNodeHeightDel[c_nHeightLimit] ; ///< Count of deleted node of each height + event_counter m_nInsertSuccess ; ///< Count of success insertion + event_counter m_nInsertFailed ; ///< Count of failed insertion + event_counter m_nInsertRetries ; ///< Count of unsuccessful retries of insertion + event_counter m_nEnsureExist ; ///< Count of \p ensure call for existed node + event_counter m_nEnsureNew ; ///< Count of \p ensure call for new node + event_counter m_nUnlinkSuccess ; ///< Count of successful call of \p unlink + event_counter m_nUnlinkFailed ; ///< Count of failed call of \p unlink + event_counter m_nEraseSuccess ; ///< Count of successful call of \p erase + event_counter m_nEraseFailed ; ///< Count of failed call of \p erase + event_counter m_nFindFastSuccess ; ///< Count of successful call of \p find and all derivatives (via fast-path) + event_counter m_nFindFastFailed ; ///< Count of failed call of \p find and all derivatives (via fast-path) + event_counter m_nFindSlowSuccess ; ///< Count of successful call of \p find and all derivatives (via slow-path) + event_counter m_nFindSlowFailed ; ///< Count of failed call of \p find and all derivatives (via slow-path) + event_counter m_nRenewInsertPosition ; ///< Count of renewing position events while inserting + event_counter m_nLogicDeleteWhileInsert ; ///< Count of events "The node has been logically deleted while inserting" + event_counter m_nNotFoundWhileInsert ; ///< Count of events "Inserting node is not found" + event_counter m_nFastErase ; ///< Fast erase event counter + event_counter m_nFastExtract ; ///< Fast extract event counter + event_counter m_nSlowErase ; ///< Slow erase event counter + event_counter m_nSlowExtract ; ///< Slow extract event counter + event_counter m_nExtractSuccess ; ///< Count of successful call of \p extract + event_counter m_nExtractFailed ; ///< Count of failed call of \p extract + event_counter m_nExtractRetries ; ///< Count of retries of \p extract call + event_counter m_nExtractMinSuccess ; ///< Count of successful call of \p extract_min + event_counter m_nExtractMinFailed ; ///< Count of failed call of \p extract_min + event_counter m_nExtractMinRetries ; ///< Count of retries of \p extract_min call + event_counter m_nExtractMaxSuccess ; ///< Count of successful call of \p extract_max + event_counter m_nExtractMaxFailed ; ///< Count of failed call of \p extract_max + event_counter m_nExtractMaxRetries ; ///< Count of retries of \p extract_max call + event_counter m_nEraseWhileFind ; ///< Count of erased item while searching + event_counter m_nExtractWhileFind ; ///< Count of extracted item while searching (RCU only) + + //@cond + void onAddNode( unsigned int nHeight ) + { + assert( nHeight > 0 && nHeight <= sizeof(m_nNodeHeightAdd) / sizeof(m_nNodeHeightAdd[0])); + ++m_nNodeHeightAdd[nHeight - 1]; + } + void onRemoveNode( unsigned int nHeight ) + { + assert( nHeight > 0 && nHeight <= sizeof(m_nNodeHeightDel) / sizeof(m_nNodeHeightDel[0])); + ++m_nNodeHeightDel[nHeight - 1]; + } + + void onInsertSuccess() { ++m_nInsertSuccess ; } + void onInsertFailed() { ++m_nInsertFailed ; } + void onInsertRetry() { ++m_nInsertRetries ; } + void onEnsureExist() { ++m_nEnsureExist ; } + void onEnsureNew() { ++m_nEnsureNew ; } + void onUnlinkSuccess() { ++m_nUnlinkSuccess ; } + void onUnlinkFailed() { ++m_nUnlinkFailed ; } + void onEraseSuccess() { ++m_nEraseSuccess ; } + void onEraseFailed() { ++m_nEraseFailed ; } + void onFindFastSuccess() { ++m_nFindFastSuccess ; } + void onFindFastFailed() { ++m_nFindFastFailed ; } + void onFindSlowSuccess() { ++m_nFindSlowSuccess ; } + void onFindSlowFailed() { ++m_nFindSlowFailed ; } + void onEraseWhileFind() { ++m_nEraseWhileFind ; } + void onExtractWhileFind() { ++m_nExtractWhileFind ; } + void onRenewInsertPosition() { ++m_nRenewInsertPosition; } + void onLogicDeleteWhileInsert() { ++m_nLogicDeleteWhileInsert; } + void onNotFoundWhileInsert() { ++m_nNotFoundWhileInsert; } + void onFastErase() { ++m_nFastErase; } + void onFastExtract() { ++m_nFastExtract; } + void onSlowErase() { ++m_nSlowErase; } + void onSlowExtract() { ++m_nSlowExtract; } + void onExtractSuccess() { ++m_nExtractSuccess; } + void onExtractFailed() { ++m_nExtractFailed; } + void onExtractRetry() { ++m_nExtractRetries; } + void onExtractMinSuccess() { ++m_nExtractMinSuccess; } + void onExtractMinFailed() { ++m_nExtractMinFailed; } + void onExtractMinRetry() { ++m_nExtractMinRetries; } + void onExtractMaxSuccess() { ++m_nExtractMaxSuccess; } + void onExtractMaxFailed() { ++m_nExtractMaxFailed; } + void onExtractMaxRetry() { ++m_nExtractMaxRetries; } + + //@endcond + }; + + /// SkipListSet empty internal statistics + struct empty_stat { + //@cond + void onAddNode( unsigned int nHeight ) const {} + void onRemoveNode( unsigned int nHeight ) const {} + void onInsertSuccess() const {} + void onInsertFailed() const {} + void onInsertRetry() const {} + void onEnsureExist() const {} + void onEnsureNew() const {} + void onUnlinkSuccess() const {} + void onUnlinkFailed() const {} + void onEraseSuccess() const {} + void onEraseFailed() const {} + void onFindFastSuccess() const {} + void onFindFastFailed() const {} + void onFindSlowSuccess() const {} + void onFindSlowFailed() const {} + void onEraseWhileFind() const {} + void onExtractWhileFind() const {} + void onRenewInsertPosition() const {} + void onLogicDeleteWhileInsert() const {} + void onNotFoundWhileInsert() const {} + void onFastErase() const {} + void onFastExtract() const {} + void onSlowErase() const {} + void onSlowExtract() const {} + void onExtractSuccess() const {} + void onExtractFailed() const {} + void onExtractRetry() const {} + void onExtractMinSuccess() const {} + void onExtractMinFailed() const {} + void onExtractMinRetry() const {} + void onExtractMaxSuccess() const {} + void onExtractMaxFailed() const {} + void onExtractMaxRetry() const {} + + //@endcond + }; + + //@cond + // For internal use only!!! + template + struct internal_node_builder { + template + struct pack: public Base + { + typedef Type internal_node_builder; + }; + }; + //@endcond + + /// Type traits for SkipListSet class + struct type_traits + { + /// Hook used + /** + Possible values are: skip_list::base_hook, skip_list::member_hook, skip_list::traits_hook. + */ + typedef base_hook<> hook; + + /// Key comparison functor + /** + No default functor is provided. If the option is not specified, the \p less is used. + */ + typedef opt::none compare; + + /// specifies binary predicate used for key compare. + /** + Default is \p std::less. + */ + typedef opt::none less; + + /// Disposer + /** + The functor used for dispose removed items. Default is opt::v::empty_disposer. + */ + typedef opt::v::empty_disposer disposer; + + /// Item counter + /** + The type for item counting feature. + Default is no item counter (\ref atomicity::empty_item_counter) + */ + typedef atomicity::empty_item_counter item_counter; + + /// C++ memory ordering model + /** + List of available memory ordering see opt::memory_model + */ + typedef opt::v::relaxed_ordering memory_model; + + /// Random level generator + /** + The random level generator is an important part of skip-list algorithm. + The node height in the skip-list have a probabilistic distribution + where half of the nodes that have level \p i pointers also have level i+1 pointers + (i = 0..30). So, the height of a node is in range [0..31]. + + See skip_list::random_level_generator option setter. + */ + typedef turbo_pascal random_level_generator; + + /// Allocator + /** + Although the skip-list is an intrusive container, + an allocator should be provided to maintain variable randomly-calculated height of the node + since the node can contain up to 32 next pointers. + The allocator specified is used to allocate an array of next pointers + for nodes which height is more than 1. + */ + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// back-off strategy used + /** + If the option is not specified, the cds::backoff::Default is used. + */ + typedef cds::backoff::Default back_off; + + /// Internal statistics + typedef empty_stat stat; + + /// RCU deadlock checking policy (only for \ref cds_intrusive_SkipListSet_rcu "RCU-based SkipListSet") + /** + List of available options see opt::rcu_check_deadlock + */ + typedef opt::v::rcu_throw_deadlock rcu_check_deadlock; + + //@cond + // For internal use only!!! + typedef opt::none internal_node_builder; + //@endcond + }; + + /// Metafunction converting option list to SkipListSet traits + /** + This is a wrapper for cds::opt::make_options< type_traits, Options...> + \p Options list see \ref SkipListSet. + */ + template + struct make_traits { +# ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined type ; ///< Metafunction result +# else + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< type_traits, Options... >::type + ,Options... + >::type type; +# endif + }; + + //@cond + namespace details { + template + class head_node: public Node + { + typedef Node node_type; + + typename node_type::atomic_marked_ptr m_Tower[skip_list::c_nHeightLimit]; + + public: + head_node( unsigned int nHeight ) + { + for ( size_t i = 0; i < sizeof(m_Tower) / sizeof(m_Tower[0]); ++i ) + m_Tower[i].store( typename node_type::marked_ptr(), atomics::memory_order_relaxed ); + + node_type::make_tower( nHeight, m_Tower ); + } + + node_type * head() const + { + return const_cast( static_cast(this)); + } + }; + + template + struct intrusive_node_builder + { + typedef NodeType node_type; + typedef AtomicNodePtr atomic_node_ptr; + typedef Alloc allocator_type; + + typedef cds::details::Allocator< atomic_node_ptr, allocator_type > tower_allocator; + + template + static node_type * make_tower( node_type * pNode, RandomGen& gen ) + { + return make_tower( pNode, gen() + 1 ); + } + + static node_type * make_tower( node_type * pNode, unsigned int nHeight ) + { + if ( nHeight > 1 ) + pNode->make_tower( nHeight, tower_allocator().NewArray( nHeight - 1, nullptr ) ); + return pNode; + } + + static void dispose_tower( node_type * pNode ) + { + unsigned int nHeight = pNode->height(); + if ( nHeight > 1 ) + tower_allocator().Delete( pNode->release_tower(), nHeight ); + } + + struct node_disposer { + void operator()( node_type * pNode ) + { + dispose_tower( pNode ); + } + }; + }; + + // Forward declaration + template + class iterator; + + } // namespace details + //@endcond + + } // namespace skip_list + + // Forward declaration + template + class SkipListSet; + +}} // namespace cds::intrusive + +#endif // #ifndef __CDS_INTRUSIVE_DETAILS_SKIP_LIST_BASE_H diff --git a/cds/intrusive/skip_list_base.h b/cds/intrusive/skip_list_base.h deleted file mode 100644 index 5419dc4e..00000000 --- a/cds/intrusive/skip_list_base.h +++ /dev/null @@ -1,654 +0,0 @@ -//$$CDS-header$$ - -#ifndef __CDS_INTRUSIVE_SKIP_LIST_BASE_H -#define __CDS_INTRUSIVE_SKIP_LIST_BASE_H - -#include -#include -#include -#include -#include - - -namespace cds { namespace intrusive { - /// SkipListSet related definitions - /** @ingroup cds_intrusive_helper - */ - namespace skip_list { - - /// The maximum possible height of any skip-list - static unsigned int const c_nHeightLimit = 32; - - /// Skip list node - /** - Template parameters: - - GC - garbage collector - - Tag - a tag used to distinguish between different implementation. An incomplete type may be used as a tag. - */ - template - class node { - public: - typedef GC gc ; ///< Garbage collector - typedef Tag tag ; ///< tag - - typedef cds::details::marked_ptr marked_ptr ; ///< marked pointer - typedef typename gc::template atomic_marked_ptr< marked_ptr> atomic_marked_ptr ; ///< atomic marked pointer specific for GC - //@cond - typedef atomic_marked_ptr tower_item_type; - //@endcond - - protected: - atomic_marked_ptr m_pNext ; ///< Next item in bottom-list (list at level 0) - unsigned int m_nHeight ; ///< Node height (size of m_arrNext array). For node at level 0 the height is 1. - 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 - - public: - /// Constructs a node of height 1 (a bottom-list node) - node() - : m_pNext( nullptr ) - , m_nHeight(1) - , m_arrNext( nullptr ) - {} - - /// Constructs a node of height \p nHeight - void make_tower( unsigned int nHeight, atomic_marked_ptr * nextTower ) - { - assert( nHeight > 0 ); - assert( (nHeight == 1 && nextTower == nullptr) // bottom-list node - || (nHeight > 1 && nextTower != nullptr) // node at level of more than 0 - ); - - m_arrNext = nextTower; - m_nHeight = nHeight; - } - - //@cond - atomic_marked_ptr * release_tower() - { - atomic_marked_ptr * pTower = m_arrNext; - m_arrNext = nullptr; - m_nHeight = 1; - return pTower; - } - - atomic_marked_ptr * get_tower() const - { - return m_arrNext; - } - //@endcond - - /// Access to element of next pointer array - atomic_marked_ptr& next( unsigned int nLevel ) - { - assert( nLevel < height() ); - assert( nLevel == 0 || (nLevel > 0 && m_arrNext != nullptr) ); - - return nLevel ? m_arrNext[ nLevel - 1] : m_pNext; - } - - /// Access to element of next pointer array (const version) - atomic_marked_ptr const& next( unsigned int nLevel ) const - { - assert( nLevel < height() ); - assert( nLevel == 0 || nLevel > 0 && m_arrNext != nullptr ); - - return nLevel ? m_arrNext[ nLevel - 1] : m_pNext; - } - - /// Access to element of next pointer array (same as \ref next function) - atomic_marked_ptr& operator[]( unsigned int nLevel ) - { - return next( nLevel ); - } - - /// Access to element of next pointer array (same as \ref next function) - atomic_marked_ptr const& operator[]( unsigned int nLevel ) const - { - return next( nLevel ); - } - - /// Height of the node - unsigned int height() const - { - return m_nHeight; - } - - /// Clears internal links - void clear() - { - assert( m_arrNext == nullptr ); - m_pNext.store( marked_ptr(), atomics::memory_order_release ); - } - - //@cond - bool is_cleared() const - { - return m_pNext == atomic_marked_ptr() - && m_arrNext == nullptr - && m_nHeight <= 1 -; - } - //@endcond - }; - - //@cond - struct undefined_gc; - struct default_hook { - typedef undefined_gc gc; - typedef opt::none tag; - }; - //@endcond - - //@cond - template < typename HookType, typename... Options> - struct hook - { - typedef typename opt::make_options< default_hook, Options...>::type options; - typedef typename options::gc gc; - typedef typename options::tag tag; - typedef node node_type; - typedef HookType hook_type; - }; - //@endcond - - /// Base hook - /** - \p Options are: - - opt::gc - garbage collector used. - - opt::tag - a tag - */ - template < typename... Options > - struct base_hook: public hook< opt::base_hook_tag, Options... > - {}; - - /// Member hook - /** - \p MemberOffset defines offset in bytes of \ref node member into your structure. - Use \p offsetof macro to define \p MemberOffset - - \p Options are: - - opt::gc - garbage collector used. - - opt::tag - a tag - */ - template < size_t MemberOffset, typename... Options > - struct member_hook: public hook< opt::member_hook_tag, Options... > - { - //@cond - static const size_t c_nMemberOffset = MemberOffset; - //@endcond - }; - - /// Traits hook - /** - \p NodeTraits defines type traits for node. - See \ref node_traits for \p NodeTraits interface description - - \p Options are: - - opt::gc - garbage collector used. - - opt::tag - a tag - */ - template - struct traits_hook: public hook< opt::traits_hook_tag, Options... > - { - //@cond - typedef NodeTraits node_traits; - //@endcond - }; - - /// Option specifying random level generator - /** - The random level generator is an important part of skip-list algorithm. - The node height in the skip-list have a probabilistic distribution - where half of the nodes that have level \p i pointers also have level i+1 pointers - (i = 0..30). - The random level generator should provide such distribution. - - The \p Type functor interface is: - \code - struct random_generator { - static unsigned int const c_nUpperBound = 32; - random_generator(); - unsigned int operator()(); - }; - \endcode - - where - - \p c_nUpperBound - constant that specifies the upper bound of random number generated. - The generator produces a number from range [0 .. c_nUpperBound) (upper bound excluded). - \p c_nUpperBound must be no more than 32. - - random_generator() - the constructor of generator object initialises the generator instance (its internal state). - - unsigned int operator()() - the main generating function. Returns random level from range 0..31. - - Stateful generators are supported. - - Available \p Type implementations: - - \ref xorshift - - \ref turbo_pascal - */ - template - struct random_level_generator { - //@cond - template - struct pack: public Base - { - typedef Type random_level_generator; - }; - //@endcond - }; - - /// Xor-shift random level generator - /** - The simplest of the generators described in George - Marsaglia's "Xorshift RNGs" paper. This is not a high-quality - generator but is acceptable for skip-list. - - The random generator should return numbers from range [0..31]. - - From Doug Lea's ConcurrentSkipListMap.java. - */ - class xorshift { - //@cond - atomics::atomic m_nSeed; - //@endcond - public: - /// The upper bound of generator's return value. The generator produces random number in range [0..c_nUpperBound) - static unsigned int const c_nUpperBound = c_nHeightLimit; - - /// Initializes the generator instance - xorshift() - { - m_nSeed.store( (unsigned int) cds::OS::Timer::random_seed(), atomics::memory_order_relaxed ); - } - - /// Main generator function - unsigned int operator()() - { - /* ConcurrentSkipListMap.java - private int randomLevel() { - int x = randomSeed; - x ^= x << 13; - x ^= x >>> 17; - randomSeed = x ^= x << 5; - if ((x & 0x80000001) != 0) // test highest and lowest bits - return 0; - int level = 1; - while (((x >>>= 1) & 1) != 0) ++level; - return level; - } - */ - unsigned int x = m_nSeed.load( atomics::memory_order_relaxed ); - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - m_nSeed.store( x, atomics::memory_order_relaxed ); - unsigned int nLevel = ((x & 0x00000001) != 0) ? 0 : cds::bitop::LSB( (~(x >> 1)) & 0x7FFFFFFF ); - assert( nLevel < c_nUpperBound ); - return nLevel; - } - }; - - /// Turbo-pascal random level generator - /** - This uses a cheap pseudo-random function that was used in Turbo Pascal. - - The random generator should return numbers from range [0..31]. - - From Doug Lea's ConcurrentSkipListMap.java. - */ - class turbo_pascal - { - //@cond - atomics::atomic m_nSeed; - //@endcond - public: - /// The upper bound of generator's return value. The generator produces random number in range [0..c_nUpperBound) - static unsigned int const c_nUpperBound = c_nHeightLimit; - - /// Initializes the generator instance - turbo_pascal() - { - m_nSeed.store( (unsigned int) cds::OS::Timer::random_seed(), atomics::memory_order_relaxed ); - } - - /// Main generator function - unsigned int operator()() - { - /* - private int randomLevel() { - int level = 0; - int r = randomSeed; - randomSeed = r * 134775813 + 1; - if (r < 0) { - while ((r <<= 1) > 0) - ++level; - } - return level; - } - */ - /* - The low bits are apparently not very random (the original used only - upper 16 bits) so we traverse from highest bit down (i.e., test - sign), thus hardly ever use lower bits. - */ - unsigned int x = m_nSeed.load( atomics::memory_order_relaxed ) * 134775813 + 1; - m_nSeed.store( x, atomics::memory_order_relaxed ); - unsigned int nLevel = ( x & 0x80000000 ) ? (31 - cds::bitop::MSBnz( (x & 0x7FFFFFFF) | 1 )) : 0; - assert( nLevel < c_nUpperBound ); - return nLevel; - } - }; - - /// SkipListSet internal statistics - template - struct stat { - typedef EventCounter event_counter ; ///< Event counter type - - event_counter m_nNodeHeightAdd[c_nHeightLimit] ; ///< Count of added node of each height - event_counter m_nNodeHeightDel[c_nHeightLimit] ; ///< Count of deleted node of each height - event_counter m_nInsertSuccess ; ///< Count of success insertion - event_counter m_nInsertFailed ; ///< Count of failed insertion - event_counter m_nInsertRetries ; ///< Count of unsuccessful retries of insertion - event_counter m_nEnsureExist ; ///< Count of \p ensure call for existed node - event_counter m_nEnsureNew ; ///< Count of \p ensure call for new node - event_counter m_nUnlinkSuccess ; ///< Count of successful call of \p unlink - event_counter m_nUnlinkFailed ; ///< Count of failed call of \p unlink - event_counter m_nEraseSuccess ; ///< Count of successful call of \p erase - event_counter m_nEraseFailed ; ///< Count of failed call of \p erase - event_counter m_nFindFastSuccess ; ///< Count of successful call of \p find and all derivatives (via fast-path) - event_counter m_nFindFastFailed ; ///< Count of failed call of \p find and all derivatives (via fast-path) - event_counter m_nFindSlowSuccess ; ///< Count of successful call of \p find and all derivatives (via slow-path) - event_counter m_nFindSlowFailed ; ///< Count of failed call of \p find and all derivatives (via slow-path) - event_counter m_nRenewInsertPosition ; ///< Count of renewing position events while inserting - event_counter m_nLogicDeleteWhileInsert ; ///< Count of events "The node has been logically deleted while inserting" - event_counter m_nNotFoundWhileInsert ; ///< Count of events "Inserting node is not found" - event_counter m_nFastErase ; ///< Fast erase event counter - event_counter m_nFastExtract ; ///< Fast extract event counter - event_counter m_nSlowErase ; ///< Slow erase event counter - event_counter m_nSlowExtract ; ///< Slow extract event counter - event_counter m_nExtractSuccess ; ///< Count of successful call of \p extract - event_counter m_nExtractFailed ; ///< Count of failed call of \p extract - event_counter m_nExtractRetries ; ///< Count of retries of \p extract call - event_counter m_nExtractMinSuccess ; ///< Count of successful call of \p extract_min - event_counter m_nExtractMinFailed ; ///< Count of failed call of \p extract_min - event_counter m_nExtractMinRetries ; ///< Count of retries of \p extract_min call - event_counter m_nExtractMaxSuccess ; ///< Count of successful call of \p extract_max - event_counter m_nExtractMaxFailed ; ///< Count of failed call of \p extract_max - event_counter m_nExtractMaxRetries ; ///< Count of retries of \p extract_max call - event_counter m_nEraseWhileFind ; ///< Count of erased item while searching - event_counter m_nExtractWhileFind ; ///< Count of extracted item while searching (RCU only) - - //@cond - void onAddNode( unsigned int nHeight ) - { - assert( nHeight > 0 && nHeight <= sizeof(m_nNodeHeightAdd) / sizeof(m_nNodeHeightAdd[0])); - ++m_nNodeHeightAdd[nHeight - 1]; - } - void onRemoveNode( unsigned int nHeight ) - { - assert( nHeight > 0 && nHeight <= sizeof(m_nNodeHeightDel) / sizeof(m_nNodeHeightDel[0])); - ++m_nNodeHeightDel[nHeight - 1]; - } - - void onInsertSuccess() { ++m_nInsertSuccess ; } - void onInsertFailed() { ++m_nInsertFailed ; } - void onInsertRetry() { ++m_nInsertRetries ; } - void onEnsureExist() { ++m_nEnsureExist ; } - void onEnsureNew() { ++m_nEnsureNew ; } - void onUnlinkSuccess() { ++m_nUnlinkSuccess ; } - void onUnlinkFailed() { ++m_nUnlinkFailed ; } - void onEraseSuccess() { ++m_nEraseSuccess ; } - void onEraseFailed() { ++m_nEraseFailed ; } - void onFindFastSuccess() { ++m_nFindFastSuccess ; } - void onFindFastFailed() { ++m_nFindFastFailed ; } - void onFindSlowSuccess() { ++m_nFindSlowSuccess ; } - void onFindSlowFailed() { ++m_nFindSlowFailed ; } - void onEraseWhileFind() { ++m_nEraseWhileFind ; } - void onExtractWhileFind() { ++m_nExtractWhileFind ; } - void onRenewInsertPosition() { ++m_nRenewInsertPosition; } - void onLogicDeleteWhileInsert() { ++m_nLogicDeleteWhileInsert; } - void onNotFoundWhileInsert() { ++m_nNotFoundWhileInsert; } - void onFastErase() { ++m_nFastErase; } - void onFastExtract() { ++m_nFastExtract; } - void onSlowErase() { ++m_nSlowErase; } - void onSlowExtract() { ++m_nSlowExtract; } - void onExtractSuccess() { ++m_nExtractSuccess; } - void onExtractFailed() { ++m_nExtractFailed; } - void onExtractRetry() { ++m_nExtractRetries; } - void onExtractMinSuccess() { ++m_nExtractMinSuccess; } - void onExtractMinFailed() { ++m_nExtractMinFailed; } - void onExtractMinRetry() { ++m_nExtractMinRetries; } - void onExtractMaxSuccess() { ++m_nExtractMaxSuccess; } - void onExtractMaxFailed() { ++m_nExtractMaxFailed; } - void onExtractMaxRetry() { ++m_nExtractMaxRetries; } - - //@endcond - }; - - /// SkipListSet empty internal statistics - struct empty_stat { - //@cond - void onAddNode( unsigned int nHeight ) const {} - void onRemoveNode( unsigned int nHeight ) const {} - void onInsertSuccess() const {} - void onInsertFailed() const {} - void onInsertRetry() const {} - void onEnsureExist() const {} - void onEnsureNew() const {} - void onUnlinkSuccess() const {} - void onUnlinkFailed() const {} - void onEraseSuccess() const {} - void onEraseFailed() const {} - void onFindFastSuccess() const {} - void onFindFastFailed() const {} - void onFindSlowSuccess() const {} - void onFindSlowFailed() const {} - void onEraseWhileFind() const {} - void onExtractWhileFind() const {} - void onRenewInsertPosition() const {} - void onLogicDeleteWhileInsert() const {} - void onNotFoundWhileInsert() const {} - void onFastErase() const {} - void onFastExtract() const {} - void onSlowErase() const {} - void onSlowExtract() const {} - void onExtractSuccess() const {} - void onExtractFailed() const {} - void onExtractRetry() const {} - void onExtractMinSuccess() const {} - void onExtractMinFailed() const {} - void onExtractMinRetry() const {} - void onExtractMaxSuccess() const {} - void onExtractMaxFailed() const {} - void onExtractMaxRetry() const {} - - //@endcond - }; - - //@cond - // For internal use only!!! - template - struct internal_node_builder { - template - struct pack: public Base - { - typedef Type internal_node_builder; - }; - }; - //@endcond - - /// Type traits for SkipListSet class - struct type_traits - { - /// Hook used - /** - Possible values are: skip_list::base_hook, skip_list::member_hook, skip_list::traits_hook. - */ - typedef base_hook<> hook; - - /// Key comparison functor - /** - No default functor is provided. If the option is not specified, the \p less is used. - */ - typedef opt::none compare; - - /// specifies binary predicate used for key compare. - /** - Default is \p std::less. - */ - typedef opt::none less; - - /// Disposer - /** - The functor used for dispose removed items. Default is opt::v::empty_disposer. - */ - typedef opt::v::empty_disposer disposer; - - /// Item counter - /** - The type for item counting feature. - Default is no item counter (\ref atomicity::empty_item_counter) - */ - typedef atomicity::empty_item_counter item_counter; - - /// C++ memory ordering model - /** - List of available memory ordering see opt::memory_model - */ - typedef opt::v::relaxed_ordering memory_model; - - /// Random level generator - /** - The random level generator is an important part of skip-list algorithm. - The node height in the skip-list have a probabilistic distribution - where half of the nodes that have level \p i pointers also have level i+1 pointers - (i = 0..30). So, the height of a node is in range [0..31]. - - See skip_list::random_level_generator option setter. - */ - typedef turbo_pascal random_level_generator; - - /// Allocator - /** - Although the skip-list is an intrusive container, - an allocator should be provided to maintain variable randomly-calculated height of the node - since the node can contain up to 32 next pointers. - The allocator specified is used to allocate an array of next pointers - for nodes which height is more than 1. - */ - typedef CDS_DEFAULT_ALLOCATOR allocator; - - /// back-off strategy used - /** - If the option is not specified, the cds::backoff::Default is used. - */ - typedef cds::backoff::Default back_off; - - /// Internal statistics - typedef empty_stat stat; - - /// RCU deadlock checking policy (only for \ref cds_intrusive_SkipListSet_rcu "RCU-based SkipListSet") - /** - List of available options see opt::rcu_check_deadlock - */ - typedef opt::v::rcu_throw_deadlock rcu_check_deadlock; - - //@cond - // For internal use only!!! - typedef opt::none internal_node_builder; - //@endcond - }; - - /// Metafunction converting option list to SkipListSet traits - /** - This is a wrapper for cds::opt::make_options< type_traits, Options...> - \p Options list see \ref SkipListSet. - */ - template - struct make_traits { -# ifdef CDS_DOXYGEN_INVOKED - typedef implementation_defined type ; ///< Metafunction result -# else - typedef typename cds::opt::make_options< - typename cds::opt::find_type_traits< type_traits, Options... >::type - ,Options... - >::type type; -# endif - }; - - //@cond - namespace details { - template - class head_node: public Node - { - typedef Node node_type; - - typename node_type::atomic_marked_ptr m_Tower[skip_list::c_nHeightLimit]; - - public: - head_node( unsigned int nHeight ) - { - for ( size_t i = 0; i < sizeof(m_Tower) / sizeof(m_Tower[0]); ++i ) - m_Tower[i].store( typename node_type::marked_ptr(), atomics::memory_order_relaxed ); - - node_type::make_tower( nHeight, m_Tower ); - } - - node_type * head() const - { - return const_cast( static_cast(this)); - } - }; - - template - struct intrusive_node_builder - { - typedef NodeType node_type; - typedef AtomicNodePtr atomic_node_ptr; - typedef Alloc allocator_type; - - typedef cds::details::Allocator< atomic_node_ptr, allocator_type > tower_allocator; - - template - static node_type * make_tower( node_type * pNode, RandomGen& gen ) - { - return make_tower( pNode, gen() + 1 ); - } - - static node_type * make_tower( node_type * pNode, unsigned int nHeight ) - { - if ( nHeight > 1 ) - pNode->make_tower( nHeight, tower_allocator().NewArray( nHeight - 1, nullptr ) ); - return pNode; - } - - static void dispose_tower( node_type * pNode ) - { - unsigned int nHeight = pNode->height(); - if ( nHeight > 1 ) - tower_allocator().Delete( pNode->release_tower(), nHeight ); - } - - struct node_disposer { - void operator()( node_type * pNode ) - { - dispose_tower( pNode ); - } - }; - }; - - // Forward declaration - template - class iterator; - - } // namespace details - //@endcond - - } // namespace skip_list - - // Forward declaration - template - class SkipListSet; - -}} // namespace cds::intrusive - -#endif // #ifndef __CDS_INTRUSIVE_SKIP_LIST_BASE_H diff --git a/cds/intrusive/skip_list_impl.h b/cds/intrusive/skip_list_impl.h index 91e2acd4..034222a1 100644 --- a/cds/intrusive/skip_list_impl.h +++ b/cds/intrusive/skip_list_impl.h @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include #include diff --git a/cds/intrusive/skip_list_nogc.h b/cds/intrusive/skip_list_nogc.h index 5564479e..cb1ca410 100644 --- a/cds/intrusive/skip_list_nogc.h +++ b/cds/intrusive/skip_list_nogc.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/cds/intrusive/skip_list_rcu.h b/cds/intrusive/skip_list_rcu.h index 9d74a974..314d1700 100644 --- a/cds/intrusive/skip_list_rcu.h +++ b/cds/intrusive/skip_list_rcu.h @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include #include diff --git a/projects/Win/vc12/cds.vcxproj b/projects/Win/vc12/cds.vcxproj index 6dbf8cbe..71c00816 100644 --- a/projects/Win/vc12/cds.vcxproj +++ b/projects/Win/vc12/cds.vcxproj @@ -745,6 +745,7 @@ + @@ -756,7 +757,6 @@ - diff --git a/projects/Win/vc12/cds.vcxproj.filters b/projects/Win/vc12/cds.vcxproj.filters index 15711764..7be01398 100644 --- a/projects/Win/vc12/cds.vcxproj.filters +++ b/projects/Win/vc12/cds.vcxproj.filters @@ -953,9 +953,6 @@ Header Files\cds\gc - - Header Files\cds\intrusive - Header Files\cds\intrusive @@ -1280,5 +1277,8 @@ Header Files\cds\intrusive\details + + Header Files\cds\intrusive\details + \ No newline at end of file diff --git a/tests/unit/print_skip_list_stat.h b/tests/unit/print_skip_list_stat.h index 4e8ec6fe..7e40c1cc 100644 --- a/tests/unit/print_skip_list_stat.h +++ b/tests/unit/print_skip_list_stat.h @@ -3,7 +3,7 @@ #ifndef __UNIT_PRINT_SKIP_LIST_STAT_H #define __UNIT_PRINT_SKIP_LIST_STAT_H -#include +#include #include namespace std {