77
77
#include < climits> // for CHAR_BIT
78
78
#include < array>
79
79
#include < thread> // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading
80
+ #include < mutex> // used for thread exit synchronization
80
81
81
82
// Platform-specific definitions of a numeric thread ID type and an invalid value
82
83
namespace moodycamel { namespace details {
@@ -562,29 +563,38 @@ namespace details
562
563
typedef RelacyThreadExitListener ThreadExitListener;
563
564
typedef RelacyThreadExitNotifier ThreadExitNotifier;
564
565
#else
566
+ class ThreadExitNotifier ;
567
+
565
568
struct ThreadExitListener
566
569
{
567
570
typedef void (*callback_t )(void *);
568
571
callback_t callback;
569
572
void * userData;
570
573
571
574
ThreadExitListener* next; // reserved for use by the ThreadExitNotifier
575
+ ThreadExitNotifier* chain; // reserved for use by the ThreadExitNotifier
572
576
};
573
-
574
-
577
+
575
578
class ThreadExitNotifier
576
579
{
577
580
public:
578
581
static void subscribe (ThreadExitListener* listener)
579
582
{
580
583
auto & tlsInst = instance ();
584
+ std::lock_guard<std::mutex> guard (mutex ());
581
585
listener->next = tlsInst.tail ;
586
+ listener->chain = &tlsInst;
582
587
tlsInst.tail = listener;
583
588
}
584
589
585
590
static void unsubscribe (ThreadExitListener* listener)
586
591
{
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 ;
588
598
ThreadExitListener** prev = &tlsInst.tail ;
589
599
for (auto ptr = tlsInst.tail ; ptr != nullptr ; ptr = ptr->next ) {
590
600
if (ptr == listener) {
@@ -604,7 +614,9 @@ namespace details
604
614
{
605
615
// This thread is about to exit, let everyone know!
606
616
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 ());
607
618
for (auto ptr = tail; ptr != nullptr ; ptr = ptr->next ) {
619
+ ptr->chain = nullptr ;
608
620
ptr->callback (ptr->userData );
609
621
}
610
622
}
@@ -615,6 +627,13 @@ namespace details
615
627
static thread_local ThreadExitNotifier notifier;
616
628
return notifier;
617
629
}
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
+ }
618
637
619
638
private:
620
639
ThreadExitListener* tail;
@@ -3521,9 +3540,6 @@ class ConcurrentQueue
3521
3540
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3522
3541
void implicit_producer_thread_exited (ImplicitProducer* producer)
3523
3542
{
3524
- // Remove from thread exit listeners
3525
- details::ThreadExitNotifier::unsubscribe (&producer->threadExitListener );
3526
-
3527
3543
// Remove from hash
3528
3544
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3529
3545
debug::DebugLock lock (implicitProdMutex);
0 commit comments