3 #ifndef CDSLIB_INTRUSIVE_DETAILS_MULTILEVEL_HASHSET_BASE_H
4 #define CDSLIB_INTRUSIVE_DETAILS_MULTILEVEL_HASHSET_BASE_H
6 #include <memory.h> // memcmp, memcpy
8 #include <cds/intrusive/details/base.h>
9 #include <cds/opt/compare.h>
10 #include <cds/algo/atomic.h>
11 #include <cds/details/marked_ptr.h>
12 #include <cds/urcu/options.h>
14 namespace cds { namespace intrusive {
16 /// MultiLevelHashSet ordered list related definitions
17 /** @ingroup cds_intrusive_helper
19 namespace multilevel_hashset {
21 /// Hash accessor option
23 @copydetails traits::hash_accessor
25 template <typename Accessor>
26 struct hash_accessor {
28 template <typename Base> struct pack: public Base
30 typedef Accessor hash_accessor;
35 /// Head node allocator option
37 @copydetails traits::head_node_allocator
39 template <typename Accessor>
40 struct head_node_allocator {
42 template <typename Base> struct pack: public Base
44 typedef Accessor head_node_allocator;
49 /// Array node allocator option
51 @copydetails traits::array_node_allocator
53 template <typename Accessor>
54 struct array_node_allocator {
56 template <typename Base> struct pack: public Base
58 typedef Accessor array_node_allocator;
63 /// \p MultiLevelHashSet internal statistics
64 template <typename EventCounter = cds::atomicity::event_counter>
66 typedef EventCounter event_counter ; ///< Event counter type
68 event_counter m_nInsertSuccess; ///< Number of success \p insert() operations
69 event_counter m_nInsertFailed; ///< Number of failed \p insert() operations
70 event_counter m_nInsertRetry; ///< Number of attempts to insert new item
71 event_counter m_nUpdateNew; ///< Number of new item inserted for \p update()
72 event_counter m_nUpdateExisting; ///< Number of existing item updates
73 event_counter m_nUpdateFailed; ///< Number of failed \p update() call
74 event_counter m_nUpdateRetry; ///< Number of attempts to update the item
75 event_counter m_nEraseSuccess; ///< Number of successful \p erase(), \p unlink(), \p extract() operations
76 event_counter m_nEraseFailed; ///< Number of failed \p erase(), \p unlink(), \p extract() operations
77 event_counter m_nEraseRetry; ///< Number of attempts to \p erase() an item
78 event_counter m_nFindSuccess; ///< Number of successful \p find() and \p get() operations
79 event_counter m_nFindFailed; ///< Number of failed \p find() and \p get() operations
81 event_counter m_nExpandNodeSuccess; ///< Number of succeeded attempts converting data node to array node
82 event_counter m_nExpandNodeFailed; ///< Number of failed attempts converting data node to array node
83 event_counter m_nSlotChanged; ///< Number of array node slot changing by other thread during an operation
84 event_counter m_nSlotConverting; ///< Number of events when we encounter a slot while it is converting to array node
86 void onInsertSuccess() { ++m_nInsertSuccess; }
87 void onInserFailed() { ++m_nInsertFailed; }
88 void onInsertRetry() { ++m_nInsertRetry; }
89 void onUpdateNew() { ++m_nUpdateNew; }
90 void onUpdateExisting() { ++m_nUpdateExisting; }
91 void onUpdateFailed() { ++m_nUpdateFailed; }
92 void onUpdateRetry() { ++m_nUpdateRetry; }
93 void onEraseSuccess() { ++m_nEraseSuccess; }
94 void onEraseFailed() { ++m_nEraseFailed; }
95 void onEraseRetry() { ++m_nEraseRetry; }
96 void onFindSuccess() { ++m_nFinSuccess; }
97 void onFindFailed() { ++m_nFindFailed; }
99 void onExpandNodeSuccess() { ++m_nExpandNodeSuccess; }
100 void onExpandNodeFailed() { ++m_nExpandNodeFailed; }
101 void onSlotChanged() { ++m_nSlotChanged; }
102 void onSlotConverting() { ++m_nSlotConverting; }
105 /// \p MultiLevelHashSet empty internal statistics
108 void onInsertSuccess() const {}
109 void onInsertFailed() const {}
110 void onInsertRetry() const {}
111 void onUpdateNew() const {}
112 void onUpdateExisting() const {}
113 void onUpdateFailed() const {}
114 void onUpdateRetry() const {}
115 void onEraseSuccess() const {}
116 void onEraseFailed() const {}
117 void onEraseRetry() const {}
118 void onFindSuccess() const {}
119 void onFindFailed() const {}
121 void onExpandNodeSuccess() const {}
122 void onExpandNodeFailed() const {}
123 void onSlotChanged() const {}
124 void onSlotConverting() const {}
128 /// MultiLevelHashSet traits
131 /// Mandatory functor to get hash value from data node
133 It is most-important feature of \p MultiLevelHashSet.
134 That functor must return a reference to fixed-sized hash value of data node.
135 The return value of that functor specifies the type of hash value.
139 typedef uint8_t hash_type[32]; // 256-bit hash type
141 hash_type hash; // 256-bit hash value
146 struct foo_hash_accessor {
147 hash_type const& operator()( foo const& d ) const
154 typedef opt::none hash_accessor;
156 /// Disposer for removing data nodes
157 typedef opt::v::empty_disposer disposer;
159 /// Hash comparing functor
161 No default functor is provided.
162 If the option is not specified, the \p less option is used.
164 typedef opt::none compare;
166 /// Specifies binary predicate used for hash compare.
168 If the option is not specified, \p memcmp() -like bit-wise hash comparator is used
169 because the hash value is treated as fixed-sized bit-string.
171 typedef opt::none less;
175 The item counting is an important part of \p MultiLevelHashSet algorithm:
176 the \p empty() member function depends on correct item counting.
177 Therefore, \p atomicity::empty_item_counter is not allowed as a type of the option.
179 Default is \p atomicity::item_counter.
181 typedef cds::atomicity::item_counter item_counter;
183 /// Head node allocator
185 Allocator for head node. That allocator uses only in the set's constructor for allocating
186 main head array and in the destructor for destroying the head array.
187 Default is \ref CDS_DEFAULT_ALLOCATOR
189 typedef CDS_DEFAULT_ALLOCATOR head_node_allocator;
191 /// Array node allocator
193 Allocator for array nodes. That allocator is used for creating \p arrayNode when the set grows.
194 The size of each array node is fixed.
195 Default is \ref CDS_DEFAULT_ALLOCATOR
197 typedef CDS_DEFAULT_ALLOCATOR array_node_allocator;
199 /// C++ memory ordering model
201 Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
202 or \p opt::v::sequential_consistent (sequentially consisnent memory model).
204 typedef opt::v::relaxed_ordering memory_model;
206 /// Back-off strategy
207 typedef cds::backoff::Default back_off;
209 /// Internal statistics
211 By default, internal statistics is disabled (\p multilevel_hashset::empty_stat).
212 Use \p multilevel_hashset::stat to enable it.
214 typedef empty_stat stat;
216 /// RCU deadlock checking policy (only for \ref cds_intrusive_MultilevelHashSet_rcu "RCU-based MultilevelHashSet")
218 List of available policy see \p opt::rcu_check_deadlock
220 typedef opt::v::rcu_throw_deadlock rcu_check_deadlock;
223 /// Metafunction converting option list to \p multilevel_hashset::traits
225 Supported \p Options are:
226 - \p multilevel_hashset::hash_accessor - mandatory option, hash accessor functor.
227 @copydetails traits::hash_accessor
228 - \p multilevel_hashset::head_node_allocator - head node allocator.
229 @copydetails traits::head_node_allocator
230 - \p multilevel_hashset::array_node_allocator - array node allocator.
231 @copydetails traits::array_node_allocator
232 - \p opt::compare - hash comparison functor. No default functor is provided.
233 If the option is not specified, the \p opt::less is used.
234 - \p opt::less - specifies binary predicate used for hash comparison.
235 If the option is not specified, \p memcmp() -like bit-wise hash comparator is used
236 because the hash value is treated as fixed-sized bit-string.
237 - \p opt::back_off - back-off strategy used. If the option is not specified, the \p cds::backoff::Default is used.
238 - \p opt::disposer - the functor used for disposing removed data node. Default is \p opt::v::empty_disposer. Due the nature
239 of GC schema the disposer may be called asynchronously.
240 - \p opt::item_counter - the type of item counting feature.
241 The item counting is an important part of \p MultiLevelHashSet algorithm:
242 the \p empty() member function depends on correct item counting.
243 Therefore, \p atomicity::empty_item_counter is not allowed as a type of the option.
244 Default is \p atomicity::item_counter.
245 - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
246 or \p opt::v::sequential_consistent (sequentially consisnent memory model).
247 - \p opt::stat - internal statistics. By default, it is disabled (\p multilevel_hashset::empty_stat).
248 To enable it use \p multilevel_hashset::stat
249 - \p opt::rcu_check_deadlock - a deadlock checking policy for \ref cds_intrusive_MultilevelHashSet_rcu "RCU-based MultilevelHashSet"
250 Default is \p opt::v::rcu_throw_deadlock
252 template <typename... Options>
255 # ifdef CDS_DOXYGEN_INVOKED
256 typedef implementation_defined type ; ///< Metafunction result
258 typedef typename cds::opt::make_options<
259 typename cds::opt::find_type_traits< traits, Options... >::type
265 /// Bit-wise memcmp-based comparator for hash value \p T
266 template <typename T>
267 struct bitwise_compare
269 int operator()( T const& lhs, T const& rhs ) const
271 return memcmp( &lhs, &rhs, sizeof(T));
277 template <typename HashType, typename UInt = size_t >
281 typedef HashType hash_type;
282 typedef UInt uint_type;
284 static CDS_CONSTEXPR size_t const c_nHashSize = (sizeof(hash_type) + sizeof(uint_type) - 1) / sizeof(uint_type);
285 static CDS_CONSTEXPR size_t const c_nBitPerByte = 8;
286 static CDS_CONSTEXPR size_t const c_nBitPerHash = sizeof(hash_type) * c_nBitPerByte;
287 static CDS_CONSTEXPR size_t const c_nBitPerInt = sizeof(uint_type) * c_nBitPerByte;
290 explicit hash_splitter( hash_type const& h )
291 : m_ptr(reinterpret_cast<uint_type const*>( &h ))
295 , m_last( m_ptr + c_nHashSize )
299 explicit operator bool() const
307 return m_pos >= c_nBitPerHash;
310 uint_type cut( size_t nBits )
313 assert( nBits <= c_nBitPerInt );
314 assert( m_pos + nBits <= c_nBitPerHash );
316 assert( m_ptr < m_last );
320 size_t const nRest = c_nBitPerInt - m_pos % c_nBitPerInt;
322 if ( nBits < nRest ) {
323 result = *m_ptr << ( nRest - nBits );
324 result = result >> ( c_nBitPerInt - nBits );
326 else if ( nBits == nRest ) {
327 result = *m_ptr >> ( c_nBitPerInt - nRest );
331 size_t const lsb = *m_ptr >> ( c_nBitPerInt - nRest );
335 result = *m_ptr << ( c_nBitPerInt - nBits );
336 result = result >> ( c_nBitPerInt - nBits );
337 result = (result << nRest) + lsb;
340 assert( m_pos <= c_nBitPerHash );
342 assert( m_ptr < m_last );
347 uint_type safe_cut( size_t nBits )
350 assert( nBits <= sizeof(uint_type) * c_nBitPerByte );
352 if ( m_pos + nBits > c_nBitPerHash )
353 nBits = c_nBitPerHash - m_pos;
354 return nBits ? cut( nBits ) : 0;
364 uint_type const* m_ptr; ///< current position in the hash
365 size_t m_pos; ///< current position in bits
366 uint_type const* m_first; ///< first position
368 uint_type const* m_last; ///< last position
371 } // namespace details
374 } // namespace multilevel_hashset
377 // Forward declaration
378 template < class GC, typename T, class Traits = multilevel_hashset::traits >
379 class MultiLevelHashSet;
382 }} // namespace cds::intrusive
384 #endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_MULTILEVEL_HASHSET_BASE_H