| 3525 | |
| 3526 | #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED |
| 3527 | void implicit_producer_thread_exited(ImplicitProducer* producer) |
| 3528 | { |
| 3529 | // Remove from thread exit listeners |
| 3530 | details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener); |
| 3531 | |
| 3532 | // Remove from hash |
| 3533 | #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH |
| 3534 | debug::DebugLock lock(implicitProdMutex); |
| 3535 | #endif |
| 3536 | auto hash = implicitProducerHash.load(std::memory_order_acquire); |
| 3537 | assert(hash != nullptr); // The thread exit listener is only registered if we were added to a hash in the first place |
| 3538 | auto id = details::thread_id(); |
| 3539 | auto hashedId = details::hash_thread_id(id); |
| 3540 | details::thread_id_t probedKey; |
| 3541 | |
| 3542 | // We need to traverse all the hashes just in case other threads aren't on the current one yet and are |
| 3543 | // trying to add an entry thinking there's a free slot (because they reused a producer) |
| 3544 | for (; hash != nullptr; hash = hash->prev) { |
| 3545 | auto index = hashedId; |
| 3546 | do { |
| 3547 | index &= hash->capacity - 1; |
| 3548 | probedKey = hash->entries[index].key.load(std::memory_order_relaxed); |
| 3549 | if (probedKey == id) { |
| 3550 | hash->entries[index].key.store(details::invalid_thread_id2, std::memory_order_release); |
| 3551 | break; |
| 3552 | } |
| 3553 | ++index; |
| 3554 | } 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 |
| 3555 | } |
| 3556 | |
| 3557 | // Mark the queue as being recyclable |
| 3558 | producer->inactive.store(true, std::memory_order_release); |
| 3559 | } |
| 3560 | |
| 3561 | static void implicit_producer_thread_exited_callback(void* userData) |
| 3562 | { |
nothing calls this directly
no test coverage detected