Brain Dump

Thread Safety

Tags
comp-arch security

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;
}
Code Snippet 1: Example of a thread safe (statically sized) stack implementation (without underflow/overflow checks). Each operation including push, pop, and is_empty are restricted behind a semaphore and therefore cannot be run by two threads at the same time.

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.