In the late 1980s and early 1990s, several different threading APIs existed. In 1995, POSIX.1c standardized the POSIX threads API, and this standard was later incorporated into SUSv3.
Several concepts apply to the Pthreads API as a whole, and we briefly introduce these before looking in detail at the API.
The Pthreads API defines a number of data types, some of which are listed in Table 29-1. We describe most of these data types in the following pages.
Table 29-1. Pthreads data types
Data type | Description |
---|---|
pthread_t | Thread identifier |
pthread_mutex_t | Mutex |
pthread_mutexattr_t | Mutex attributes object |
pthread_cond_t | Condition variable |
pthread_condattr_t | Condition variable attributes object |
pthread_key_t | Key for thread-specific data |
pthread_once_t | One-time initialization control context |
pthread_attr_t | Thread attributes object |
SUSv3 doesn’t specify how these data types should be represented, and portable programs should treat them as opaque data. By this, we mean that a program should avoid any reliance on knowledge of the structure or contents of a variable of one of these types. In particular, we can’t compare variables of these types using the C == operator.
In the traditional UNIX API, errno is a global integer variable. However, this doesn’t suffice for threaded programs. If a thread made a function call that returned an error in a global errno variable, then this would confuse other threads that might also be making function calls and checking errno. In other words, race conditions would result. Therefore, in threaded programs, each thread has its own errno value. On Linux, a thread-specific errno is achieved in a similar manner to most other UNIX implementations: errno is defined as a macro that expands into a function call returning a modifiable lvalue that is distinct for each thread. (Since the lvalue is modifiable, it is still possible to write assignment statements of the form errno = value in threaded programs.)
To summarize, the errno mechanism has been adapted for threads in a manner that leaves error reporting unchanged from the traditional UNIX API.
The original POSIX.1 standard followed K&R C usage in allowing a program to declare errno as extern int errno. SUSv3 doesn’t permit this usage (the change actually occurred in 1995 in POSIX.1c). Nowadays, a program is required to declare errno by including <errno.h>
, which enables the implementation of a per-thread errno.
The traditional method of returning status from system calls and some library functions is to return 0 on success and -1 on error, with errno being set to indicate the error. The functions in the Pthreads API do things differently. All Pthreads functions return 0 on success or a positive value on failure. The failure value is one of the same values that can be placed in errno by traditional UNIX system calls.
Because each reference to errno in a threaded program carries the overhead of a function call, our example programs don’t directly assign the return value of a Pthreads function to errno. Instead, we use an intermediate variable and employ our errExitEN() diagnostic function (Common Functions and Header Files), like so:
pthread_t *thread; int s; s = pthread_create(&thread, NULL, func, &arg); if (s != 0) errExitEN(s, "pthread_create");
On Linux, programs that use the Pthreads API must be compiled with the cc -pthread option. The effects of this option include the following:
The _REENTRANT
preprocessor macro is defined. This causes the declarations of a few reentrant functions to be exposed.
The program is linked with the libpthread library (the equivalent of -lpthread).
The precise options for compiling a multithreaded program vary across implementations (and compilers). Some other implementations (e.g., Tru64) also use cc -pthread; Solaris and HP-UX use cc -mt.