6 namespace honey {
namespace lockfree
13 template<
class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>>
20 struct MarkedHandle : TaggedHandle
25 MarkedHandle() =
default;
26 MarkedHandle(Handle handle, Int tag,
bool mark) : TaggedHandle(handle, (tag<<1) | mark) {}
28 Int nextTag()
const {
return (tag>>1)+1; }
29 bool mark()
const {
return tag &
true; }
30 void mark(
bool b) { tag = (tag & (~Int(0)<<1)) | b; }
38 key(forward<typename Pair::first_type>(pair.first)),
40 val(forward<typename Pair::second_type>(pair.second))
63 _maxLoadFactor(4) { expand(
true); }
76 Node* node = _freeList.
construct(forward<Pair>(pair));
77 szt hash = _hash(node->key);
78 node->soKey = BitOp::reverse(hash) |
true;
79 auto& bucket = getBucket(hash % _bucketCount);
80 if (!get<0>(list_insert(bucket, *node))) { _freeList.
destroy(node);
return false; }
89 szt hash = _hash(key);
90 szt soKey = BitOp::reverse(hash) |
true;
91 auto& bucket = getBucket(hash % _bucketCount);
92 if (!list_delete(bucket, key, soKey, val))
return false;
102 Node* prev = &getBucket(0);
108 auto ckey = _freeList.
deref(cur)->key;
109 auto csoKey = _freeList.
deref(cur)->soKey;
117 cur = MarkedHandle(next, cur.nextTag(),
false);
121 prev = _freeList.
deref(cur);
127 auto next_ = MarkedHandle(next, cur.nextTag(),
false);
128 if (prev->next.cas(next_, cur))
143 szt hash = _hash(key);
144 szt soKey = BitOp::reverse(hash) |
true;
145 auto& bucket = getBucket(hash % _bucketCount);
147 bool found; Node* prev; MarkedHandle cur, next;
150 tie(found, prev, cur, next) = list_find(bucket, key, soKey);
151 if (!found)
return false;
153 if (val) val = _freeList.
deref(cur)->val;
162 bool empty()
const {
return !_size; }
168 float load_factor()
const {
return _bucketCount ? float(_size) / _bucketCount : _size ? numeral<float>().inf() : 0; }
176 void expand(
bool init =
false)
181 szt count = _segmentCount ? (1<<_segmentCount) : 2;
182 auto buckets = make_unique<Atomic<MarkedHandle>[]>(
count);
183 std::fill_n(buckets.get(),
count, MarkedHandle());
184 assert(_segmentCount < _segments.size(),
"Max segments reached");
185 _segments[_segmentCount] = move(buckets);
187 _bucketCount +=
count;
192 Node& getBucket(
szt i)
const
194 szt segment = BitOp::log2Floor(i ? i : 1);
195 Node* parent = i ? &getBucket(~(1<<segment) & i) : nullptr;
196 auto& bucket = _segments[segment][i >= 2 ? i - (1<<segment) : i];
201 Node* node = _freeList.
construct(make_pair(Key(), T()));
202 node->soKey = BitOp::reverse(i) & (~
szt(0) << 1);
206 bool inserted; MarkedHandle cur;
207 tie(inserted, cur) = list_insert(*parent, *node);
208 if (!inserted) _freeList.
destroy(node);
210 bucket.cas(MarkedHandle(cur, old.nextTag(),
false), old);
215 if (!bucket.cas(MarkedHandle(_freeList.
handle(node), old.nextTag(),
false), old))
220 return *_freeList.
deref(old);
224 tuple<bool, MarkedHandle> list_insert(Node& head, Node& node)
const
228 bool found; Node* prev; MarkedHandle cur, next;
229 tie(found, prev, cur, next) = list_find(head, node.key, node.soKey);
230 if (found)
return make_tuple(
false, cur);
232 auto node_ = MarkedHandle(_freeList.
handle(&node), cur.nextTag(),
false);
233 if (prev->next.cas(node_, cur))
return make_tuple(
true, node_);
237 bool list_delete(Node& head,
const Key& key,
szt soKey, optional<T&> val =
optnull)
239 bool found; Node* prev; MarkedHandle cur, next;
242 tie(found, prev, cur, next) = list_find(head, key, soKey);
243 if (!found)
return false;
244 if (val) val = _freeList.
deref(cur)->val;
245 }
while (!_freeList.
deref(cur)->next.cas(MarkedHandle(next, next.nextTag(),
true), next));
247 if (prev->next.cas(MarkedHandle(next, cur.nextTag(),
false), cur))
250 list_find(head, key, soKey);
255 tuple<bool, Node*, MarkedHandle, MarkedHandle> list_find(Node& head,
const Key& key,
szt soKey)
const
263 if (!cur)
return make_tuple(
false, prev, cur, MarkedHandle());
265 auto ckey = _freeList.
deref(cur)->key;
266 auto csoKey = _freeList.
deref(cur)->soKey;
270 if (csoKey == soKey && _equal(ckey, key))
return make_tuple(
true, prev, cur, next);
271 if (csoKey > soKey)
return make_tuple(
false, prev, cur, next);
272 prev = _freeList.
deref(cur);
277 auto next_ = MarkedHandle(next, cur.nextTag(),
false);
278 if (prev->next.cas(next_, cur))
290 mutable FreeList _freeList;
294 array<UniquePtr<Atomic<MarkedHandle>[]>, numeral<uint64>().sizeBits> _segments;
296 Atomic<szt> _bucketCount;
298 Atomic<float> _maxLoadFactor;
bool find(const Key &key, optional< T & > val=optnull) const
Find element with key and copy its value into val. Returns true on success, false if not found...
Definition: UnorderedMap.h:141
Enables blocks to be referenced via index rather than pointer so that they can include a tag while st...
Definition: Pool.h:75
Holds block handle and tag to prevent lock-free ABA issues.
Definition: Pool.h:95
T * construct(Args &&...args)
Construct object and remove from free list.
Definition: FreeList.h:35
void reserve(szt capacity)
Ensure that enough storage is allocated for a number of objects.
Definition: FreeList.h:27
static optnull_t optnull
Null optional, use to reset an optional to an uninitialized state or test for initialization.
Definition: Optional.h:12
~UnorderedMap()
Definition: UnorderedMap.h:65
float load_factor() const
The current load factor. The load factor is the ratio between the number of elements and the number o...
Definition: UnorderedMap.h:168
float max_load_factor() const
Get the max load factor. The internal hash table will expand when the load factor is above the max lo...
Definition: UnorderedMap.h:170
Inherit to declare that class is not copyable.
Definition: Meta.h:286
static auto _
Definition: Module.cpp:8
Must be a load op. Synchronize with a prior release in another thread.
unsigned char uint8
Definition: Core.h:12
No order constraint, same as plain load/store. Unsafe but best performance.
UnorderedMap(szt capacity=0, const Hash &hash=Hash(), const KeyEqual &equal=KeyEqual())
Definition: UnorderedMap.h:56
void max_load_factor(float f)
Set the max load factor.
Definition: UnorderedMap.h:172
bool erase(const Key &key, optional< T & > val=optnull)
Remove element with key from the map and copy its value into val. Returns true on success...
Definition: UnorderedMap.h:87
#define assert(...)
Forwards to assert_#args. See assert_1(), assert_2().
Definition: Debug.h:24
szt capacity() const
The number of elements for which storage is allocated.
Definition: UnorderedMap.h:70
szt count(const Key &key) const
Return number of elements with matching key (either 0 or 1)
Definition: UnorderedMap.h:159
void reserve(szt capacity)
Ensure that enough storage is allocated for a number of elements.
Definition: UnorderedMap.h:68
size_t szt
Size type, shorthand for size_t.
Definition: Core.h:90
void destroy(T *ptr)
Destroy object and add to free list.
Definition: FreeList.h:40
Lock-free unordered map. Uses auto-expanding freelist allocator so memory is only reclaimed upon dest...
Definition: UnorderedMap.h:14
szt capacity() const
The number of objects for which storage is allocated.
Definition: FreeList.h:29
Specialization for references.
Definition: Optional.h:132
bool insert(Pair &&pair)
Insert new key-value pair into the map. Returns true on success, false if element with key already ex...
Definition: UnorderedMap.h:74
T * deref(Handle handle) const
Get object from compressed handle.
Definition: FreeList.h:45
T value_type
Definition: UnorderedMap.h:54
szt size() const
The number of elements in the map.
Definition: UnorderedMap.h:164
Int tag
Definition: Pool.h:109
void clear()
Remove all elements.
Definition: UnorderedMap.h:98
bool empty() const
Check whether the map does not contain any elements.
Definition: UnorderedMap.h:162
Handle handle(T *ptr) const
Get compressed handle for object.
Definition: FreeList.h:43
szt bucket_count() const
The number of buckets. A bucket is a slot in the internal hash table to which elements are assigned b...
Definition: UnorderedMap.h:166
Global Honeycomb namespace.
mt::uintBySize< sizeof(atomic::SwapMaxType)/2 >::type Int
Definition: Pool.h:78