| 3556 | |
| 3557 | #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED |
| 3558 | void implicit_producer_thread_exited(ImplicitProducer* producer) |
| 3559 | { |
| 3560 | // Remove from hash |
| 3561 | #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH |
| 3562 | debug::DebugLock lock(implicitProdMutex); |
| 3563 | #endif |
| 3564 | auto hash = implicitProducerHash.load(std::memory_order_acquire); |
| 3565 | assert(hash != nullptr); // The thread exit listener is only registered if we were added to a hash in the first place |
| 3566 | auto id = details::thread_id(); |
| 3567 | auto hashedId = details::hash_thread_id(id); |
| 3568 | details::thread_id_t probedKey; |
| 3569 | |
| 3570 | // We need to traverse all the hashes just in case other threads aren't on the current one yet and are |
| 3571 | // trying to add an entry thinking there's a free slot (because they reused a producer) |
| 3572 | for (; hash != nullptr; hash = hash->prev) { |
| 3573 | auto index = hashedId; |
| 3574 | do { |
| 3575 | index &= hash->capacity - 1u; |
| 3576 | probedKey = id; |
| 3577 | if (hash->entries[index].key.compare_exchange_strong(probedKey, details::invalid_thread_id2, std::memory_order_seq_cst, std::memory_order_relaxed)) { |
| 3578 | break; |
| 3579 | } |
| 3580 | ++index; |
| 3581 | } while (probedKey != details::invalid_thread_id); // Can happen if the hash has changed but we weren't put back in it yet, or if we weren't added to this hash in the first place |
| 3582 | } |
| 3583 | |
| 3584 | // Mark the queue as being recyclable |
| 3585 | producer->inactive.store(true, std::memory_order_release); |
| 3586 | } |
| 3587 | |
| 3588 | static void implicit_producer_thread_exited_callback(void* userData) |
| 3589 | { |
nothing calls this directly
no test coverage detected