I have added a nested Watch class to ThreadCollisionWarning class that detects also if a critical section is ever used by two different threads ( for example you can detect if a given class is constructed and destroyed within the same thread).
The code is the following:
#ifndef THREAD_COLLISION_WARNING #define THREAD_COLLISION_WARNING #include <stdexcept> #ifdef NDEBUG #define THREAD_WATCH(obj) #define SCOPED_WATCH(obj) #define WATCH(obj) #else #define THREAD_WATCH(obj) ThreadCollisionWarning _##obj; #define SCOPED_WATCH(obj) ThreadCollisionWarning::ScopedWatch scoped_watch_##obj(_##obj); #define WATCH(obj) ThreadCollisionWarning::Watch watch_##obj(_##obj); #endif class ThreadCollisionWarning { public: ThreadCollisionWarning() :theActiveThread(0) { } ~ThreadCollisionWarning() { } class Watch { public: Watch(ThreadCollisionWarning& aTCW) :theWarner(aTCW) { theWarner.enter_self(); } ~Watch() { } private: ThreadCollisionWarning& theWarner; }; class ScopedWatch { public: ScopedWatch(ThreadCollisionWarning& aTCW) :theWarner(aTCW) { theWarner.enter(); } ~ScopedWatch() { theWarner.leave(); } private: ThreadCollisionWarning& theWarner; }; private: void enter_self() { //If the active thread is 0 then I'll write the current thread ID //if two or more threads arrive here only one will success to write on theActiveThread //the current thread ID if (! __sync_bool_compare_and_swap(&theActiveThread, 0, pthread_self())) { //Last chance! may be is the thread itself calling from a critical section //another critical section if (!__sync_bool_compare_and_swap(&theActiveThread, pthread_self(), theActiveThread)) { throw std::runtime_error("Thread Collision"); } } } void enter() { if (!__sync_bool_compare_and_swap(&theActiveThread, 0, pthread_self())) { //gotcha! another thread is trying to use the same class throw std::runtime_error("Thread Collision"); } } void leave() { __sync_fetch_and_xor(&theActiveThread, theActiveThread); } pthread_t theActiveThread; }; #endif
The nested Watch class (used by WATCH macro) just during his constructor initializes theActiveThread member with the current id thread if it isn't still initialized, in case it gives another chance to check if the active thread is itself.
So let's see some examples of use:
Case #1: Check that one thread ever uses some critical section (recursion allowed)
struct Shared { void foo() { WATCH(CriticaSectionA); bar(); } void bar() { WATCH(CriticaSectionA); } THREAD_WATCH(CriticaSectionA); };
Case #2: Check that a class is constructed and destroyed inside the same thread
struct Shared { Shared() { WATCH(CTOR_DTOR_SECTION); ... } ~Shared() { WATCH(CTOR_DTOR_SECTION); ... } THREAD_WATCH(CTOR_DTOR_SECTION); };
note that doing so the Shared destructor can throw an exception, so do not use this in a production code (put the WATCH between a try-catch and just notify it in some way).
Case #3: Two or more different threads can enter a critical section but in exclusive way (useful to check if external sync mechanism are working).
struct Shared { foo() { SCOPED_WATCH(CriticalSectionA); } THREAD_WATCH(CriticalSectionA); };