The C interface to the Dinkum Threads Library is very similar to the thread support interface defined in the Posix Standard. It consists of two headers:
"Dinkum/threads/threads.h"
-- for defining types and functions that implement thread-related operations"Dinkum/threads/xtimec.h"
-- for defining a struct and functions for high-resolution time operationsHere is a sample program to illustrate the use of the C interface:
#include <stdio.h> #include <stdlib.h> #include "Dinkum/threads/threads.h" #include "Dinkum/threads/xtimec.h" #define DATA_SIZE 20 #define DATA_LIMIT 100 /* data queue */ static int data[DATA_SIZE]; static int begin; static int end; static int queue_not_full; /* thread-specfic storage */ static tss_t key; /* initialization */ static once_flag init_flag = ONCE_FLAG_INIT; static void init(void) { /* initialize global data from first calling thread */ queue_not_full = 1; tss_create(&key, free); } static void setup(void) { /* initialize thread data */ call_once(&init_flag, init); tss_set(key, malloc(sizeof(int))); *(int*)tss_get(key) = 0; } /* synchronization objects */ static mtx_t data_mtx; static cnd_t queue_full_c; static cnd_t queue_empty_c; /* utilities */ static void delay(void) { /* delay randomly from 0 to 200 milliseconds */ xtime xt; xtime_get(&xt, TIME_UTC); xt.nsec += (rand() * 200) / (RAND_MAX + 1) * 1000000; thrd_sleep(&xt); } /* produce data */ static int producer(void *arg) { /* insert sequential values from 0 to DATA_LIMIT into queue */ setup(); while (*(int*)tss_get(key) < DATA_LIMIT) { /* insert *val into queue */ delay(); mtx_lock(&data_mtx); while (queue_not_full == 0) cnd_wait(&queue_full_c, &data_mtx); data[end++] = (*(int*)tss_get(key))++; if (end == DATA_SIZE) end = 0; if (end + 1 == begin || end + 1 == DATA_SIZE && begin == 0) queue_not_full = 0; cnd_signal(&queue_empty_c); mtx_unlock(&data_mtx); } return 0; } /* consume data */ static int finished = 0; static int consumer(void *arg) { /* remove data values from queue and display values */ setup(); while (!finished || begin != end) { /* remove and display data values */ delay(); mtx_lock(&data_mtx); while (!finished && begin == end) cnd_wait(&queue_empty_c, &data_mtx); if (begin != end) { /* remove and display a data value */ *(int*)tss_get(key) = -data[begin++]; printf("%d\n", *(int*)tss_get(key)); if (begin == DATA_SIZE) begin = 0; queue_not_full = 1; cnd_signal(&queue_full_c); } mtx_unlock(&data_mtx); } return 0; } int main() { /* create a consumer thread and two producer threads */ thrd_t thr0, thr1, thr2; if (mtx_init(&data_mtx, mtx_plain) != thrd_success) { /* display error message and quit */ puts("Error: unable to initialize mutex"); exit(EXIT_FAILURE); } /* error checking omitted for clarity */ cnd_init(&queue_full_c); cnd_init(&queue_empty_c); thrd_create(&thr0, consumer, 0); thrd_create(&thr1, producer, 0); thrd_create(&thr2, producer, 0); thrd_join(thr1, 0); thrd_join(thr2, 0); mtx_lock(&data_mtx); finished = 1; mtx_unlock(&data_mtx); thrd_join(thr0, 0); return 0; }
Threads · Condition Variables · Mutexes · Once Functions · Thread-specific Storage · Return Values
Use the functions and types with the prefix thrd
to manage
threads. Each thread has an identifier of type
thrd_t
, which is passed as an argument
to the functions that manage specific threads. Each thread begins execution in a
function of type thrd_start_t
.
To create a new thread call the function
thrd_create
with the address of the
thread identifier, the address of the thread function, and an argument to be passed
to the thread function. The thread ends when it returns from the thread function or when
it calls thrd_exit
. For convenience, a thread
can provide a result code
of type int
when it ends, either by returning the
code from the thread function or by passing the code to thrd_exit
.
To block a thread until another thread ends call
thrd_join
, passing the
identifier of the thread to wait for and, optionally, the address of a variable of
type int
where the result code will be stored. To properly clean up
resources allocated by the operating system, an application should call either
thrd_join
or thrd_detach
once for each thread created by thrd_create
.
Two functions operate on the current thread; they do not take a thread identifier argument.
Use thrd_sleep
to suspend
execution of the current thread until a particular time. Use
thrd_yield
to permit other
threads to run even if the current thread would ordinarily continue to run.
Two functions operate on thread identifiers. Use
thrd_equal
to determine whether
two thread identifiers refer to the same thread.
Use thrd_current
to get
a thread identifier that refers to the current thread.
Use the functions and type with the prefix cnd
to manage
condition variables.
Each condition variable has an identifier of type
cnd_t
, which is passed as an argument
to the functions that manage condition variables. Use
cnd_init
to create a condition
variable and cnd_destroy
to
release any resources associated with a condition variable when it is no longer needed.
To wait for a condition variable to be signalled call
cnd_wait
or
cnd_timedwait
.
To unblock threads waiting for a condition variable call
cnd_signal
or
cnd_broadcast
.
Use the functions and type with the prefix mtx
to manage
mutexes.
Each mutex has an identifier of type
mtx_t
, which is passed as an argument
to the functions that manage mutexes. Use
mtx_init
to create a mutex
and mtx_destroy
to
release any resources associated with a mutex when it is no longer needed.
To lock a mutex call
mtx_lock
,
mtx_timedlock
or
mtx_trylock
. To
unlock a mutex call
mtx_unlock
.
Use a value of type once_flag
,
initialized to the value ONCE_FLAG_INIT
,
to ensure that a function is called exactly once by passing
a function pointer and the address of the once_flag
object
to call_once
.
Use the functions and types with the prefix tss
to manage
thread-specific storage.
Each thread-specific storage pointer has an identifier of type
tss_t
, which is passed as an argument
to the functions that manage thread-specific storage.
Call tss_create
to create a thread-specific storage pointer and
tss_delete
to release any
resources associated with a thread-specific storage pointer when it is no longer
needed. To get the value held by the pointer in the current thread call
tss_get
. To change the value
held by the pointer in the current thread call
tss_set
.
Each thread-specific storage pointer may have an associated
destructor, specified in the call
to tss_create
. The destructor
will be called when a thread terminates and the value of the pointer
associated with that thread is not 0. The value of the pointer for that
thread is set to 0 before calling the destructor and the old value is
passed to the destructor. Since a destructor can store non-0 values in
thread-specific storage pointers, this process will be repeated until no pointers
for the terminating thread hold non-0 values or until a system-specific
maximum number of iterations
TSS_DTOR_ITERATIONS
has been made.
Most of the functions
return a value of type int
that indicates whether
the function succeeded. The values are as follows:
See also the Table of Contents and the Index.
Copyright © 1992-2013 by Dinkumware, Ltd. Portions derived from work copyright © 2001 by William E. Kempf. All rights reserved.