xenium
vyukov_hash_map.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_HPP
7 #define XENIUM_VYUKOV_HASH_MAP_HPP
8 
9 #include <xenium/acquire_guard.hpp>
10 #include <xenium/backoff.hpp>
11 #include <xenium/hash.hpp>
12 #include <xenium/parameter.hpp>
13 #include <xenium/policy.hpp>
14 #include <xenium/utils.hpp>
15 
16 #include <atomic>
17 #include <cstdint>
18 
19 namespace xenium {
20 
21 namespace policy {
30  template <class T>
32 } // namespace policy
33 
34 namespace impl {
35  template <class Key, class Value, class ValueReclaimer, class Reclaimer, bool TrivialKey, bool TrivialValue>
36  struct vyukov_hash_map_traits;
37 } // namespace impl
38 
46 template <class T, class Reclaimer>
47 struct managed_ptr;
48 
49 namespace detail {
50  template <class T>
51  struct vyukov_supported_type {
52  static constexpr bool value = std::is_trivial<T>::value && (sizeof(T) == 4 || sizeof(T) == 8);
53  };
54  template <class T, class Reclaimer>
55  struct vyukov_supported_type<managed_ptr<T, Reclaimer>> {
56  static_assert(std::is_base_of<typename Reclaimer::template enable_concurrent_ptr<T>, T>::value,
57  "The type T specified in managed_ptr must derive from Reclaimer::enable_concurrent_ptr");
58  static constexpr bool value = true;
59  };
60 } // namespace detail
61 
134 template <class Key, class Value, class... Policies>
136  using reclaimer = parameter::type_param_t<policy::reclaimer, parameter::nil, Policies...>;
137  using value_reclaimer = parameter::type_param_t<policy::value_reclaimer, parameter::nil, Policies...>;
138  using hash = parameter::type_param_t<policy::hash, xenium::hash<Key>, Policies...>;
139  using backoff = parameter::type_param_t<policy::backoff, no_backoff, Policies...>;
140 
141  template <class... NewPolicies>
142  using with = vyukov_hash_map<Key, Value, NewPolicies..., Policies...>;
143 
144  static_assert(parameter::is_set<reclaimer>::value, "reclaimer policy must be specified");
145 
146 private:
147  using traits = typename impl::vyukov_hash_map_traits<Key,
148  Value,
149  value_reclaimer,
150  reclaimer,
151  detail::vyukov_supported_type<Key>::value,
152  detail::vyukov_supported_type<Value>::value>;
153 
154 public:
155  explicit vyukov_hash_map(std::size_t initial_capacity = 128);
156  ~vyukov_hash_map();
157 
158  class iterator;
159  using accessor = typename traits::accessor;
160 
161  using key_type = typename traits::key_type;
162  using value_type = typename traits::value_type;
163 
177  bool emplace(key_type key, value_type value);
178 
194  template <class... Args>
195  std::pair<accessor, bool> get_or_emplace(key_type key, Args&&... args);
196 
215  template <class Factory>
216  std::pair<accessor, bool> get_or_emplace_lazy(key_type key, Factory&& factory);
217 
230  bool extract(const key_type& key, accessor& accessor);
231 
242  bool erase(const key_type& key);
243 
254  void erase(iterator& pos);
255 
268  bool try_get_value(const key_type& key, accessor& result) const;
269 
270  // TODO - implement contains
271  // bool contains(const key_type& key) const;
272 
282  iterator find(const key_type& key);
283 
291  iterator begin();
292 
301  iterator end() { return iterator(); }
302 
303 private:
304  struct unlocker;
305 
306  struct bucket_state;
307  struct bucket;
308  struct extension_item;
309  struct extension_bucket;
310  struct block;
311  using block_ptr = typename reclaimer::template concurrent_ptr<block, 0>;
312  using guarded_block = typename block_ptr::guard_ptr;
313 
314  static constexpr std::uint32_t bucket_to_extension_ratio = 128;
315  static constexpr std::uint32_t bucket_item_count = 3;
316  static constexpr std::uint32_t extension_item_count = 10;
317 
318  static constexpr std::size_t item_counter_bits = utils::find_last_bit_set(bucket_item_count);
319  static constexpr std::size_t lock_bit = 2 * item_counter_bits + 1;
320  static constexpr std::size_t version_shift = lock_bit;
321 
322  static constexpr std::uint32_t lock = 1u << (lock_bit - 1);
323  static constexpr std::size_t version_inc = static_cast<std::size_t>(1) << lock_bit;
324 
325  static constexpr std::uint32_t item_count_mask = (1u << item_counter_bits) - 1;
326  static constexpr std::uint32_t delete_item_mask = item_count_mask << item_counter_bits;
327 
328  static constexpr std::align_val_t cacheline_size{64};
329 
330  block_ptr data_block;
331  std::atomic<int> resize_lock;
332 
333  block* allocate_block(std::uint32_t bucket_count);
334 
335  bucket& lock_bucket(hash_t hash, guarded_block& block, bucket_state& state);
336  void grow(bucket& bucket, bucket_state state);
337  void do_grow();
338 
339  template <bool AcquireAccessor, class Factory, class Callback>
340  bool do_get_or_emplace(Key&& key, Factory&& factory, Callback&& callback);
341 
342  bool do_extract(const key_type& key, accessor& result);
343 
344  static extension_item* allocate_extension_item(block* b, hash_t hash);
345  static void free_extension_item(extension_item* item);
346 };
347 
366 template <class Key, class Value, class... Policies>
367 class vyukov_hash_map<Key, Value, Policies...>::iterator {
368 public:
369  using iterator_category = std::forward_iterator_tag;
370  using difference_type = std::ptrdiff_t;
371  using value_type = typename traits::iterator_value_type;
372  using reference = typename traits::iterator_reference;
373  using pointer = value_type*;
374 
375  iterator() = default;
376  ~iterator();
377 
378  iterator(iterator&&);
379  iterator& operator=(iterator&&);
380 
381  iterator(const iterator&) = delete;
382  iterator& operator=(const iterator&) = delete;
383 
384  bool operator==(const iterator& r) const;
385  bool operator!=(const iterator& r) const;
386  iterator& operator++();
387 
388  reference operator*();
389  pointer operator->();
390 
396  void reset();
397 
398 private:
399  guarded_block block{};
400  bucket* current_bucket{};
401  bucket_state current_bucket_state{};
402  std::uint32_t index{};
403  extension_item* extension{};
404  std::atomic<extension_item*>* prev{};
405  friend struct vyukov_hash_map;
406 
407  void move_to_next_bucket();
408  Value* erase_current();
409 };
410 
411 } // namespace xenium
412 
413 #define XENIUM_VYUKOV_HASH_MAP_IMPL
414 #include <xenium/impl/vyukov_hash_map.hpp>
415 #include <xenium/impl/vyukov_hash_map_traits.hpp>
416 #undef XENIUM_VYUKOV_HASH_MAP_IMPL
417 
418 #endif
xenium::vyukov_hash_map::iterator
A ForwardIterator to safely iterate vyukov_hash_map.
Definition: vyukov_hash_map.hpp:367
xenium::vyukov_hash_map::emplace
bool emplace(key_type key, value_type value)
Inserts a new element into the container if the container doesn't already contain an element with an ...
Definition: vyukov_hash_map.hpp:206
xenium::vyukov_hash_map::find
iterator find(const key_type &key)
Finds an element with key equivalent to key.
Definition: vyukov_hash_map.hpp:752
xenium::vyukov_hash_map
A concurrent hash-map that uses fine-grained locking.
Definition: vyukov_hash_map.hpp:135
xenium::policy::backoff
Policy to configure the backoff strategy.
Definition: policy.hpp:39
xenium::vyukov_hash_map::end
iterator end()
Returns an iterator to the element following the last element of the container.
Definition: vyukov_hash_map.hpp:301
xenium::policy::value_reclaimer
Policy to configure the reclaimer used for internally alloced nodes in vyukov_hash_map.
Definition: vyukov_hash_map.hpp:31
xenium::vyukov_hash_map::get_or_emplace_lazy
std::pair< accessor, bool > get_or_emplace_lazy(key_type key, Factory &&factory)
Inserts a new element into the container if the container doesn't already contain an element with an ...
xenium::vyukov_hash_map::extract
bool extract(const key_type &key, accessor &accessor)
Removes the element with the key equivalent to key (if one exists), and provides an accessor to the r...
Definition: vyukov_hash_map.hpp:311
xenium::vyukov_hash_map::erase
bool erase(const key_type &key)
Removes the element with the key equivalent to key (if one exists).
Definition: vyukov_hash_map.hpp:303
xenium::managed_ptr
A helper struct to define that the lifetime of value objects of type T has to be managed by the speci...
Definition: vyukov_hash_map.hpp:47
xenium::vyukov_hash_map::get_or_emplace
std::pair< accessor, bool > get_or_emplace(key_type key, Args &&... args)
Inserts a new element into the container if the container doesn't already contain an element with an ...
xenium::policy::reclaimer
Policy to configure the reclamation scheme to be used.
Definition: policy.hpp:25
xenium::no_backoff
Dummy backoff strategy that does nothing.
Definition: backoff.hpp:16
xenium::vyukov_hash_map::try_get_value
bool try_get_value(const key_type &key, accessor &result) const
Provides an accessor to the value associated with the specified key, if such an element exists in the...
Definition: vyukov_hash_map.hpp:506
xenium::vyukov_hash_map::begin
iterator begin()
Returns an iterator to the first element of the container.
Definition: vyukov_hash_map.hpp:781