7777#include < climits> // for CHAR_BIT
7878#include < array>
7979#include < thread> // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading
80+ #include < mutex> // used for thread exit synchronization
8081
8182// Platform-specific definitions of a numeric thread ID type and an invalid value
8283namespace moodycamel { namespace details {
@@ -562,29 +563,38 @@ namespace details
562563 typedef RelacyThreadExitListener ThreadExitListener;
563564 typedef RelacyThreadExitNotifier ThreadExitNotifier;
564565#else
566+ class ThreadExitNotifier ;
567+
565568 struct ThreadExitListener
566569 {
567570 typedef void (*callback_t )(void *);
568571 callback_t callback;
569572 void * userData;
570573
571574 ThreadExitListener* next; // reserved for use by the ThreadExitNotifier
575+ ThreadExitNotifier* chain; // reserved for use by the ThreadExitNotifier
572576 };
573-
574-
577+
575578 class ThreadExitNotifier
576579 {
577580 public:
578581 static void subscribe (ThreadExitListener* listener)
579582 {
580583 auto & tlsInst = instance ();
584+ std::lock_guard<std::mutex> guard (mutex ());
581585 listener->next = tlsInst.tail ;
586+ listener->chain = &tlsInst;
582587 tlsInst.tail = listener;
583588 }
584589
585590 static void unsubscribe (ThreadExitListener* listener)
586591 {
587- auto & tlsInst = instance ();
592+ std::lock_guard<std::mutex> guard (mutex ());
593+ if (!listener->chain ) {
594+ return ; // race with ~ThreadExitNotifier
595+ }
596+ auto & tlsInst = *listener->chain ;
597+ listener->chain = nullptr ;
588598 ThreadExitListener** prev = &tlsInst.tail ;
589599 for (auto ptr = tlsInst.tail ; ptr != nullptr ; ptr = ptr->next ) {
590600 if (ptr == listener) {
@@ -604,7 +614,9 @@ namespace details
604614 {
605615 // This thread is about to exit, let everyone know!
606616 assert (this == &instance () && " If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined." );
617+ std::lock_guard<std::mutex> guard (mutex ());
607618 for (auto ptr = tail; ptr != nullptr ; ptr = ptr->next ) {
619+ ptr->chain = nullptr ;
608620 ptr->callback (ptr->userData );
609621 }
610622 }
@@ -615,6 +627,13 @@ namespace details
615627 static thread_local ThreadExitNotifier notifier;
616628 return notifier;
617629 }
630+
631+ static inline std::mutex& mutex ()
632+ {
633+ // Must be static because the ThreadExitNotifier could be destroyed while unsubscribe is called
634+ static std::mutex mutex;
635+ return mutex;
636+ }
618637
619638 private:
620639 ThreadExitListener* tail;
@@ -3521,9 +3540,6 @@ class ConcurrentQueue
35213540#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
35223541 void implicit_producer_thread_exited (ImplicitProducer* producer)
35233542 {
3524- // Remove from thread exit listeners
3525- details::ThreadExitNotifier::unsubscribe (&producer->threadExitListener );
3526-
35273543 // Remove from hash
35283544#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
35293545 debug::DebugLock lock (implicitProdMutex);
0 commit comments