Thread API
Here we present the API the OS provides for thread creation and control
Thread creation
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg
);
Arguments of this function:
thread
: A pointer to a structure of typepthread_t
, this struct is used to interact with threadsaatr
: Used to specify any attributes this thread might have (Stack size, scheduling priority, etc).start_routine
: Function the thread should start running. This is a pointer to a function with return typevoid *
.arg
is the argument to be passed to the function where the thread begins execution.
Thread completion
What if we want to wait for a thread to complete? You must call the routine pthread_join()
pthread_join(pthread_t th, void **thread_return);
This routine takes two arguments:
th
: Of typepthread_t
indicates which thread to wait for.thread_return
: It's a pointer to avoid *
pointer, used to store the return value of the function that the thread is executing. If we don't care about the return value, we can useNULL
You should never return a pointer to a value allocated in the stack of a thread. When the thread ends its execution, it is destroyed alongside his stack, hence the value that the pointer in pointing to is lost and we get UB.
Locks
Mutual exclusion to a critical section via locks. The most basic pair of routines to use for this purpose is provided by the following:
pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_unlock(pthread_mutex_t *mutex);
These can be used to create a "container" around our critical block of code:
pthread_mutex_t lock;
...
pthread_mutex_lock(&lock);
x = x + 1; // Or whatever your critical section is
pthread_mutex_unlock(&lock);
This works as follows:
- If no other thread holds the lock when
pthread_mutex_lock(&lock);
is called, then the thread will acquire the lock. - If other thread holds the lock,
pthread_mutex_lock(&lock);
will "wait" for the lock to become available. - The thread makes the critical section operation.
- The thread releases the lock using
pthread_mutex_unlock(&lock);
This code snippet is badly written (for simplicity) since:
- The lock is poorly initialized, and it should be initialized either using
PTHREAD_MUTEX_INITIALIZER
at compile time orpthread_mutex_init()
at runtime. - The code doesn't check error codes when calling lock and unlock.
Condition Variables
Use when a thread has to wait for other thread on some condition state. The two primary routines look like this:
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
pthread_cond_signal(pthread_cond_t *cond);
To use a condition variable, we need a lock that is associated with this condition. When calling either of the above routines, the lock should be held.