MCPcopy Create free account
hub / github.com/Snapchat/KeyDB / get_or_add_implicit_producer

Function get_or_add_implicit_producer

deps/concurrentqueue/concurrentqueue.h:3374–3675  ·  view source on GitHub ↗

Only fails (returns nullptr) if memory allocation fails

Source from the content-addressed store, hash-verified

3372
3373 // Only fails (returns nullptr) if memory allocation fails
3374 ImplicitProducer* get_or_add_implicit_producer()
3375 {
3376 // Note that since the data is essentially thread-local (key is thread ID),
3377 // there's a reduced need for fences (memory ordering is already consistent
3378 // for any individual thread), except for the current table itself.
3379
3380 // Start by looking for the thread ID in the current and all previous hash tables.
3381 // If it's not found, it must not be in there yet, since this same thread would
3382 // have added it previously to one of the tables that we traversed.
3383
3384 // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table
3385
3386#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3387 debug::DebugLock lock(implicitProdMutex);
3388#endif
3389
3390 auto id = details::thread_id();
3391 auto hashedId = details::hash_thread_id(id);
3392
3393 auto mainHash = implicitProducerHash.load(std::memory_order_acquire);
3394 assert(mainHash != nullptr); // silence clang-tidy and MSVC warnings (hash cannot be null)
3395 for (auto hash = mainHash; hash != nullptr; hash = hash->prev) {
3396 // Look for the id in this hash
3397 auto index = hashedId;
3398 while (true) { // Not an infinite loop because at least one slot is free in the hash table
3399 index &= hash->capacity - 1;
3400
3401 auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed);
3402 if (probedKey == id) {
3403 // Found it! If we had to search several hashes deep, though, we should lazily add it
3404 // to the current main hash table to avoid the extended search next time.
3405 // Note there's guaranteed to be room in the current hash table since every subsequent
3406 // table implicitly reserves space for all previous tables (there's only one
3407 // implicitProducerHashCount).
3408 auto value = hash->entries[index].value;
3409 if (hash != mainHash) {
3410 index = hashedId;
3411 while (true) {
3412 index &= mainHash->capacity - 1;
3413 probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed);
3414 auto empty = details::invalid_thread_id;
3415#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3416 auto reusable = details::invalid_thread_id2;
3417 if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed)) ||
3418 (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) {
3419#else
3420 if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed))) {
3421#endif
3422 mainHash->entries[index].value = value;
3423 break;
3424 }
3425 ++index;
3426 }
3427 }
3428
3429 return value;
3430 }
3431 if (probedKey == details::invalid_thread_id) {

Callers 2

inner_enqueueMethod · 0.85
inner_enqueue_bulkMethod · 0.85

Calls 4

thread_idFunction · 0.85
hash_thread_idFunction · 0.85
clearMethod · 0.45

Tested by

no test coverage detected