MCPcopy Create free account
hub / github.com/cameron314/concurrentqueue / get_or_create

Method get_or_create

tests/corealgos.h:211–328  ·  view source on GitHub ↗

Only fails (returns nullptr) if memory allocation fails

Source from the content-addressed store, hash-verified

209
210 // Only fails (returns nullptr) if memory allocation fails
211 T* get_or_create()
212 {
213 // Note that since the data is essentially thread-local (key is thread ID),
214 // there's a reduced need for fences (memory ordering is already consistent
215 // for any individual thread), except for the current table itself
216
217 // Start by looking for the thread ID in the current and all previous hash tables.
218 // If it's not found, it must not be in there yet, since this same thread would
219 // have added it previously to one of the tables that we traversed.
220
221 // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table
222
223 auto id = details::thread_id();
224 auto hashedId = details::hash_thread_id(id);
225
226 auto mainHash = currentHash.load(std::memory_order_acquire);
227 for (auto hash = mainHash; hash != nullptr; hash = hash->prev) {
228 // Look for the id in this hash
229 auto index = hashedId;
230 while (true) { // Not an infinite loop because at least one slot is free in the hash table
231 index &= hash->capacity - 1;
232
233 auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed);
234 if (probedKey == id) {
235 // Found it! If we had to search several hashes deep, though, we should lazily add it
236 // to the current main hash table to avoid the extended search next time.
237 // Note there's guaranteed to be room in the current hash table since every subsequent
238 // table implicitly reserves space for all previous tables (there's only one
239 // currentHashCount).
240 auto value = hash->entries[index].value;
241 if (hash != mainHash) {
242 index = hashedId;
243 while (true) {
244 index &= mainHash->capacity - 1;
245 probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed);
246 auto expected = details::invalid_thread_id;
247 if (probedKey == expected && mainHash->entries[index].key.compare_exchange_strong(expected, id, std::memory_order_relaxed)) {
248 mainHash->entries[index].value = value;
249 break;
250 }
251 ++index;
252 }
253 }
254
255 return value;
256 }
257 if (probedKey == details::invalid_thread_id) {
258 break; // Not in this hash table
259 }
260 ++index;
261 }
262 }
263
264 // Insert!
265 auto newCount = 1 + currentHashCount.fetch_add(1, std::memory_order_relaxed);
266 while (true) {
267 if (newCount >= (mainHash->capacity >> 1) && !resizeInProgress.test_and_set(std::memory_order_acquire)) {
268 // We've acquired the resize lock, try to allocate a bigger hash table.

Callers 2

run_testFunction · 0.80
core_thread_localMethod · 0.80

Calls 9

test_and_setMethod · 0.80
thread_idFunction · 0.70
hash_thread_idFunction · 0.70
loadMethod · 0.45
fetch_addMethod · 0.45
clearMethod · 0.45
storeMethod · 0.45
addMethod · 0.45

Tested by 2

run_testFunction · 0.64
core_thread_localMethod · 0.64