I have an array that must be shared between threads, protected by a semaphore. I’ve put the initialization code inside a function that can be called multiple times, a “constructor”, as follows:
#include <stdbool.h> //for bool
#include <semaphore.h>
sem_t global_mutex;
char global_array[N]; // Protected with global_mutex
struct my_struct *new_my_struct(){
static bool is_init = false; // This will be initialized only once, right?
if (!is_init){ // 1
sem_init(&global_mutex, 0, 1); // 2
sem_wait(&global_mutex); // 3
if (!is_init){ // 4
is_init = true; // 5
... initialize global_array ... // 6
}
sem_post(&global_mutex); // 7
}
... proceed on the create and return a my_struct pointer ...
}
In an ideal world, a thread would run from 1 to 7, initialize the array and exit the critical region. Even if another thread had stopped in 2, the test in 4 would be false and the array wouldn’t be overwritten. I haven’t thinked much of what would happen if a thread stuck in 1 and reinitialized the semaphore, but I believe it isn’t of much concern as long as is_init be set to true by the first thread to run!
Now, there is a race condition if a thread stops in 4, and another one runs from the beggining to completion, initializing and populating the global_array. When the thread stopped at 4 runs, it will reinitialize the array and delete the state stored by the first thread.
I would like to know if there is any way to not suffer that race condition (maybe a clever use of static?) or if I should separate the initialization code from the constructor and use it in the main thread, when there’s no concurrency.
This code is in use and I haven’t suffered from a race condition yet. However, as I know its possible, I’d wish to correct it.
If the real use of the semaphore is really as a mutex, use just that
pthread_mutex_t. These can be initialized statically, so your problem would disappear.The syntax would be
If you really need a dynamic initialization of a global object, have a look into
pthread_once. This is the type (pthread_once_t) and function that is foreseen by POSIX for such a task.