<threads.h>
Multithreading FunctionsFunction | Description |
---|---|
call_once() |
Call a function one time no matter how many threads try |
cnd_broadcast() |
Wake up all threads waiting on a condition variable |
cnd_destroy() |
Free up resources from a condition variable |
cnd_init() |
Initialize a condition variable to make it ready for use |
cnd_signal() |
Wake up a thread waiting on a condition variable |
cnd_timedwait() |
Wait on a condition variable with a timeout |
cnd_wait() |
Wait for a signal on a condition variable |
mtx_destroy() |
Cleanup a mutex when done with it |
mtx_init() |
Initialize a mutex for use |
mtx_lock() |
Acquire a lock on a mutex |
mtx_timedlock() |
Lock a mutex allowing for timeout |
mtx_trylock() |
Try to lock a mutex, returning if not possible |
mtx_unlock() |
Free a mutex when you’re done with the critical section |
thrd_create() |
Create a new thread of execution |
thrd_current() |
Get the ID of the calling thread |
thrd_detach() |
Automatically clean up threads when they exit |
thrd_equal() |
Compare two thread descriptors for equality |
thrd_exit() |
Stop and exit this thread |
thrd_join() |
Wait for a thread to exit |
thrd_yield() |
Stop running that other threads might run |
tss_create() |
Create new thread-specific storage |
tss_delete() |
Clean up a thread-specific storage variable |
tss_get() |
Get thread-specific data |
tss_set() |
Set thread-specific data |
We have a bunch of good things at our disposal with this one:
call_once()
function!Enjoy!
call_once()
Call a function one time no matter how many threads try
#include <threads.h>
void call_once(once_flag *flag, void (*func)(void));
If you have a bunch of threads running over the same piece of code that calls a function, but you only want that function to run one time, call_once()
can help you out.
The catch is the function that is called doesn’t return anything and takes no arguments.
If you need more than that, you’ll have to set a threadsafe flag such as atomic_flag
, or one that you protect with a mutex.
To use this, you need to pass it a pointer to a function to execute, func
, and also a pointer to a flag of type once_flag
.
once_flag
is an opaque type, so all you need to know is that you initialize it to the value ONCE_FLAG_INIT
.
Returns nothing.
#include <stdio.h>
#include <threads.h>
once_flag of = ONCE_FLAG_INIT; // Initialize it like this
void run_once_function(void)
{
printf("I'll only run once!\n");
}
int run(void *arg)
{
(void)arg;
printf("Thread running!\n");
call_once(&of, run_once_function);
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, NULL);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(t[i], NULL);
}
Output (might vary per run):
Thread running!
Thread running!
I'll only run once!
Thread running!
Thread running! Thread running!
cnd_broadcast()
Wake up all threads waiting on a condition variable
#include <threads.h>
int cnd_broadcast(cnd_t *cond);
This is just like cnd_signal()
in that it wakes up threads that are waiting on a condition variable…. except instead of just rousing one thread, it wakes them all.
Of course, only one will get the mutex, and the rest will have to wait their turn. But instead of being asleep waiting for a signal, they’ll be asleep waiting to reacquire the mutex. They’re rearin’ to go, in other words.
This can make a difference in a specific set of circumstances where cnd_signal()
might leave you hanging.
If you’re relying on subsequent threads to issue the next cnd_signal()
, but you have the cnd_wait()
in a while
loop65 that doesn’t allow any threads to escape, you’ll be stuck. No more threads will be woken up from the wait.
But if you cnd_broadcast()
, all the threads will be woken, and presumably at least one of them will be allowed to escape the while
loop, freeing it up to broadcast the next wakeup when its work is done.
Returns thrd_success
or thrd_error
depending on how well things went.
In the example below, we launch a bunch of threads, but they’re only allowed to run if their ID matches the current ID. If it doesn’t, they go back to waiting.
If you cnd_signal()
to wake the next thread, it might not be the one with the proper ID to run. If it’s not, it goes back to sleep and we hang (because no thread is awake to hit cnd_signal()
again).
But if you cnd_broadcast()
to wake them all, then they’ll all try (one after another) to get out of the while
loop. And one of them will make it.
Try switching the cnd_broadcast()
to cnd_signal()
to see likely deadlocks. It doesn’t happen every time, but usually does.
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
int id = *(int*)arg;
static int current_id = 0;
mtx_lock(&mutex);
while (id != current_id) {
printf("THREAD %d: waiting\n", id);
cnd_wait(&condvar, &mutex);
if (id != current_id)
printf("THREAD %d: woke up, but it's not my turn!\n", id);
else
printf("THREAD %d: woke up, my turn! Let's go!\n", id);
}
current_id++;
printf("THREAD %d: signaling thread %d to run\n", id, current_id);
//cnd_signal(&condvar);
cnd_broadcast(&condvar);
mtx_unlock(&mutex);
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
int id[] = {4, 3, 2, 1, 0};
mtx_init(&mutex, mtx_plain);
cnd_init(&condvar);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, id + i);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(t[i], NULL);
mtx_destroy(&mutex);
cnd_destroy(&condvar);
}
Example run with cnd_broadcast()
:
THREAD 4: waiting
THREAD 1: waiting
THREAD 3: waiting
THREAD 2: waiting
THREAD 0: signaling thread 1 to run
THREAD 2: woke up, but it's not my turn!
THREAD 2: waiting
THREAD 4: woke up, but it's not my turn!
THREAD 4: waiting
THREAD 3: woke up, but it's not my turn!
THREAD 3: waiting
THREAD 1: woke up, my turn! Let's go!
THREAD 1: signaling thread 2 to run
THREAD 4: woke up, but it's not my turn!
THREAD 4: waiting
THREAD 3: woke up, but it's not my turn!
THREAD 3: waiting
THREAD 2: woke up, my turn! Let's go!
THREAD 2: signaling thread 3 to run
THREAD 4: woke up, but it's not my turn!
THREAD 4: waiting
THREAD 3: woke up, my turn! Let's go!
THREAD 3: signaling thread 4 to run
THREAD 4: woke up, my turn! Let's go! THREAD 4: signaling thread 5 to run
Example run with cnd_signal()
:
THREAD 4: waiting
THREAD 1: waiting
THREAD 3: waiting
THREAD 2: waiting
THREAD 0: signaling thread 1 to run
THREAD 4: woke up, but it's not my turn!
THREAD 4: waiting
[deadlock at this point]
See how THREAD 0
signaled that it was THREAD 1
’s turn? But—bad news—it was THREAD 4
that got woken up. So no one continued the process. cnd_broadcast()
would have woken them all, so eventually THREAD 1
would have run, gotten out of the while
, and broadcast for the next thread to run.
cnd_signal()
, mtx_lock()
, mtx_unlock()
cnd_destroy()
Free up resources from a condition variable
#include <threads.h>
void cnd_destroy(cnd_t *cond);
This is the opposite of cnd_init()
and should be called when all threads are done using a condition variable.
Returns nothing!
General-purpose condition variable example here, but you can see the cnd_destroy()
down at the end.
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
mtx_lock(&mutex);
printf("Thread: waiting...\n");
cnd_wait(&condvar, &mutex);
printf("Thread: running again!\n");
mtx_unlock(&mutex);
return 0;
}
int main(void)
{
thrd_t t;
mtx_init(&mutex, mtx_plain);
cnd_init(&condvar);
printf("Main creating thread\n");
thrd_create(&t, run, NULL);
// Sleep 0.1s to allow the other thread to wait
thrd_sleep(&(struct timespec){.tv_nsec=100000000L}, NULL);
mtx_lock(&mutex);
printf("Main: signaling thread\n");
cnd_signal(&condvar);
mtx_unlock(&mutex);
thrd_join(t, NULL);
mtx_destroy(&mutex);
cnd_destroy(&condvar); // <-- DESTROY CONDITION VARIABLE
}
Output:
Main creating thread
Thread: waiting...
Main: signaling thread Thread: running again!
cnd_init()
Initialize a condition variable to make it ready for use
#include <threads.h>
int cnd_init(cnd_t *cond);
This is the opposite of cnd_destroy()
. This prepares a condition variable for use, doing behind-the-scenes work on it.
Don’t use a condition variable without calling this first!
If all goes well, returns thrd_success
. It all doesn’t go well, it could return thrd_nomem
if the system is out of memory, or thread_error
in the case of any other error.
General-purpose condition variable example here, but you can see the cnd_init()
down at the start of main()
.
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
mtx_lock(&mutex);
printf("Thread: waiting...\n");
cnd_wait(&condvar, &mutex);
printf("Thread: running again!\n");
mtx_unlock(&mutex);
return 0;
}
int main(void)
{
thrd_t t;
mtx_init(&mutex, mtx_plain);
cnd_init(&condvar); // <-- INITIALIZE CONDITION VARIABLE
printf("Main creating thread\n");
thrd_create(&t, run, NULL);
// Sleep 0.1s to allow the other thread to wait
thrd_sleep(&(struct timespec){.tv_nsec=100000000L}, NULL);
mtx_lock(&mutex);
printf("Main: signaling thread\n");
cnd_signal(&condvar);
mtx_unlock(&mutex);
thrd_join(t, NULL);
mtx_destroy(&mutex);
cnd_destroy(&condvar);
}
Output:
Main creating thread
Thread: waiting...
Main: signaling thread Thread: running again!
cnd_signal()
Wake up a thread waiting on a condition variable
#include <threads.h>
int cnd_signal(cnd_t *cond);
If you have a thread (or a bunch of threads) waiting on a condition variable, this function will wake one of them up to run.
Compare to cnd_broadcast()
that wakes up all the threads. See the cnd_broadcast()
page for more information on when you’re want to use that versus this.
Returns thrd_success
or thrd_error
depending on how happy your program is.
General-purpose condition variable example here, but you can see the cnd_signal()
in the middle of main()
.
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
mtx_lock(&mutex);
printf("Thread: waiting...\n");
cnd_wait(&condvar, &mutex);
printf("Thread: running again!\n");
mtx_unlock(&mutex);
return 0;
}
int main(void)
{
thrd_t t;
mtx_init(&mutex, mtx_plain);
cnd_init(&condvar);
printf("Main creating thread\n");
thrd_create(&t, run, NULL);
// Sleep 0.1s to allow the other thread to wait
thrd_sleep(&(struct timespec){.tv_nsec=100000000L}, NULL);
mtx_lock(&mutex);
printf("Main: signaling thread\n");
cnd_signal(&condvar); // <-- SIGNAL CHILD THREAD HERE!
mtx_unlock(&mutex);
thrd_join(t, NULL);
mtx_destroy(&mutex);
cnd_destroy(&condvar);
}
Output:
Main creating thread
Thread: waiting...
Main: signaling thread Thread: running again!
cnd_timedwait()
Wait on a condition variable with a timeout
#include <threads.h>
int cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mtx,
const struct timespec *restrict ts);
This is like cnd_wait()
except we get to specify a timeout, as well.
Note that the thread still must reacquire the mutex to get more work done even after the timeout. The the main difference is that regular cnd_wait()
will only try to get the mutex after a cnd_signal()
or cnd_broadcast()
, whereas cnd_timedwait()
will do that, too, and try to get the mutex after the timeout.
The timeout is specified as an absolute UTC time since Epoch. You can get this with the timespec_get()
function and then add values on to the result to timeout later than now, as shown in the example.
Beware that you can’t have more than 999999999 nanoseconds in the tv_nsec
field of the struct timespec
. Mod those so they stay in range.
If the thread wakes up for a non-timeout reason (e.g. signal or broadcast), returns thrd_success
. If woken up due to timeout, returns thrd_timedout
. Otherwise returns thrd_error
.
This example has a thread wait on a condition variable for a maximum of 1.75 seconds. And it always times out because no one ever sends a signal. Tragic.
#include <stdio.h>
#include <time.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
mtx_lock(&mutex);
struct timespec ts;
// Get the time now
timespec_get(&ts, TIME_UTC);
// Add on 1.75 seconds from now
ts.tv_sec += 1;
ts.tv_nsec += 750000000L;
// Handle nsec overflow
ts.tv_sec += ts.tv_nsec / 1000000000L;
ts.tv_nsec = ts.tv_nsec % 1000000000L;
printf("Thread: waiting...\n");
int r = cnd_timedwait(&condvar, &mutex, &ts);
switch (r) {
case thrd_success:
printf("Thread: signaled!\n");
break;
case thrd_timedout:
printf("Thread: timed out!\n");
return 1;
case thrd_error:
printf("Thread: Some kind of error\n");
return 2;
}
mtx_unlock(&mutex);
return 0;
}
int main(void)
{
thrd_t t;
mtx_init(&mutex, mtx_plain);
cnd_init(&condvar);
printf("Main creating thread\n");
thrd_create(&t, run, NULL);
// Sleep 3s to allow the other thread to timeout
thrd_sleep(&(struct timespec){.tv_sec=3}, NULL);
thrd_join(t, NULL);
mtx_destroy(&mutex);
cnd_destroy(&condvar);
}
Output:
Main creating thread
Thread: waiting... Thread: timed out!
cnd_wait()
Wait for a signal on a condition variable
#include <threads.h>
int cnd_wait(cnd_t *cond, mtx_t *mtx);
This puts the calling thread to sleep until it is awakened by a call to cnd_signal()
or cnd_broadcast()
.
If everything’s fantastic, returns thrd_success
. Otherwise it returns thrd_error
to report that something has gone fantastically, horribly awry.
General-purpose condition variable example here, but you can see the cnd_wait()
in the run()
function.
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
mtx_lock(&mutex);
printf("Thread: waiting...\n");
cnd_wait(&condvar, &mutex); // <-- WAIT HERE!
printf("Thread: running again!\n");
mtx_unlock(&mutex);
return 0;
}
int main(void)
{
thrd_t t;
mtx_init(&mutex, mtx_plain);
cnd_init(&condvar);
printf("Main creating thread\n");
thrd_create(&t, run, NULL);
// Sleep 0.1s to allow the other thread to wait
thrd_sleep(&(struct timespec){.tv_nsec=100000000L}, NULL);
mtx_lock(&mutex);
printf("Main: signaling thread\n");
cnd_signal(&condvar); // <-- SIGNAL CHILD THREAD HERE!
mtx_unlock(&mutex);
thrd_join(t, NULL);
mtx_destroy(&mutex);
cnd_destroy(&condvar);
}
Output:
Main creating thread
Thread: waiting...
Main: signaling thread Thread: running again!
mtx_destroy()
Cleanup a mutex when done with it
#include <threads.h>
void mtx_destroy(mtx_t *mtx);
The opposite of mtx_init()
, this function frees up any resources associated with the given mutex.
You should call this when all threads are done using the mutex.
Returns nothing, the selfish ingrate!
General-purpose mutex example here, but you can see the mtx_destroy()
down at the end.
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
static int count = 0;
mtx_lock(&mutex);
printf("Thread: I got %d!\n", count);
count++;
mtx_unlock(&mutex);
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
mtx_init(&mutex, mtx_plain);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, NULL);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(t[i], NULL);
mtx_destroy(&mutex); // <-- DESTROY THE MUTEX HERE
}
Output:
Thread: I got 0!
Thread: I got 1!
Thread: I got 2!
Thread: I got 3! Thread: I got 4!
mtx_init()
Initialize a mutex for use
#include <threads.h>
int mtx_init(mtx_t *mtx, int type);
Before you can use a mutex variable, you have to initialize it with this call to get it all prepped and ready to go.
But wait! It’s not quite that simple. You have to tell it what type
of mutex you want to create.
Type | Description |
---|---|
mtx_plain |
Regular ol’ mutex |
mtx_timed |
Mutex that supports timeouts |
mtx_plain|mtx_recursive |
Recursive mutex |
mtx_timed|mtx_recursive |
Recursive mutex that supports timeouts |
As you can see, you can make a plain or timed mutex recursive by bitwise-ORing the value with mtx_recursive
.
“Recursive” means that the holder of a lock can call mtx_lock()
multiple times on the same lock. (They have to unlock it an equal number of times before anyone else can take the mutex.) This might ease coding from time to time, especially if you call a function that needs to lock the mutex when you already hold the mutex.
And the timeout gives a thread a chance to try to get the lock for a while, but then bail out if it can’t get it in that timeframe. You use the mtx_timedlock()
function with mtx_timed
mutexes.
Returns thrd_success
in a perfect world, and potentially thrd_error
in an imperfect one.
General-purpose mutex example here, but you can see the mtx_init()
down at the top of main()
:
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
static int count = 0;
mtx_lock(&mutex);
printf("Thread: I got %d!\n", count);
count++;
mtx_unlock(&mutex);
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
mtx_init(&mutex, mtx_plain); // <-- CREATE THE MUTEX HERE
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, NULL);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(t[i], NULL);
mtx_destroy(&mutex); // <-- DESTROY THE MUTEX HERE
}
Output:
Thread: I got 0!
Thread: I got 1!
Thread: I got 2!
Thread: I got 3! Thread: I got 4!
mtx_lock()
Acquire a lock on a mutex
#include <threads.h>
int mtx_lock(mtx_t *mtx);
If you’re a thread and want to enter a critical section, do I have the function for you!
A thread that calls this function will wait until it can acquire the mutex, then it will grab it, wake up, and run!
If the mutex is recursive and is already locked by this thread, it will be locked again and the lock count will increase. If the mutex is not recursive and the thread already holds it, this call will error out.
Returns thrd_success
on goodness and thrd_error
on badness.
General-purpose mutex example here, but you can see the mtx_lock()
in the run()
function:
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
static int count = 0;
mtx_lock(&mutex); // <-- LOCK HERE
printf("Thread: I got %d!\n", count);
count++;
mtx_unlock(&mutex);
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
mtx_init(&mutex, mtx_plain); // <-- CREATE THE MUTEX HERE
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, NULL);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(t[i], NULL);
mtx_destroy(&mutex); // <-- DESTROY THE MUTEX HERE
}
Output:
Thread: I got 0!
Thread: I got 1!
Thread: I got 2!
Thread: I got 3! Thread: I got 4!
mtx_unlock()
, mtx_trylock()
, mtx_timedlock()
mtx_timedlock()
Lock a mutex allowing for timeout
#include <threads.h>
int mtx_timedlock(mtx_t *restrict mtx, const struct timespec *restrict ts);
This is just like mtx_lock()
except you can add a timeout if you don’t want to wait forever.
The timeout is specified as an absolute UTC time since Epoch. You can get this with the timespec_get()
function and then add values on to the result to timeout later than now, as shown in the example.
Beware that you can’t have more than 999999999 nanoseconds in the tv_nsec
field of the struct timespec
. Mod those so they stay in range.
If everything works and the mutex is obtained, returns thrd_success
. If a timeout happens first, returns thrd_timedout
.
Otherwise, returns thrd_error
. Because if nothing is right, everything is wrong.
This example has a thread wait on a mutex for a maximum of 1.75 seconds. And it always times out because no one ever sends a signal.
#include <stdio.h>
#include <time.h>
#include <threads.h>
mtx_t mutex;
int run(void *arg)
{
(void)arg;
struct timespec ts;
// Get the time now
timespec_get(&ts, TIME_UTC);
// Add on 1.75 seconds from now
ts.tv_sec += 1;
ts.tv_nsec += 750000000L;
// Handle nsec overflow
ts.tv_sec += ts.tv_nsec / 1000000000L;
ts.tv_nsec = ts.tv_nsec % 1000000000L;
printf("Thread: waiting for lock...\n");
int r = mtx_timedlock(&mutex, &ts);
switch (r) {
case thrd_success:
printf("Thread: grabbed lock!\n");
break;
case thrd_timedout:
printf("Thread: timed out!\n");
break;
case thrd_error:
printf("Thread: Some kind of error\n");
break;
}
mtx_unlock(&mutex);
return 0;
}
int main(void)
{
thrd_t t;
mtx_init(&mutex, mtx_plain);
mtx_lock(&mutex);
printf("Main creating thread\n");
thrd_create(&t, run, NULL);
// Sleep 3s to allow the other thread to timeout
thrd_sleep(&(struct timespec){.tv_sec=3}, NULL);
mtx_unlock(&mutex);
thrd_join(t, NULL);
mtx_destroy(&mutex);
}
Output:
Main creating thread
Thread: waiting for lock... Thread: timed out!
mtx_lock()
, mtx_trylock()
, timespec_get()
mtx_trylock()
Try to lock a mutex, returning if not possible
#include <threads.h>
int mtx_trylock(mtx_t *mtx);
This works just like mtx_lock
except that it returns instantly if a lock can’t be obtained.
The spec notes that there’s a chance that mtx_trylock()
might spuriously fail with thrd_busy
even if there are no other threads holding the lock. I’m not sure why this is, but you should defensively code against it.
Returns thrd_success
if all’s well. Or thrd_busy
if some other thread holds the lock. Or thrd_error
, which means something went right. I mean “wrong”.
#include <stdio.h>
#include <time.h>
#include <threads.h>
mtx_t mutex;
int run(void *arg)
{
int id = *(int*)arg;
int r = mtx_trylock(&mutex); // <-- TRY TO GRAB THE LOCK
switch (r) {
case thrd_success:
printf("Thread %d: grabbed lock!\n", id);
break;
case thrd_busy:
printf("Thread %d: lock already taken :(\n", id);
return 1;
case thrd_error:
printf("Thread %d: Some kind of error\n", id);
return 2;
}
mtx_unlock(&mutex);
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
int id[THREAD_COUNT];
mtx_init(&mutex, mtx_plain);
for (int i = 0; i < THREAD_COUNT; i++) {
id[i] = i;
thrd_create(t + i, run, id + i);
}
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(t[i], NULL);
mtx_destroy(&mutex);
}
Output (varies by run):
Thread 0: grabbed lock!
Thread 1: lock already taken :(
Thread 4: lock already taken :(
Thread 3: grabbed lock! Thread 2: lock already taken :(
mtx_lock()
, mtx_timedlock()
, mtx_unlock()
mtx_unlock()
Free a mutex when you’re done with the critical section
#include <threads.h>
int mtx_unlock(mtx_t *mtx);
After you’ve done all the dangerous stuff you have to do, wherein the involved threads should not be stepping on each other’s toes… you can free up your stranglehold on the mutex by calling mtx_unlock()
.
Returns thrd_success
on success. Or thrd_error
on error. It’s not very original in this regard.
General-purpose mutex example here, but you can see the mtx_unlock()
in the run()
function:
#include <stdio.h>
#include <threads.h>
cnd_t condvar;
mtx_t mutex;
int run(void *arg)
{
(void)arg;
static int count = 0;
mtx_lock(&mutex);
printf("Thread: I got %d!\n", count);
count++;
mtx_unlock(&mutex); // <-- UNLOCK HERE
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
mtx_init(&mutex, mtx_plain);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, NULL);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(t[i], NULL);
mtx_destroy(&mutex);
}
Output:
Thread: I got 0!
Thread: I got 1!
Thread: I got 2!
Thread: I got 3! Thread: I got 4!
mtx_lock()
, mtx_timedlock()
, mtx_trylock()
thrd_create()
Create a new thread of execution
#include <threads.h>
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
Now you have the POWER!
Right?
This is how you launch new threads to make your program do multiple things at once66!
In order to make this happen, you need to pass a pointer to a thrd_t
that will be used to represent the thread you’re spawning.
That thread will start running the function you pass a pointer to in func
. This is a value of type thrd_start_t
, which is a pointer to a function that returns an int
and takes a single void*
as a parameter, i.e.:
int thread_run_func(void *arg)
And, as you might have guessed, the pointer you pass to thrd_create()
for the arg
parameter is passed on to the func
function. This is how you can give additional information to the thread when it starts up.
Of course, for arg
, you have to be sure to pass a pointer to an object that is thread-safe or per-thread.
If the thread returns from the function, it exits just as if it had called thrd_exit()
.
Finally, the value that the func
function returns can be picked up by the parent thread with thrd_join()
.
In the case of goodness, returns thrd_success
. If you’re out of memory, will return thrd_nomem
. Otherwise, thrd_error
.
#include <stdio.h>
#include <threads.h>
int run(void *arg)
{
int id = *(int*)arg;
printf("Thread %d: I'm alive!!\n", id);
return id;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
int id[THREAD_COUNT]; // One of these per thread
for (int i = 0; i < THREAD_COUNT; i++) {
id[i] = i; // Let's pass in the thread number as the ID
thrd_create(t + i, run, id + i);
}
for (int i = 0; i < THREAD_COUNT; i++) {
int res;
thrd_join(t[i], &res);
printf("Main: thread %d exited with code %d\n", i, res);
}
}
Output (might vary from run to run):
Thread 1: I'm alive!!
Thread 0: I'm alive!!
Thread 3: I'm alive!!
Thread 2: I'm alive!!
Main: thread 0 exited with code 0
Main: thread 1 exited with code 1
Main: thread 2 exited with code 2
Main: thread 3 exited with code 3
Thread 4: I'm alive!! Main: thread 4 exited with code 4
thrd_current()
Get the ID of the calling thread
#include <threads.h>
(void); thrd_t thrd_current
Each thread has an opaque ID of type thrd_t
. This is the value we see get initialized when we call thrd_create()
.
But what if you want to get the ID of the currently running thread?
No problem! Just call this function and it will be returned to you.
Why? Who knows!
Well, to be honest, I could see it being used a couple places.
thrd_detach()
. I’m not sure why you’d want to do this, however.thrd_equal()
function. Seems like the most legit use.If anyone has another use, please let me know.
Returns the calling thread’s ID.
Here’s a general example that shows getting the current thread ID and comparing it to a previously-recorded thread ID and taking exciting action based on the result! Starring Arnold Schwarzenegger!
#include <stdio.h>
#include <threads.h>
thrd_t first_thread_id;
int run(void *arg)
{
(void)arg;
thrd_t my_id = thrd_current(); // <-- GET MY THREAD ID
if (thrd_equal(my_id, first_thread_id))
printf("I'm the first thread!\n");
else
printf("I'm not the first!\n");
return 0;
}
int main(void)
{
thrd_t t;
thrd_create(&first_thread_id, run, NULL);
thrd_create(&t, run, NULL);
thrd_join(first_thread_id, NULL);
thrd_join(t, NULL);
}
Output:
Come on, you got what you want, Cohaagen! Give deez people ay-ah!
No, wait, that’s an Arnold Schwarzenegger quote from Total Recall, one of the best science fiction films of all time. Watch it now and then come back to finish this reference page.
Man–what an ending! And Johnny Cab? So excellent. Anyway!
Output:
I'm the first thread! I'm not the first!
thrd_detach()
Automatically clean up threads when they exit
#include <threads.h>
int thrd_detach(thrd_t thr);
Normally you have to thrd_join()
to get resources associated with a deceased thread cleaned up. (Most notably, its exit status is still floating around waiting to get picked up.)
But if you call thrd_detach()
on the thread first, manual cleanup isn’t necessary. They just exit and are cleaned up by the OS.
(Note that when the main thread dies, all the threads die in any case.)
thrd_success
if the thread successfully detaches, thrd_error
otherwise.
#include <stdio.h>
#include <threads.h>
thrd_t first_thread_id;
int run(void *arg)
{
(void)arg;
printf("Thread running!\n");
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t;
for (int i = 0; i < THREAD_COUNT; i++) {
thrd_create(&t, run, NULL);
thrd_detach(t);
}
// No need to thrd_join()!
// Sleep a quarter second to let them all finish
thrd_sleep(&(struct timespec){.tv_nsec=250000000}, NULL);
}
thrd_equal()
Compare two thread descriptors for equality
#include <threads.h>
int thrd_equal(thrd_t thr0, thrd_t thr1);
If you have two thread descriptors in thrd_t
variables, you can test them for equality with this function.
For example, maybe one of the threads has special powers the others don’t, and the run function needs to be able to tell them apart, as in the example.
Returns non-zero if the threads are equal. Returns 0
if they’re not.
Here’s a general example that shows getting the current thread ID and comparing it to a previously-recorded thread ID and taking boring action based on the result.
#include <stdio.h>
#include <threads.h>
thrd_t first_thread_id;
int run(void *arg)
{
(void)arg;
thrd_t my_id = thrd_current();
if (thrd_equal(my_id, first_thread_id)) // <-- COMPARE!
printf("I'm the first thread!\n");
else
printf("I'm not the first!\n");
return 0;
}
int main(void)
{
thrd_t t;
thrd_create(&first_thread_id, run, NULL);
thrd_create(&t, run, NULL);
thrd_join(first_thread_id, NULL);
thrd_join(t, NULL);
}
Output:
I'm the first thread! I'm not the first!
thrd_exit()
Stop and exit this thread
#include <threads.h>
_Noreturn void thrd_exit(int res);
A thread commonly exits by returning from its run function. But if it wants to exit early (perhaps from deeper in the call stack), this function will get that done.
The res
code can be picked up by a thread calling thrd_join()
, and is equivalent to returning a value from the run function.
Like with returning from the run function, this will also properly clean up all the thread-specific storage associated with this thread—all the destructors for the threads TSS variables will be called. If there are any remaining TSS variables with destructors after the first round of destruction67, the remaining destructors will be called. This happens repeatedly until there are no more, or the number of rounds of carnage reaches TSS_DTOR_ITERATIONS
.
If the main thread calls this, it’s as if you called exit(EXIT_SUCCESS)
.
This function never returns because the thread calling it is killed in the process. Trippy!
Threads in this example exit early with result 22
if they get a NULL
value for arg
.
#include <stdio.h>
#include <threads.h>
thrd_t first_thread_id;
int run(void *arg)
{
(void)arg;
if (arg == NULL)
thrd_exit(22);
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, i == 2? NULL: "spatula");
for (int i = 0; i < THREAD_COUNT; i++) {
int res;
thrd_join(t[i], &res);
printf("Thread %d exited with code %d\n", i, res);
}
}
Output:
Thread 0 exited with code 0
Thread 1 exited with code 0
Thread 2 exited with code 22
Thread 3 exited with code 0 Thread 4 exited with code 0
thrd_join()
Wait for a thread to exit
#include <threads.h>
int thrd_join(thrd_t thr, int *res);
When a parent thread fires off some child threads, it can wait for them to complete with this call
Threads in this example exit early with result 22
if they get a NULL
value for arg
. The parent thread picks up this result code with thrd_join()
.
#include <stdio.h>
#include <threads.h>
thrd_t first_thread_id;
int run(void *arg)
{
(void)arg;
if (arg == NULL)
thrd_exit(22);
return 0;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, i == 2? NULL: "spatula");
for (int i = 0; i < THREAD_COUNT; i++) {
int res;
thrd_join(t[i], &res);
printf("Thread %d exited with code %d\n", i, res);
}
}
Output:
Thread 0 exited with code 0
Thread 1 exited with code 0
Thread 2 exited with code 22
Thread 3 exited with code 0 Thread 4 exited with code 0
thrd_sleep()
Sleep for a specific number of seconds and nanoseconds
#include <threads.h>
int thrd_sleep(const struct timespec *duration, struct timespec *remaining);
This function puts the current thread to sleep for a while68 allowing other threads to run.
The calling thread will wake up after the time has elapsed, or if it gets interrupted by a signal or something.
If it doesn’t get interrupted, it’ll sleep at least as long as you asked. Maybe a tad longer. You know how hard it can be to get out of bed.
The structure looks like this:
struct timespec {
time_t tv_sec; // Seconds
long tv_nsec; // Nanoseconds (billionths of a second)
};
Don’t set tv_nsec
greater than 999,999,999. I can’t see what officially happens if you do, but on my system thrd_sleep()
returns -2
and fails.
Returns 0
on timeout, or -1
if interrupted by a signal. Or any negative value on some other error. Weirdly, the spec allows this “other error negative value” to also be -1
, so good luck with that.
#include <stdio.h>
#include <threads.h>
int main(void)
{
// Sleep for 3.25 seconds
thrd_sleep(&(struct timespec){.tv_sec=3, .tv_nsec=250000000}, NULL);
return 0;
}
thrd_yield()
Stop running that other threads might run
#include <threads.h>
void thrd_yield(void);
If you have a thread that’s hogging the CPU and you want to give your other threads time to run, you can call thrd_yield()
. If the system sees fit, it will put the calling thread to sleep and one of the other threads will run instead.
It’s a good way to be “polite” to the other threads in your program if you want the encourage them to run instead.
Returns nothing!
This example’s kinda poor because the OS is probably going to reschedule threads on the output anyway, but it gets the point across.
The main thread is giving other threads a chance to run after every block of dumb work it does.
#include <stdio.h>
#include <threads.h>
int run(void *arg)
{
int main_thread = arg != NULL;
if (main_thread) {
long int total = 0;
for (int i = 0; i < 10; i++) {
for (long int j = 0; j < 1000L; j++)
total++;
printf("Main thread yielding\n");
thrd_yield(); // <-- YIELD HERE
}
} else
printf("Other thread running!\n");
return 0;
}
#define THREAD_COUNT 10
int main(void)
{
thrd_t t[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, i == 0? "main": NULL);
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(t[i], NULL);
return 0;
}
The output will vary from run to run. Notice that even after thrd_yield()
other threads might not yet be ready to run and the main thread will continue.
Main thread yielding
Main thread yielding
Main thread yielding
Other thread running!
Other thread running!
Other thread running!
Other thread running!
Main thread yielding
Other thread running!
Other thread running!
Main thread yielding
Main thread yielding
Main thread yielding
Other thread running!
Main thread yielding
Main thread yielding
Main thread yielding
Other thread running! Other thread running!
tss_create()
Create new thread-specific storage
#include <threads.h>
int tss_create(tss_t *key, tss_dtor_t dtor);
This helps when you need per-thread storage of different values.
A common place this comes up is if you have a file scope variable that is shared between a bunch of functions and often returned. That’s not threadsafe. One way to refactor is to replace it with thread-specific storage so that each thread gets their own code and doesn’t step on other thread’s toes.
To make this work, you pass in a pointer to a tss_t
key—this is the variable you will use in subsequent tss_set()
and tss_get()
calls to set and get the value associated with the key.
The interesting part of this is the dtor
destructor pointer of type tss_dtor_t
. This is actually a pointer to a function that takes a void*
argument and returns void
, i.e.
void dtor(void *p) { ... }
This function will be called per thread when the thread exits with thrd_exit()
(or returns from the run function).
It’s unspecified behavior to call this function while other threads’ destructors are running.
Returns nothing!
This is a general-purpose TSS example. Note the TSS variable is created near the top of main()
.
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
tss_t str;
void some_function(void)
{
// Retrieve the per-thread value of this string
char *tss_string = tss_get(str);
// And print it
printf("TSS string: %s\n", tss_string);
}
int run(void *arg)
{
int serial = *(int*)arg; // Get this thread's serial number
free(arg);
// malloc() space to hold the data for this thread
char *s = malloc(64);
sprintf(s, "thread %d! :)", serial); // Happy little string
// Set this TSS variable to point at the string
tss_set(str, s);
// Call a function that will get the variable
some_function();
return 0; // Equivalent to thrd_exit(0); fires destructors
}
#define THREAD_COUNT 15
int main(void)
{
thrd_t t[THREAD_COUNT];
// Make a new TSS variable, the free() function is the destructor
tss_create(&str, free); // <-- CREATE TSS VAR!
for (int i = 0; i < THREAD_COUNT; i++) {
int *n = malloc(sizeof *n); // Holds a thread serial number
*n = i;
thrd_create(t + i, run, n);
}
for (int i = 0; i < THREAD_COUNT; i++) {
thrd_join(t[i], NULL);
}
// And all threads are done, so let's free this
tss_delete(str);
}
Output:
TSS string: thread 0! :)
TSS string: thread 2! :)
TSS string: thread 1! :)
TSS string: thread 5! :)
TSS string: thread 3! :)
TSS string: thread 6! :)
TSS string: thread 4! :)
TSS string: thread 7! :)
TSS string: thread 8! :)
TSS string: thread 9! :)
TSS string: thread 10! :)
TSS string: thread 13! :)
TSS string: thread 12! :)
TSS string: thread 11! :) TSS string: thread 14! :)
tss_delete()
, tss_set()
, tss_get()
, thrd_exit()
tss_delete()
Clean up a thread-specific storage variable
#include <threads.h>
void tss_delete(tss_t key);
This is the opposite of tss_create()
. You create (initialize) the TSS variable before using it, then, when all the threads are done that need it, you delete (deinitialize/free) it with this.
This doesn’t call any destructors! Those are all called by thrd_exit()
!
Returns nothing!
This is a general-purpose TSS example. Note the TSS variable is deleted near the bottom of main()
.
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
tss_t str;
void some_function(void)
{
// Retrieve the per-thread value of this string
char *tss_string = tss_get(str);
// And print it
printf("TSS string: %s\n", tss_string);
}
int run(void *arg)
{
int serial = *(int*)arg; // Get this thread's serial number
free(arg);
// malloc() space to hold the data for this thread
char *s = malloc(64);
sprintf(s, "thread %d! :)", serial); // Happy little string
// Set this TSS variable to point at the string
tss_set(str, s);
// Call a function that will get the variable
some_function();
return 0; // Equivalent to thrd_exit(0); fires destructors
}
#define THREAD_COUNT 15
int main(void)
{
thrd_t t[THREAD_COUNT];
// Make a new TSS variable, the free() function is the destructor
tss_create(&str, free);
for (int i = 0; i < THREAD_COUNT; i++) {
int *n = malloc(sizeof *n); // Holds a thread serial number
*n = i;
thrd_create(t + i, run, n);
}
for (int i = 0; i < THREAD_COUNT; i++) {
thrd_join(t[i], NULL);
}
// And all threads are done, so let's free this
tss_delete(str); // <-- DELETE TSS VARIABLE!
}
Output:
TSS string: thread 0! :)
TSS string: thread 2! :)
TSS string: thread 1! :)
TSS string: thread 5! :)
TSS string: thread 3! :)
TSS string: thread 6! :)
TSS string: thread 4! :)
TSS string: thread 7! :)
TSS string: thread 8! :)
TSS string: thread 9! :)
TSS string: thread 10! :)
TSS string: thread 13! :)
TSS string: thread 12! :)
TSS string: thread 11! :) TSS string: thread 14! :)
tss_create()
, tss_set()
, tss_get()
, thrd_exit()
tss_get()
Get thread-specific data
#include <threads.h>
void *tss_get(tss_t key);
Once you’ve set a variable with tss_set()
, you can retrieve the value with tss_get()
—just pass in the key and you’ll get a pointer to the value back.
Don’t call this from a destructor.
Returns the value stored for the given key
, or NULL
if there’s trouble.
This is a general-purpose TSS example. Note the TSS variable is retrieved in some_function()
, below.
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
tss_t str;
void some_function(void)
{
// Retrieve the per-thread value of this string
char *tss_string = tss_get(str); // <-- GET THE VALUE
// And print it
printf("TSS string: %s\n", tss_string);
}
int run(void *arg)
{
int serial = *(int*)arg; // Get this thread's serial number
free(arg);
// malloc() space to hold the data for this thread
char *s = malloc(64);
sprintf(s, "thread %d! :)", serial); // Happy little string
// Set this TSS variable to point at the string
tss_set(str, s);
// Call a function that will get the variable
some_function();
return 0; // Equivalent to thrd_exit(0); fires destructors
}
#define THREAD_COUNT 15
int main(void)
{
thrd_t t[THREAD_COUNT];
// Make a new TSS variable, the free() function is the destructor
tss_create(&str, free);
for (int i = 0; i < THREAD_COUNT; i++) {
int *n = malloc(sizeof *n); // Holds a thread serial number
*n = i;
thrd_create(t + i, run, n);
}
for (int i = 0; i < THREAD_COUNT; i++) {
thrd_join(t[i], NULL);
}
// And all threads are done, so let's free this
tss_delete(str);
}
Output:
TSS string: thread 0! :)
TSS string: thread 2! :)
TSS string: thread 1! :)
TSS string: thread 5! :)
TSS string: thread 3! :)
TSS string: thread 6! :)
TSS string: thread 4! :)
TSS string: thread 7! :)
TSS string: thread 8! :)
TSS string: thread 9! :)
TSS string: thread 10! :)
TSS string: thread 13! :)
TSS string: thread 12! :)
TSS string: thread 11! :) TSS string: thread 14! :)
tss_set()
Set thread-specific data
#include <threads.h>
int tss_set(tss_t key, void *val);
Once you’ve set up your TSS variable with tss_create()
, you can set it on a per thread basis with tss_set()
.
key
is the identifier for this data, and val
is a pointer to it.
The destructor specified in tss_create()
will be called for the value set when the thread exits.
Also, if there’s a destructor and there is already at value for this key in place, the destructor will not be called for the already-existing value. In fact, this function will never cause a destructor to be called. So you’re on your own, there—best clean up the old value before overwriting it with the new one.
Returns thrd_success
when happy, and thrd_error
when not.
This is a general-purpose TSS example. Note the TSS variable is set in run()
, below.
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
tss_t str;
void some_function(void)
{
// Retrieve the per-thread value of this string
char *tss_string = tss_get(str);
// And print it
printf("TSS string: %s\n", tss_string);
}
int run(void *arg)
{
int serial = *(int*)arg; // Get this thread's serial number
free(arg);
// malloc() space to hold the data for this thread
char *s = malloc(64);
sprintf(s, "thread %d! :)", serial); // Happy little string
// Set this TSS variable to point at the string
tss_set(str, s); // <-- SET THE TSS VARIABLE
// Call a function that will get the variable
some_function();
return 0; // Equivalent to thrd_exit(0); fires destructors
}
#define THREAD_COUNT 15
int main(void)
{
thrd_t t[THREAD_COUNT];
// Make a new TSS variable, the free() function is the destructor
tss_create(&str, free);
for (int i = 0; i < THREAD_COUNT; i++) {
int *n = malloc(sizeof *n); // Holds a thread serial number
*n = i;
thrd_create(t + i, run, n);
}
for (int i = 0; i < THREAD_COUNT; i++) {
thrd_join(t[i], NULL);
}
// And all threads are done, so let's free this
tss_delete(str);
}
Output:
TSS string: thread 0! :)
TSS string: thread 2! :)
TSS string: thread 1! :)
TSS string: thread 5! :)
TSS string: thread 3! :)
TSS string: thread 6! :)
TSS string: thread 4! :)
TSS string: thread 7! :)
TSS string: thread 8! :)
TSS string: thread 9! :)
TSS string: thread 10! :)
TSS string: thread 13! :)
TSS string: thread 12! :)
TSS string: thread 11! :) TSS string: thread 14! :)