Only fails (returns nullptr) if memory allocation fails
| 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) { |
no test coverage detected