I thought of 2 solutions, both valid, yet each suited for particular purposes the other doesn't.
- Use RW-Lock provided by the host, and include explicit memory fencing call when releasing he reader lock. Example of releasing reader lock:
#include <stdatomic.h>
#include <pthread.h>
...
atomic_thread_fence(memory_order_release);
pthread_rwlock_unlock(&gc->thr_xor_gc);
...
Advantages:
- performance optimized by OS,
- realtime-aware,
- Use regular mutices and condition variables. Example of releasing reader lock:
#include <pthread.h>
...
pthread_lock(&gc->lock_main); // the master lock of the entire GC structure.
if( &gc->inprogress_gc ) // the boolean indicating whether GC thread is active
{
pthread_unlock(&gc->lock_main);
return; // may return a value if appropriate.
}
gc->inprogress_threads --; // count of reading threads.
pthread_cond_signal(&gc->cv_writer);
pthread_unlock(&gc->lock_main);
...
Advantages:
If I want application threads to stall when there's any thread that's blocked attempting to do GC, I can customimze this behavior (where as regular RW-Lock cannot do that explicitly, unless optional POSIX APIs that alters scheduling are available).
All fences are implicit.
The latest GCC and Clang compilers support the atomic_thread_fence function, and Single Unix Specification mandate implementations to support all pthread interfaces. For earlier versions of the compilers and OSes, compiler built-ins and vendor-specific APIs may be available. For toolchains and platforms LACKING ALL of these, probably they'll not be supporting multi-threading in the first place.