Threads Case Study: PThreads

Creation

To represent the thread data structure, PThread supports a pthread_t data type in C.

pthread_t aThread;

The data type contains an ID, execution state, and any other information that's relevant to the thread.

int pthread_create(pthread_t *thread,
                   pthread_attr_t *attr,
                   void * (*start_routine)(void *),
                   void *arg);

Attributes

Pthread attributes allow us to specify the stack size, scheduling policy, inheritance, joinablility, priority, and system process scope of the newly created pthread. If NULL is passed in, all these attributes will be instantiated with some default values. There are multiple API calls available to pthread attributes.

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
pthread_attr_{set/get}{attribute}

Detachable

In pthreads, the default behavior of thread creation is joinable. With joinable threads, the parent thread creates children threads and can join them at a later time. The parent thread will not terminate until the children threads have completed their execution and have been joined via the explicit join operation.

In the event of parent thread exits early and fails to terminate children threads properly, the children threads can become zombie threads and eat up memory because it is expecting to join the parent before releasing its resources. However, if we specify the thread to be detachable, the thread can continue execute and release its resource upon termination.

Here's an example of detached threads.

#include <stdio.h>
#include <pthread.h>

void *foo(void *arg) {
    printf("Foobar!\n")
    pthread_exit(NULL);
}

int main(void) {
    int i;
    pthread_t tid;

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    pthread_create(NULL, &attr, foo, NULL);

    return 0;
}

Mutexes

The mutex type is represented by the following construct.

pthread_mutex_t my_mutex;

While the lock and unlock operation will have the following signature.

int pthread_mutex_lock(pthread_mutex_t* mutex);
int pthread_mutex_unlock(pthread_mutex_t* mutex);

Here's an example of thread safe insertion to a list.

list<int> my_list;
pthread_mutex_t m;

void safe_insert(int i) {
    pthread_mutex_lock(m);
    my_list.insert(i);
    pthread_mutex_unlock(m);
}

Other Mutex Operations

Mutex needs to be initialized. The attribute specifies the mutex behavior when it is shared among processes.

int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr);

We can also check a mutex and see if it is locked. If it is locked, the check function will return immediately instead of being blocked.

int pthread_mutex_trylock(pthread_mutex_t* mutex);

Finally we need to destroy the mutex to free up memory.

int pthread_mutex_destroy(pthread_mutex_t* mutex);

Mutex Safety

  • Shared data should always be access through a single mutex.

  • Mutex scope must be visible to all threads.

  • We should globally order locks. This is to ensure there is no deadlock in the operations.

Condition Variables

The condition variable type is represented by the following construct.

pthread_cond_t cond;

The wait operation takes two arguments, the condition and the mutex. A thread that is entering the wait operation will automatically release the mutex and place itself on the wait queue that's associated with the condition variable. When the thread is woken up, it will automatically re-acquire the mutex before exiting the wait operation.

int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);

Here are signal and broadcast operations.

int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);

Other Cond Operations

int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr);
int pthread_cond_destroy(pthread_cond_t* cond);

Cond Safety

  • Do not forget to notify waiting threads.

  • When in doubt, use broadcast.

  • You do not need a mutex to signal or broadcast.

Last updated