I have code that locks every function (of a certain library), and which I would like to optimize. Given functions A
and B
, I don't mind any A
running concurrently with any other A
, or any B
running concurrently with any other B
, but no A
can run while any B
is running, and vice-versa. The thread count is dynamic and for reason out of my control I am forced to use static allocation for mutexes and conditional variables (i.e. PTHREAD_MUTEX_INITIALIZER
).
I have a hunch that the most efficient approach is two conditional variables. Using pthread_mutex_trylock
allows one function (i.e. A
) to run in parallel while the other must be serialized. Also *trylock
with static initialization is actually undefined behavior...
Edit:
Perhaps something like this? I'm not sure if this:
- Can be simpler. After all, mutexes are implemented using semaphores, but it takes four mutexes and two conditional variables to implement what is basically an inverse semaphore.
- Covers all race conditions.
- Is "fair" (beyond default priority and scheduling)?
static int countA = 0;
static int countB = 0;
static pthread_mutex_t lockCountA = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t lockCountB = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t lockA = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t lockB = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t condA = PTHREAD_COND_INITIALIZER;
static pthread_cond_t condB = PTHREAD_COND_INITIALIZER;
// for B(), just s/B/A/g
static void A(void) {
pthread_mutex_lock(&lockB);
while(countB)
pthread_cond_wait(&condB, &lockB);
pthread_mutex_lock(&lockCountA);
countA += 1;
pthread_mutex_unlock(&lockCountA)
doA();
pthread_mutex_lock(&lockCountA)
countA -= 1;
if countA == 0:
pthread_cond_signal(&condA);
mutex_unlock(&lockCountA)
mutex_unlock(&lockB);
}