Thread Safety
Is the idea of preventing race-condition and ensuring the semantics of a data-structure (or operation) remains consistent when called in a multi-threaded environment.
The common way to enforce thread-safety is by wrapping a mutex-lock around critical sections (sections where only one thread may have access at any time) of our code.
Thread Safe Stack example
#define STACK_SIZE 20
int count;
double values[STACK_SIZE];
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
void push(double v) {
pthread_mutex_lock(&m);
values[count++] = v;
pthread_mutex_unlock(&m);
}
double pop() {
pthread_mutex_lock(&m);
double v = values[--count];
pthread_mutex_unlock(&m);
return v;
}
int is_empty() {
pthread_mutex_lock(&m);
int result = count == 0;
pthread_mutex_unlock(&m);
return result;
}
Note: The operations in figure fig:code:thread-safe-stack are thread-safe but the
result is not guaranteed to be up-to-date by the time its returned to the caller.
Consider for example a situation in which one thread pushes to it, another calls
is_empty
and another pushes once again. If these threads are unlocked in the same
order as the calls then by time the thread that calls is_empty
does something with
the result the information would already be out of date.
This is the usually the reason why functions that return sizes are deprecated or
removed in thread-safe data structures.