HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
wfree_unordered_map.hpp
1// Copyright Take Vos 2019-2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "required.hpp"
8#include <atomic>
9#include <array>
10#include <optional>
11#include <vector>
12#include <typeinfo>
13#include <typeindex>
14
15namespace tt {
16
17template<typename K, typename V>
23
33 K key;
34
35 template<typename X=K, std::enable_if_t<std::is_same_v<X,std::type_index>, int> = 0>
36 constexpr wfree_unordered_map_item() noexcept :
37 value(), hash(0), key(std::type_index(typeid(void))) {}
38
39 template<typename X=K, std::enable_if_t<!std::is_same_v<X,std::type_index>, int> = 0>
40 constexpr wfree_unordered_map_item() noexcept :
41 value(), hash(0), key() {}
42
43 constexpr wfree_unordered_map_item(wfree_unordered_map_item const &) noexcept = delete;
44 constexpr wfree_unordered_map_item(wfree_unordered_map_item &&) noexcept = delete;
45 ~wfree_unordered_map_item() noexcept = default;
46 constexpr wfree_unordered_map_item &operator=(wfree_unordered_map_item const &) noexcept = delete;
47 constexpr wfree_unordered_map_item &operator=(wfree_unordered_map_item &&) noexcept = delete;
48};
49
55template<typename K, typename V, size_t MAX_NR_ITEMS>
57public:
58 using key_type = K;
59 using mapped_type = V;
60
61private:
62 static constexpr size_t CAPACITY = MAX_NR_ITEMS * 2;
63
64 std::array<wfree_unordered_map_item<K,V>, CAPACITY> items = {};
65
66public:
67 constexpr wfree_unordered_map() noexcept = default;
68 constexpr wfree_unordered_map(wfree_unordered_map const &) noexcept = delete;
69 constexpr wfree_unordered_map(wfree_unordered_map &&) noexcept = delete;
70 ~wfree_unordered_map() noexcept = default;
71 constexpr wfree_unordered_map &operator=(wfree_unordered_map const &) noexcept = delete;
72 constexpr wfree_unordered_map &operator=(wfree_unordered_map &&) noexcept = delete;
73
74 static size_t make_hash(K const &key) noexcept {
75 ttlet hash = std::hash<K>{}(key);
76 return hash >= 3 ? hash : hash + 3;
77 }
78
79 void insert(K key, V value) noexcept {
80 ttlet hash = make_hash(key);
81
82 auto index = hash % CAPACITY;
83 while (true) {
84 auto &item = items[index];
85
86 // First look for an empty (0) item, highly likely when doing insert.
87 size_t item_hash = 0;
88 if (item.hash.compare_exchange_strong(item_hash, 1, std::memory_order_acquire)) {
89 // Success, we found an empty entry, we marked it as busy (1).
90 item.key = std::move(key);
91 item.value = value;
92 item.hash.store(hash, std::memory_order_release);
93 return;
94
95 } else if (item_hash == hash && key == item.key) {
96 // Key was already in map, replace the value.
97 item.value = value;
98 return;
99
100 } else {
101 // Either this item was already in use by another key, or
102 // Another thread was ahead of us with claiming this item (hopefully the other
103 // thread doesn't insert the same key).
104 // Even though compare_exchange was used here, this algorithm is wait-free since all threads
105 // including the one here are making normal progress.
106 index = (index + 1) % CAPACITY;
107 }
108 }
109 }
110
111 std::vector<K> keys() const noexcept {
113 // XXX - with counting items, we could reserve capacity.
114
115 for (ttlet &item: items) {
116 if (item.hash >= 3) {
117 r.push_back(item.key);
118 }
119 }
120 return r;
121 }
122
123 V& operator[](K const &key) noexcept {
124 ttlet hash = make_hash(key);
125
126 auto index = hash % CAPACITY;
127 while (true) {
128 auto &item = items[index];
129
130 // First look for an empty (0) item, highly likely when doing insert.
131 size_t item_hash = 0;
132 if (item.hash.compare_exchange_strong(item_hash, 1, std::memory_order_acquire)) {
133 // Success, we found an empty entry, we marked it as busy (1).
134 item.key = std::move(key);
135 item.hash.store(hash, std::memory_order_release);
136
137 if constexpr (std::is_default_constructible_v<V>) {
138 item.value = {};
139 } else {
140 // Make sure we default initialize the memory.
141 std::memset(&(item.value), 0, sizeof(item.value));
142 }
143 return item.value;
144
145 } else if (item_hash == hash && key == item.key) {
146 // Key was already in map, replace the value.
147 return item.value;
148
149 } else {
150 // Either this item was already in use by another key, or
151 // Another thread was ahead of us with claiming this item (hopefully the other
152 // thread doesn't insert the same key).
153 // Even though compare_exchange was used here, this algorithm is wait-free since all threads
154 // including the one here are making normal progress.
155 index = (index + 1) % CAPACITY;
156 }
157 }
158 }
159
160 std::optional<V> get(K const &key) const noexcept {
161 ttlet hash = make_hash(key);
162
163 auto index = hash % CAPACITY;
164 while (true) {
165 auto &item = items[index];
166
167 auto item_hash = item.hash.load(std::memory_order_acquire);
168
169 if (item_hash == hash && key == item.key) {
170 // Found key
171 return { item.value };
172
173 } else if (item_hash == 0) {
174 // Item is empty.
175 return {};
176
177 } else {
178 index = (index + 1) % CAPACITY;
179 }
180 }
181 }
182
183 V get(K const &key, V const &default_value) const noexcept {
184 if (ttlet optional_value = get(key)) {
185 return *optional_value;
186 } else {
187 return default_value;
188 }
189 }
190
191 std::optional<V> erase(K const &key) noexcept {
192 ttlet hash = make_hash(key);
193
194 auto index = hash % CAPACITY;
195 while (true) {
196 auto &item = items[index];
197 auto item_hash = item.hash.load(std::memory_order_acquire);
198
199 if (item_hash == hash && key == item.key) {
200 // Set tombstone. Don't actually delete the key or value.
201 item.hash.store(1, std::memory_order_release);
202 return { item.value };
203
204 } else if (item_hash == 0) {
205 // Item is empty.
206 return {};
207
208 } else {
209 index = (index + 1) % CAPACITY;
210 }
211 }
212 }
213};
214
215
216}
STL namespace.
Definition wfree_unordered_map.hpp:18
std::atomic< size_t > hash
Definition wfree_unordered_map.hpp:32
V value
Definition wfree_unordered_map.hpp:22
Definition wfree_unordered_map.hpp:56
T memset(T... args)
T move(T... args)
T push_back(T... args)