xenium
vyukov_hash_map_traits.hpp
1 //
2 // Copyright (c) 2018-2020 Manuel Pöter.
3 // Licensed under the MIT License. See LICENSE file in the project root for full license information.
4 //
5 
6 #ifndef XENIUM_VYUKOV_HASH_MAP_IMPL
7  #error "This is an impl file and must not be included directly!"
8 #endif
9 
10 namespace xenium::impl {
11 namespace bits {
12  template <class Key>
13  struct vyukov_hash_map_common {
14  using key_type = Key;
15 
16  template <class Accessor>
17  static void reset(Accessor&& acc) {
18  acc.reset();
19  }
20  };
21 
22  template <class Key>
23  struct vyukov_hash_map_trivial_key : vyukov_hash_map_common<Key> {
24  template <class Cell>
25  static bool compare_trivial_key(Cell& key_cell, const Key& key, hash_t /*hash*/) {
26  return key_cell.load(std::memory_order_relaxed) == key;
27  }
28 
29  template <class Accessor>
30  static bool compare_nontrivial_key(const Accessor&, const Key&) {
31  return true;
32  }
33 
34  template <class Hash>
35  static hash_t rehash(const Key& k) {
36  return Hash{}(k);
37  }
38  };
39 
40  template <class Key>
41  struct vyukov_hash_map_nontrivial_key : vyukov_hash_map_common<Key> {
42  template <class Cell>
43  static bool compare_trivial_key(Cell& key_cell, const Key& /*key*/, hash_t hash) {
44  return key_cell.load(std::memory_order_relaxed) == hash;
45  }
46 
47  template <class Accessor>
48  static bool compare_nontrivial_key(const Accessor& acc, const Key& key) {
49  return acc.key() == key;
50  }
51 
52  template <class Hash>
53  static hash_t rehash(hash_t h) {
54  return h;
55  }
56  };
57 } // namespace bits
58 template <class Key, class Value, class VReclaimer, class ValueReclaimer, class Reclaimer>
59 struct vyukov_hash_map_traits<Key, managed_ptr<Value, VReclaimer>, ValueReclaimer, Reclaimer, true, true> :
60  bits::vyukov_hash_map_trivial_key<Key> {
61  static_assert(!parameter::is_set<ValueReclaimer>::value,
62  "value_reclaimer policy can only be used with non-trivial key/value types");
63 
64  using value_type = Value*;
65  using storage_key_type = std::atomic<Key>;
66  using storage_value_type = typename VReclaimer::template concurrent_ptr<Value>;
67  using iterator_value_type = std::pair<const Key, Value*>;
68  using iterator_reference = iterator_value_type;
69 
70  class accessor {
71  public:
72  accessor() = default;
73  Value* operator->() const noexcept { return guard.get(); }
74  Value& operator*() const noexcept { return guard.get(); }
75  void reset() { guard.reset(); }
76  void reclaim() { guard.reclaim(); }
77 
78  private:
79  accessor(storage_value_type& v, std::memory_order order) : guard(acquire_guard(v, order)) {}
80  typename storage_value_type::guard_ptr guard;
81  friend struct vyukov_hash_map_traits;
82  };
83 
84  static accessor acquire(storage_value_type& v, std::memory_order order) { return accessor(v, order); }
85 
86  template <bool AcquireAccessor>
87  static void store_item(storage_key_type& key_cell,
88  storage_value_type& value_cell,
89  hash_t /*hash*/,
90  Key k,
91  Value* v,
92  std::memory_order order,
93  accessor& acc) {
94  key_cell.store(k, std::memory_order_relaxed);
95  value_cell.store(v, order);
96  if (AcquireAccessor) {
97  acc.guard = typename storage_value_type::guard_ptr(v);
98  }
99  }
100 
101  template <bool AcquireAccessor>
102  static bool compare_key(storage_key_type& key_cell,
103  storage_value_type& value_cell,
104  const Key& key,
105  hash_t /*hash*/,
106  accessor& acc) {
107  if (key_cell.load(std::memory_order_relaxed) != key) {
108  return false;
109  }
110  if (AcquireAccessor) {
111  acc.guard = typename storage_value_type::guard_ptr(value_cell.load(std::memory_order_relaxed));
112  }
113  return true;
114  }
115 
116  static iterator_reference deref_iterator(storage_key_type& k, storage_value_type& v) {
117  return {k.load(std::memory_order_relaxed), v.load(std::memory_order_relaxed).get()};
118  }
119 
120  static void reclaim(accessor& a) { a.guard.reclaim(); }
121  static void reclaim_internal(accessor&) {} // noop
122 };
123 
124 template <class Key, class Value, class VReclaimer, class ValueReclaimer, class Reclaimer>
125 struct vyukov_hash_map_traits<Key, managed_ptr<Value, VReclaimer>, ValueReclaimer, Reclaimer, false, true> :
126  bits::vyukov_hash_map_nontrivial_key<Key> {
127  using reclaimer = std::conditional_t<parameter::is_set<ValueReclaimer>::value, ValueReclaimer, Reclaimer>;
128 
129  struct node : reclaimer::template enable_concurrent_ptr<node> {
130  node(Key&& key, Value* value) : key(std::move(key)), value(std::move(value)) {}
131 
132  Key key;
133  typename VReclaimer::template concurrent_ptr<Value> value;
134  };
135 
136  using value_type = Value*;
137  using storage_key_type = std::atomic<size_t>;
138  using storage_value_type = typename reclaimer::template concurrent_ptr<node>;
139  using iterator_value_type = std::pair<const Key&, value_type>;
140  using iterator_reference = iterator_value_type;
141 
142  class accessor {
143  public:
144  accessor() = default;
145  Value* operator->() const noexcept { return value_guard.get(); }
146  Value& operator*() const noexcept { return *value_guard.get(); }
147  void reset() {
148  value_guard.reset();
149  node_guard.reset();
150  }
151 
152  private:
153  accessor(storage_value_type& v, std::memory_order order) :
154  node_guard(acquire_guard(v, order)),
155  value_guard(acquire_guard(node_guard->value, order)) {}
156  [[nodiscard]] const Key& key() const { return node_guard->key; }
157  // accessor(typename storage_value_type::marked_ptr v) : guard(v) {}
158  typename storage_value_type::guard_ptr node_guard;
159  typename VReclaimer::template concurrent_ptr<Value>::guard_ptr value_guard;
160  friend struct vyukov_hash_map_traits;
161  friend struct bits::vyukov_hash_map_nontrivial_key<Key>;
162  };
163 
164  static accessor acquire(storage_value_type& v, std::memory_order order) { return accessor(v, order); }
165 
166  template <bool AcquireAccessor>
167  static void store_item(storage_key_type& key_cell,
168  storage_value_type& value_cell,
169  hash_t hash,
170  Key&& k,
171  Value* v,
172  std::memory_order order,
173  accessor& acc) {
174  auto n = new node(std::move(k), v);
175  if (AcquireAccessor) {
176  acc.node_guard = typename storage_value_type::guard_ptr(n); // TODO - is this necessary?
177  acc.value_guard = typename VReclaimer::template concurrent_ptr<Value>::guard_ptr(v);
178  }
179  key_cell.store(hash, std::memory_order_relaxed);
180  value_cell.store(n, order);
181  }
182 
183  template <bool AcquireAccessor>
184  static bool compare_key(storage_key_type& key_cell,
185  storage_value_type& value_cell,
186  const Key& key,
187  hash_t hash,
188  accessor& acc) {
189  if (key_cell.load(std::memory_order_relaxed) != hash) {
190  return false;
191  }
192  acc.node_guard = typename storage_value_type::guard_ptr(value_cell.load(std::memory_order_relaxed));
193  if (acc.node_guard->key == key) {
194  if (AcquireAccessor) {
195  acc.value_guard = typename VReclaimer::template concurrent_ptr<Value>::guard_ptr(
196  acc.node_guard->value.load(std::memory_order_relaxed));
197  }
198  return true;
199  }
200  return false;
201  }
202 
203  static iterator_reference deref_iterator(storage_key_type&, storage_value_type& v) {
204  auto node = v.load(std::memory_order_relaxed);
205  return {node->key, node->value.load(std::memory_order_relaxed).get()};
206  }
207 
208  static void reclaim(accessor& a) {
209  a.value_guard.reclaim();
210  a.node_guard.reclaim();
211  }
212  static void reclaim_internal(accessor& a) {
213  // copy guard to avoid resetting the accessor's guard_ptr.
214  // TODO - this could be simplified by avoiding reset of
215  // guard_ptrs in reclaim().
216  auto g = a.node_guard;
217  g.reclaim();
218  }
219 };
220 
221 template <class Key, class Value, class ValueReclaimer, class Reclaimer>
222 struct vyukov_hash_map_traits<Key, Value, ValueReclaimer, Reclaimer, true, true> :
223  bits::vyukov_hash_map_trivial_key<Key> {
224  static_assert(!parameter::is_set<ValueReclaimer>::value,
225  "value_reclaimer policy can only be used with non-trivial key/value types");
226 
227  using value_type = Value;
228  using storage_key_type = std::atomic<Key>;
229  using storage_value_type = std::atomic<Value>;
230  using iterator_value_type = std::pair<const Key, Value>;
231  using iterator_reference = iterator_value_type;
232 
233  class accessor {
234  public:
235  accessor() = default;
236  const value_type& operator*() const noexcept { return v; }
237 
238  private:
239  accessor(storage_value_type& v, std::memory_order order) : v(v.load(order)) {}
240  value_type v;
241  friend struct vyukov_hash_map_traits;
242  };
243 
244  static void reset(accessor&&) {}
245  static accessor acquire(storage_value_type& v, std::memory_order order) { return accessor(v, order); }
246 
247  template <bool AcquireAccessor>
248  static void store_item(storage_key_type& key_cell,
249  storage_value_type& value_cell,
250  hash_t /*hash*/,
251  Key k,
252  Value v,
253  std::memory_order order,
254  accessor& acc) {
255  key_cell.store(k, std::memory_order_relaxed);
256  value_cell.store(v, order);
257  if (AcquireAccessor) {
258  acc.v = v;
259  }
260  }
261 
262  template <bool AcquireAccessor>
263  static bool compare_key(storage_key_type& key_cell,
264  storage_value_type& value_cell,
265  const Key& key,
266  hash_t /*hash*/,
267  accessor& acc) {
268  if (key_cell.load(std::memory_order_relaxed) != key) {
269  return false;
270  }
271  if (AcquireAccessor) {
272  acc.v = value_cell.load(std::memory_order_relaxed);
273  }
274  return true;
275  }
276 
277  static iterator_reference deref_iterator(storage_key_type& k, storage_value_type& v) {
278  return {k.load(std::memory_order_relaxed), v.load(std::memory_order_relaxed)};
279  }
280 
281  static void reclaim(accessor&) {} // noop
282  static void reclaim_internal(accessor&) {} // noop
283 };
284 
285 template <class Key, class Value, class ValueReclaimer, class Reclaimer>
286 struct vyukov_hash_map_traits<Key, Value, ValueReclaimer, Reclaimer, true, false> :
287  bits::vyukov_hash_map_trivial_key<Key> {
288  using reclaimer = std::conditional_t<parameter::is_set<ValueReclaimer>::value, ValueReclaimer, Reclaimer>;
289 
290  struct node : reclaimer::template enable_concurrent_ptr<node> {
291  explicit node(Value&& value) : value(std::move(value)) {}
292  Value value;
293  };
294 
295  using value_type = Value;
296  using storage_key_type = std::atomic<Key>;
297  using storage_value_type = typename reclaimer::template concurrent_ptr<node>;
298  using iterator_value_type = std::pair<const Key, Value&>;
299  using iterator_reference = iterator_value_type;
300 
301  class accessor {
302  public:
303  accessor() = default;
304  Value* operator->() const noexcept { return &guard->value; }
305  Value& operator*() const noexcept { return guard->value; }
306  void reset() { guard.reset(); }
307 
308  private:
309  accessor(storage_value_type& v, std::memory_order order) : guard(acquire_guard(v, order)) {}
310  typename storage_value_type::guard_ptr guard;
311  friend struct vyukov_hash_map_traits;
312  };
313 
314  static accessor acquire(storage_value_type& v, std::memory_order order) { return accessor(v, order); }
315 
316  template <bool AcquireAccessor>
317  static bool compare_key(storage_key_type& key_cell,
318  storage_value_type& value_cell,
319  const Key& key,
320  hash_t /*hash*/,
321  accessor& acc) {
322  if (key_cell.load(std::memory_order_relaxed) != key) {
323  return false;
324  }
325  if (AcquireAccessor) {
326  acc.guard = typename storage_value_type::guard_ptr(value_cell.load(std::memory_order_relaxed));
327  }
328  return true;
329  }
330 
331  template <bool AcquireAccessor>
332  static void store_item(storage_key_type& key_cell,
333  storage_value_type& value_cell,
334  hash_t /*hash*/,
335  Key&& k,
336  Value&& v,
337  std::memory_order order,
338  accessor& acc) {
339  auto n = new node(std::move(v));
340  if (AcquireAccessor) {
341  acc.guard = typename storage_value_type::guard_ptr(n);
342  }
343  key_cell.store(k, std::memory_order_relaxed);
344  value_cell.store(n, order);
345  }
346 
347  static iterator_reference deref_iterator(storage_key_type& k, storage_value_type& v) {
348  auto node = v.load(std::memory_order_relaxed);
349  return {k.load(std::memory_order_relaxed), node->value};
350  }
351 
352  static void reclaim(accessor& a) { a.guard.reclaim(); }
353  static void reclaim_internal(accessor& a) {
354  // copy guard to avoid resetting the accessor's guard_ptr.
355  // TODO - this could be simplified by avoiding reset of
356  // guard_ptrs in reclaim().
357  auto g = a.guard;
358  g.reclaim();
359  }
360 };
361 
362 template <class Key, class Value, class ValueReclaimer, class Reclaimer, bool TrivialValue>
363 struct vyukov_hash_map_traits<Key, Value, ValueReclaimer, Reclaimer, false, TrivialValue> :
364  bits::vyukov_hash_map_nontrivial_key<Key> {
365  using reclaimer = std::conditional_t<parameter::is_set<ValueReclaimer>::value, ValueReclaimer, Reclaimer>;
366 
367  struct node : reclaimer::template enable_concurrent_ptr<node> {
368  node(Key&& key, Value&& value) : data(std::move(key), std::move(value)) {}
369 
370  std::pair<const Key, Value> data;
371  };
372 
373  using value_type = Value;
374  using storage_key_type = std::atomic<hash_t>;
375  using storage_value_type = typename reclaimer::template concurrent_ptr<node>;
376  using iterator_value_type = std::pair<const Key, Value>;
377  using iterator_reference = iterator_value_type&;
378 
379  class accessor {
380  public:
381  accessor() = default;
382  Value* operator->() const noexcept { return &guard->data.second; }
383  Value& operator*() const noexcept { return guard->data.second; }
384  void reset() { guard.reset(); }
385 
386  private:
387  accessor(storage_value_type& v, std::memory_order order) : guard(acquire_guard(v, order)) {}
388  [[nodiscard]] const Key& key() const { return guard->data.first; }
389  typename storage_value_type::guard_ptr guard;
390  friend struct vyukov_hash_map_traits;
391  friend struct bits::vyukov_hash_map_nontrivial_key<Key>;
392  };
393 
394  static accessor acquire(storage_value_type& v, std::memory_order order) { return accessor(v, order); }
395 
396  template <bool AcquireAccessor>
397  static void store_item(storage_key_type& key_cell,
398  storage_value_type& value_cell,
399  hash_t hash,
400  Key&& k,
401  Value&& v,
402  std::memory_order order,
403  accessor& acc) {
404  auto n = new node(std::move(k), std::move(v));
405  if (AcquireAccessor) {
406  acc.guard = typename storage_value_type::guard_ptr(n);
407  }
408  key_cell.store(hash, std::memory_order_relaxed);
409  value_cell.store(n, order);
410  }
411 
412  template <bool AcquireAccessor>
413  static bool compare_key(storage_key_type& key_cell,
414  storage_value_type& value_cell,
415  const Key& key,
416  hash_t hash,
417  accessor& acc) {
418  if (key_cell.load(std::memory_order_relaxed) != hash) {
419  return false;
420  }
421  acc.guard = typename storage_value_type::guard_ptr(value_cell.load(std::memory_order_relaxed));
422  return acc.guard->data.first == key;
423  }
424 
425  static iterator_reference deref_iterator(storage_key_type&, storage_value_type& v) {
426  auto node = v.load(std::memory_order_relaxed);
427  return node->data;
428  }
429 
430  static void reclaim(accessor& a) { a.guard.reclaim(); }
431  static void reclaim_internal(accessor& a) {
432  // copy guard to avoid resetting the accessor's guard_ptr.
433  // TODO - this could be simplified by avoiding reset of
434  // guard_ptrs in reclaim().
435  auto g = a.guard;
436  g.reclaim();
437  }
438 };
439 } // namespace xenium::impl