Multithreaded Programming Guide

Chapter 4 Programming with Synchronization Objects

This chapter describes the synchronization types that are available with threads. The chapter also discusses when and how to use synchronization.

Synchronization objects are variables in memory that you access just like data. Threads in different processes can communicate with each other through synchronization objects that are placed in threads-controlled shared memory. The threads can communicate with each other even though the threads in different processes are generally invisible to each other.

Synchronization objects can also be placed in files. The synchronization objects can have lifetimes beyond the life of the creating process.

The available types of synchronization objects are

Situations that can benefit from the use of synchronization include the following:


Note –

On 32-bit architectures, a long long is not atomic. (An atomic operation cannot be divided into smaller operations.) A long long is read and written as two 32-bit quantities. The types int, char , float, and pointers are atomic on SPARC architecture machines and Intel Architecture machines.


Mutual Exclusion Lock Attributes

Use mutual exclusion locks (mutexes) to serialize thread execution. Mutual exclusion locks synchronize threads, usually by ensuring that only one thread at a time executes a critical section of code. Mutex locks can also preserve single-threaded code.

To change the default mutex attributes, you can declare and initialize an attribute object. Often, the mutex attributes are set in one place at the beginning of the application so the attributes can be located quickly and modified easily. Table 4–1 lists the functions that manipulate mutex attributes.

Table 4–1 Mutex Attributes Routines

Operation 

Related Function Description 

Initialize a mutex attribute object 

pthread_mutexattr_init Syntax

Destroy a mutex attribute object 

pthread_mutexattr_destroy Syntax

Set the scope of a mutex 

pthread_mutexattr_setpshared Syntax

Get the scope of a mutex 

pthread_mutexattr_getpshared Syntax

Set the mutex type attribute 

pthread_mutexattr_settype Syntax

Get the mutex type attribute 

pthread_mutexattr_gettype Syntax

Set mutex attribute's protocol 

pthread_mutexattr_setprotocol Syntax

Get mutex attribute's protocol 

pthread_mutexattr_getprotocol Syntax

Set mutex attribute's priority ceiling 

pthread_mutexattr_setprioceiling Syntax

Get mutex attribute's priority ceiling 

pthread_mutexattr_getprioceiling Syntax

Set mutex's priority ceiling 

pthread_mutex_setprioceiling Syntax

Get mutex's priority ceiling 

pthread_mutex_getprioceiling Syntax

Set mutex's robust attribute 

pthread_mutexattr_setrobust_np Syntax

Get mutex's robust attribute 

pthread_mutexattr_getrobust_np Syntax

Initializing a Mutex Attribute Object

Use pthread_mutexattr_init(3C) to initialize attributes that are associated with the mutex object to their default values. Storage for each attribute object is allocated by the threads system during execution.

pthread_mutexattr_init Syntax

int pthread_mutexattr_init(pthread_mutexattr_t *mattr);
#include <pthread.h>

pthread_mutexattr_t mattr;
int ret;

/* initialize an attribute to default value */
ret = pthread_mutexattr_init(&mattr); 

mattr is an opaque type that contains a system-allocated attribute object. See Table 4–2 for information about the attributes in the mattr object.

Before a mutex attribute object can be reinitialized, the object must first be destroyed by a call to pthread_mutexattr_destroy(3C). The pthread_mutexattr_init() call results in the allocation of an opaque object. If the object is not destroyed, a memory leak results.

Table 4–2 Default Attribute Values for mattr

Attribute 

Value 

Result 

pshared

PTHREAD_PROCESS_PRIVATE

The initialized mutex can be used within a process. Only those threads created by the same process can operate on the mutex. 

type

PTHREAD_MUTEX_DEFAULT

The Solaris Pthreads implementation maps PTHREAD_MUTEX_DEFAULT to PTHREAD_MUTEX_NORMAL, which does not detect deadlock.

protocol

PTHREAD_PRIO_NONE

Thread priority and scheduling are not affected by the priority of the mutex owned by the thread. 

prioceiling

– 

The prioceiling value is drawn from the existing priority range for the SCHED_FIFO policy, as returned by the sched_get_priority_min() and sched_get_priority_max() functions. This priority range is determined by the Solaris version on which the mutex is created.

robustness

PTHREAD_MUTEX_STALLED_NP

When the owner of a mutex dies, all future calls to pthread_mutex_lock() for this mutex will be blocked from progress.

pthread_mutexattr_init Return Values

pthread_mutexattr_init() returns zero after completing successfully. Any other return value indicates that an error occurred. If either of the following conditions occurs, the function fails and returns the corresponding value.


ENOMEM

Description:

Insufficient memory exists to initialize the mutex attribute object.

Destroying a Mutex Attribute Object

pthread_mutexattr_destroy(3C) deallocates the storage space used to maintain the attribute object created by pthread_mutexattr_init().

pthread_mutexattr_destroy Syntax

int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr)
#include <pthread.h> 
pthread_mutexattr_t mattr; 
int ret; 
/* destroy an attribute */ 
ret = pthread_mutexattr_destroy(&mattr); 

pthread_mutexattr_destroy Return Values

pthread_mutexattr_destroy() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by mattr is invalid.

Setting the Scope of a Mutex

pthread_mutexattr_setpshared(3C) sets the scope of the mutex variable.

pthread_mutexattr_setpshared Syntax

int pthread_mutexattr_setpshared(pthread_mutexattr_t *restrict mattr,
         int *restrict pshared);
#include <pthread.h> 
pthread_mutexattr_t mattr; 
int ret; 
ret = pthread_mutexattr_init(&mattr); 
/* * resetting to its default value: private */ 
ret = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE);

The scope of a mutex variable can be either process private (intraprocess) or system wide (interprocess). To share the mutex among threads from more than one process, create the mutex in shared memory with the pshared attribute set to PTHREAD_PROCESS_SHARED .

If the mutex pshared attribute is set to PTHREAD_PROCESS_PRIVATE , only those threads created by the same process can operate on the mutex.

pthread_mutexattr_setpshared Return Values

pthread_mutexattr_setpshared() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by mattr is invalid.

Getting the Scope of a Mutex

pthread_mutexattr_getpshared(3C) returns the scope of the mutex variable defined by pthread_mutexattr_setpshared().

pthread_mutexattr_getpshared Syntax

int pthread_mutexattr_getpshared(pthread_mutexattr_t *restrict mattr, 
          int *restrict pshared);
#include <pthread.h> 
pthread_mutexattr_t mattr; 
int pshared, ret; 
/* get pshared of mutex */ 
ret = pthread_mutexattr_getpshared(&mattr, &pshared); 

Get the current value of pshared for the attribute object mattr. The value is either PTHREAD_PROCESS_SHARED or PTHREAD_PROCESS_PRIVATE.

pthread_mutexattr_getpshared Return Values

pthread_mutexattr_getpshared() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by mattr is invalid.

Setting the Mutex Type Attribute

pthread_mutexattr_settype(3C) sets the mutex type attribute.

pthread_mutexattr_settype Syntax

#include <pthread.h>

int pthread_mutexattr_settype(pthread_mutexattr_t  *attr , int type);

The default value of the type attribute is PTHREAD_MUTEX_DEFAULT.

The type argument specifies the type of mutex. The following list describes the valid mutex types:


PTHREAD_MUTEX_NORMAL

Description:

This type of mutex does not detect deadlock. A thread attempting to relock this mutex without first unlocking the mutex deadlocks. Attempting to unlock a mutex locked by a different thread results in undefined behavior. Attempting to unlock an unlocked mutex results in undefined behavior.


PTHREAD_MUTEX_ERRORCHECK

Description:

This type of mutex provides error checking. A thread attempting to relock this mutex without first unlocking the mutex returns an error. A thread attempting to unlock a mutex that another thread has locked returns an error. A thread attempting to unlock an unlocked mutex returns an error.


PTHREAD_MUTEX_RECURSIVE

Description:

A thread attempting to relock this mutex without first unlocking the mutex succeeds in locking the mutex. The relocking deadlock that can occur with mutexes of type PTHREAD_MUTEX_NORMAL cannot occur with this type of mutex. Multiple locks of this mutex require the same number of unlocks to release the mutex before another thread can acquire the mutex. A thread attempting to unlock a mutex that another thread has locked returns an error. A thread attempting to unlock an unlocked mutex returns an error.


PTHREAD_MUTEX_DEFAULT

Description:

An implementation is allowed to map this attribute to one of the other mutex types. The Solaris implementation maps this attribute to PTHREAD_PROCESS_NORMAL.

pthread_mutexattr_settype Return Values

If successful, the pthread_mutexattr_settype function returns zero. Otherwise, an error number is returned to indicate the error.


EINVAL

Description:

The value type or attr is invalid.

Getting the Mutex Type Attribute

pthread_mutexattr_gettype(3C) gets the mutex type attribute set by pthread_mutexattr_settype().

pthread_mutexattr_gettype Syntax

#include <pthread.h>

int pthread_mutexattr_gettype(pthread_mutexattr_t  *restrict attr , 
int  *restrict type);

The default value of the type attribute is PTHREAD_MUTEX_DEFAULT.

The type argument specifies the type of mutex. Valid mutex types include

For a description of each type, see pthread_mutexattr_settype Syntax.

pthread_mutexattr_gettype Return Values

On successful completion, pthread_mutexattr_gettype() returns 0. Any other return value indicates that an error occurred.


EINVAL

Description:

The value specified by type is invalid.

Setting the Mutex Attribute's Protocol

pthread_mutexattr_setprotocol(3C) sets the protocol attribute of a mutex attribute object.

pthread_mutexattr_setprotocol Syntax

#include <pthread.h> 
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, 
          int protocol);

attr points to a mutex attribute object created by an earlier call to pthread_mutexattr_init().

protocol defines the protocol that is applied to the mutex attribute object.

The value of protocol that is defined in pthread.h can be one of the following values: PTHREAD_PRIO_NONE , PTHREAD_PRIO_INHERIT, or PTHREAD_PRIO_PROTECT .

The PTHREAD_PRIO_INHERIT and PTHREAD_PRIO_PROTECT mutex attributes are usable only by privileged processes running in the realtime (RT) scheduling class SCHED_FIFO or SCHED_RR.

A thread can simultaneously own several mutexes initialized with a mix of PTHREAD_PRIO_INHERIT and PTHREAD_PRIO_PROTECT. In this case, the thread executes at the highest priority obtained by either of these protocols.

pthread_mutexattr_setprotocol Return Values

On successful completion, pthread_mutexattr_setprotocol() returns 0. Any other return value indicates that an error occurred.

If either of the following conditions occurs, pthread_mutexattr_setprotocol() might fail and return the corresponding value.


EINVAL

Description:

The value specified by attr or protocol is not valid.


EPERM

Description:

The caller does not have the privilege to perform the operation.

Getting the Mutex Attribute's Protocol

pthread_mutexattr_getprotocol(3C) gets the protocol attribute of a mutex attribute object.

pthread_mutexattr_getprotocol Syntax

#include <pthread.h> 
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict attr, 
int *restrict protocol);

attr points to a mutex attribute object created by an earlier call to pthread_mutexattr_init().

protocol contains one of the following protocol attributes: PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT, or PTHREAD_PRIO_PROTECT which are defined by the header <pthread.h>.

pthread_mutexattr_getprotocol Return Values

On successful completion, pthread_mutexattr_getprotocol() returns 0. Any other return value indicates that an error occurred.

If either of the following conditions occurs, pthread_mutexattr_getprotocol() might fail and return the corresponding value.


EINVAL

Description:

The value specified by attr is NULL, or the value specified by attr or protocol is invalid.


EPERM

Description:

The caller does not have the privilege to perform the operation.

Setting the Mutex Attribute's Priority Ceiling

pthread_mutexattr_setprioceiling(3C) sets the priority ceiling attribute of a mutex attribute object.

pthread_mutexattr_setprioceiling Syntax

#include <pthread.h> 
int pthread_mutexattr_setprioceiling(pthread_mutexatt_t *attr, int prioceiling);

attr points to a mutex attribute object created by an earlier call to pthread_mutexattr_init().

prioceiling specifies the priority ceiling of initialized mutexes. The ceiling defines the minimum priority level at which the critical section guarded by the mutex is executed. prioceiling falls within the maximum range of priorities defined by SCHED_FIFO. To avoid priority inversion, set prioceiling to a priority higher than or equal to the highest priority of all threads that might lock the particular mutex.

pthread_mutexattr_setprioceiling Return Values

On successful completion, pthread_mutexattr_setprioceiling() returns 0. Any other return value indicates that an error occurred.

If either of the following conditions occurs, pthread_mutexattr_setprioceiling() might fail and return the corresponding value.


EINVAL

Description:

The value specified by attr is NULL or invalid or prioceiling is invalid.


EPERM

Description:

The caller does not have the privilege to perform the operation.

Getting the Mutex Attribute's Priority Ceiling

pthread_mutexattr_getprioceiling(3C) gets the priority ceiling attribute of a mutex attribute object.

pthread_mutexattr_getprioceiling Syntax

#include <pthread.h> 
int pthread_mutexattr_getprioceiling(const pthread_mutexatt_t *restrict attr, 
           int *restrict prioceiling);

attr designates the attribute object created by an earlier call to pthread_mutexattr_init().

pthread_mutexattr_getprioceiling() returns the priority ceiling of initialized mutexes in prioceiling. The ceiling defines the minimum priority level at which the critical section guarded by the mutex is executed. prioceiling falls within the maximum range of priorities defined by SCHED_FIFO. To avoid priority inversion, set prioceiling to a priority higher than or equal to the highest priority of all threads that might lock the particular mutex.

pthread_mutexattr_getprioceiling Return Values

On successful completion, pthread_mutexattr_getprioceiling() returns 0. Any other return value indicates that an error occurred.

If either of the following conditions occurs, pthread_mutexattr_getprioceiling() might fail and return the corresponding value.


EINVAL

Description:

The value specified by attr is NULL.


EPERM

Description:

The caller does not have the privilege to perform the operation.

Setting the Mutex's Priority Ceiling

pthread_mutexattr_setprioceiling(3C) sets the priority ceiling of a mutex.

pthread_mutex_setprioceiling Syntax

#include <pthread.h> 
int pthread_mutex_setprioceiling(pthread_mutex_t *restrict mutex, 
          int prioceiling, int *restrict old_ceiling);

pthread_mutex_setprioceiling() changes the priority ceiling, prioceiling, of a mutex, mutex. pthread_mutex_setprioceiling() locks a mutex if unlocked, or blocks until pthread_mutex_setprioceiling() successfully locks the mutex, changes the priority ceiling of the mutex and releases the mutex. The process of locking the mutex need not adhere to the priority protect protocol.

If pthread_mutex_setprioceiling() succeeds, the previous value of the priority ceiling is returned in old_ceiling. If pthread_mutex_setprioceiling() fails, the mutex priority ceiling remains unchanged.

pthread_mutex_setprioceiling Return Values

On successful completion, pthread_mutex_setprioceiling() returns 0. Any other return value indicates that an error occurred.

If any of the following conditions occurs, pthread_mutex_setprioceiling() might fail and return the corresponding value.


EINVAL

Description:

The priority requested by prioceiling is out of range.


EINVAL

Description:

The mutex was not initialized with its protocol attribute having the value of THREAD_PRIO_PROTECT.


EPERM

Description:

The caller does not have the privilege to perform the operation.

Getting the Mutex's Priority Ceiling

pthread_mutexattr_getprioceiling(3C) gets the priority ceiling of a mutex.

pthread_mutex_getprioceiling Syntax

#include <pthread.h> 
int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict mutex, 
          int *restrict prioceiling);

pthread_mutex_getprioceiling() returns the priority ceiling, prioceiling of a mutex.

pthread_mutex_getprioceiling Return Values

On successful completion, pthread_mutex_getprioceiling() returns 0. Any other return value indicates that an error occurred.

If any of the following conditions occurs, pthread_mutexatt_getprioceiling() fails and returns the corresponding value.

If any of the following conditions occurs, pthread_mutex_getprioceiling() might fail and return the corresponding value.


EINVAL

Description:

The value specified by mutex does not refer to a currently existing mutex.


EPERM

Description:

The caller does not have the privilege to perform the operation.

Setting the Mutex's Robust Attribute

pthread_mutexattr_setrobust_np(3C) sets the robust attribute of a mutex attribute object.

pthread_mutexattr_setrobust_np Syntax

#include <pthread.h> 
int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *attr, int *robustness);

Note –

pthread_mutexattr_setrobust_np() applies only if the symbol _POSIX_THREAD_PRIO_INHERIT is defined.

In the Solaris 10 and prior releases, the PTHREAD_MUTEX_ROBUST_NP attribute can only be applied to mutexes that are also marked with the PTHREAD_PRIO_INHERIT protocol attribute. This restriction is lifted in subsequent Solaris releases.


attr points to the mutex attribute object previously created by a call to pthread_mutexattr_init().

robustness defines the behavior when the owner of the mutex terminates without unlocking the mutex, usually because its process terminated abnormally. The value of robustness that is defined in pthread.h is PTHREAD_MUTEX_ROBUST_NP or PTHREAD_MUTEX_STALLED_NP. The default value is PTHREAD_MUTEX_STALLED_NP .

pthread_mutexattr_setrobust_np Return Values

On successful completion, pthread_mutexattr_setrobust_np() returns 0. Any other return value indicates that an error occurred.

pthread_mutexattr_setrobust_np() might fail if the following condition occurs:


EINVAL

Description:

The value specified by attr or robustness is invalid.

Getting the Mutex's Robust Attribute

pthread_mutexattr_getrobust_np(3C) gets the robust attribute of a mutex attribute object.

pthread_mutexattr_getrobust_np Syntax

#include <pthread.h> 
int pthread_mutexattr_getrobust_np(const pthread_mutexattr_t *attr, int *robustness);

attr points to the mutex attribute object previously created by a call to pthread_mutexattr_init().

robustness is the value of the robust attribute of a mutex attribute object.

pthread_mutexattr_getrobust_np Return Values

On successful completion, pthread_mutexattr_getrobust_np() returns 0. Any other return value indicates that an error occurred.

pthread_mutexattr_getrobust_np() might fail if the following condition occurs:


EINVAL

Description:

The value specified by attr or robustness is invalid.

Using Mutual Exclusion Locks

Table 4–3 lists the functions that manipulate mutex locks.

Table 4–3 Routines for Mutual Exclusion Locks

Operation 

Related Function Description 

Initialize a mutex 

pthread_mutex_init Syntax

Make mutex consistent 

pthread_mutex_consistent_np Syntax

Lock a mutex 

pthread_mutex_lock Syntax

Unlock a mutex 

pthread_mutex_unlock Syntax

Lock with a nonblocking mutex 

pthread_mutex_trylock Syntax

Lock a mutex before a specified time 

pthread_mutex_timedlock() Syntax

Lock a mutex within a specified time interval 

pthread_mutex_reltimedlock_np() Syntax

Destroy a mutex 

pthread_mutex_destroy Syntax

The default scheduling policy, SCHED_OTHER, does not specify the order in which threads can acquire a lock. When multiple SCHED_OTHER threads are waiting for a mutex, the order of acquisition is undefined. Under the SCHED_FIFO and SCHED_RR real-time scheduling policies, the behavior is to unblock waiting threads in priority order.

Initializing a Mutex

Use pthread_mutex_init(3C) to initialize the mutex pointed at by mp to its default value or to specify mutex attributes that have already been set with pthread_mutexattr_init() . The default value for mattr is NULL .

pthread_mutex_init Syntax

int pthread_mutex_init(pthread_mutex_t *restrict mp,
          const pthread_mutexattr_t *restrict mattr);
#include <pthread.h>

pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
pthread_mutexattr_t mattr;
int ret;

/* initialize a mutex to its default value */
ret = pthread_mutex_init(&mp, NULL);

/* initialize a mutex */
ret = pthread_mutex_init(&mp, &mattr); 

When the mutex is initialized, the mutex is in an unlocked state. The mutex can be in memory that is shared between processes or in memory private to a process.


Note –

For a mutex that is being initialized with the PTHREAD_MUTEX_ROBUST_NP attribute, the mutex memory must be cleared to zero before initialization.


The effect of mattr set to NULL is the same as passing the address of a default mutex attribute object, but without the memory overhead.

Use the macro PTHREAD_MUTEX_INITIALIZER to initialize statically defined mutexes to their default attributes.

Do not reinitialize or destroy a mutex lock while other threads are using the mutex. Program failure results if either action is not done correctly. If a mutex is reinitialized or destroyed, the application must be sure the mutex is not currently in use.

pthread_mutex_init Return Values

pthread_mutex_init() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occurs, the function fails and returns the corresponding value.


EBUSY

Description:

The implementation has detected an attempt to reinitialize the object referenced by mp, a previously initialized but not yet destroyed mutex.


EINVAL

Description:

The mattr attribute value is invalid. The mutex has not been modified.


EFAULT

Description:

The address for the mutex pointed at by mp is invalid.

Making a Mutex Consistent

If the owner of a robust mutex terminates without unlocking the mutex, the mutex is unlocked and marked inconsistent. The next owner acquires the lock with an EOWNERDEAD return code.

pthread_mutex_consistent_np() makes the mutex object, mutex, consistent after the death of its owner.

pthread_mutex_consistent_np Syntax

#include <pthread.h> 
int pthread_mutex_consistent_np(pthread_mutex_t *mutex); 

Call pthread_mutex_lock() to acquire the inconsistent mutex. The EOWNWERDEAD return value indicates an inconsistent mutex.

Call pthread_mutex_consistent_np() while holding the mutex acquired by a previous call to pthread_mutex_lock().

The critical section protected by the mutex might have been left in an inconsistent state by a failed owner. In this case, make the mutex consistent only if you can make the critical section protected by the mutex consistent.

Calls to pthread_mutex_lock(), pthread_mutex_unlock() , and pthread_mutex_trylock() for a consistent mutex behave in the normal manner.

The behavior of pthread_mutex_consistent_np() for a mutex that is not inconsistent, or is not held, is undefined.

pthread_mutex_consistent_np Return Values

pthread_mutex_consistent_np() returns zero after completing successfully. Any other return value indicates that an error occurred.

pthread_mutex_consistent_np() fails if the following condition occurs:


EINVAL

Description:

The current thread does not own the mutex or the mutex is not a PTHREAD_MUTEX_ROBUST_NP mutex having an inconsistent state.

Locking a Mutex

Use pthread_mutex_lock(3C) to lock the mutex pointed to by mutex.

pthread_mutex_lock Syntax

int  pthread_mutex_lock(pthread_mutex_t *mutex); 
#include <pthread.h>

pthread_mutex_t mutex;
int ret;

ret = pthread_ mutex_lock(&mp); /* acquire the mutex */

When pthread_mutex_lock() returns, the mutex is locked. The calling thread is the owner. If the mutex is already locked and owned by another thread, the calling thread blocks until the mutex becomes available.

If the mutex type is PTHREAD_MUTEX_NORMAL , deadlock detection is not provided. Attempting to relock the mutex causes deadlock. If a thread attempts to unlock a mutex not locked by the thread or a mutex that is unlocked, undefined behavior results.

If the mutex type is PTHREAD_MUTEX_ERRORCHECK , then error checking is provided. If a thread attempts to relock a mutex that the thread has already locked, an error is returned. If a thread attempts to unlock a mutex not locked by the thread or a mutex that is unlocked, an error is returned.

If the mutex type is PTHREAD_MUTEX_RECURSIVE , then the mutex maintains the concept of a lock count. When a thread successfully acquires a mutex for the first time, the lock count is set to 1. Every time a thread relocks this mutex, the lock count is incremented by 1. Every time the thread unlocks the mutex, the lock count is decremented by 1. When the lock count reaches 0, the mutex becomes available for other threads to acquire. If a thread attempts to unlock a mutex not locked by the thread or a mutex that is unlocked, an error is returned.

The mutex type PTHREAD_MUTEX_DEFAULT is the same as PTHREAD_MUTEX_NORMAL.

pthread_mutex_lock Return Values

pthread_mutex_lock() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occurs, the function fails and returns the corresponding value.


EAGAIN

Description:

The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded.


EDEADLK

Description:

The current thread already owns the mutex.

If the mutex was initialized with the PTHREAD_MUTEX_ROBUST_NProbustness attribute, pthread_mutex_lock() may return one of the following values:


EOWNERDEAD

Description:

The last owner of this mutex terminated while holding the mutex. This mutex is now owned by the caller. The caller must attempt to make the state protected by the mutex consistent.

If the caller is able to make the state consistent, call pthread_mutex_consistent_np() for the mutex and unlock the mutex. Subsequent calls to pthread_mutex_lock() behave normally.

If the caller is unable to make the state consistent, do not call pthread_mutex_init() for the mutex. Unlock the mutex instead. Subsequent calls to pthread_mutex_lock() fail to acquire the mutex and return an ENOTRECOVERABLE error code.

If the owner that acquired the lock with EOWNERDEAD terminates while holding the mutex, the next owner acquires the lock with EOWNERDEAD.


ENOTRECOVERABLE

Description:

The mutex you are trying to acquire was protecting state left irrecoverable by the mutex's previous owner. The mutex has not been acquired. This irrecoverable condition can occur when:

  • The lock was previously acquired with EOWNERDEAD

  • The owner was unable to cleanup the state

  • The owner unlocked the mutex without making the mutex state consistent


ENOMEM

Description:

The limit on the number of simultaneously held mutexes has been exceeded.

Unlocking a Mutex

Use pthread_mutex_unlock(3C) to unlock the mutex pointed to by mutex.

pthread_mutex_unlock Syntax

int pthread_mutex_unlock(pthread_mutex_t *mutex); 
#include <pthread.h>

pthread_mutex_t mutex;
int ret;

ret = pthread_mutex_unlock(&mutex); /* release the mutex */

pthread_mutex_unlock() releases the mutex object referenced by mutex. The manner in which a mutex is released is dependent upon the mutex's type attribute. If threads are blocked on the mutex object when pthread_mutex_unlock() is called and the mutex becomes available, the scheduling policy determines which thread acquires the mutex. For PTHREAD_MUTEX_RECURSIVE mutexes, the mutex becomes available when the count reaches zero and the calling thread no longer has any locks on this mutex.

pthread_mutex_unlock Return Values

pthread_mutex_unlock() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EPERM

Description:

The current thread does not own the mutex.

Locking a Mutex Without Blocking

Use pthread_mutex_trylock(3C) to attempt to lock the mutex pointed to by mutex, and return immediately if the mutex is already locked.

pthread_mutex_trylock Syntax

int pthread_mutex_trylock(pthread_mutex_t *mutex); 
#include <pthread.h>

pthread_mutex_t mutex;
int ret;

ret = pthread_mutex_trylock(&mutex); /* try to lock the mutex */

pthread_mutex_trylock() is a nonblocking version of pthread_mutex_lock(). If the mutex object referenced by mutex is currently locked by any thread, including the current thread, the call returns immediately. Otherwise, the mutex is locked and the calling thread is the owner.

pthread_mutex_trylock Return Values

pthread_mutex_trylock() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occurs, the function fails and returns the corresponding value.


EBUSY

Description:

The mutex could not be acquired because the mutex pointed to by mutex was already locked.


EAGAIN

Description:

The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded.

If the symbol _POSIX_THREAD_PRIO_INHERIT is defined, the mutex is initialized with the protocol attribute value PTHREAD_PRIO_INHERIT . Additionally, if the robustness argument of pthread_mutexattr_setrobust_np() is PTHREAD_MUTEX_ROBUST_NP, the function fails and returns one of the following values:


EOWNERDEAD

Description:

See the discussion in pthread_mutex_lock Return Values.


ENOTRECOVERABLE

Description:

See the discussion in pthread_mutex_lock Return Values.


ENOMEM

Description:

The limit on the number of simultaneously held mutexes has been exceeded.

Locking a Mutex Before a Specified Absolute Time

Use the pthread_mutex_timedlock(3C) function to attempt until a specified time to lock a mutex object.

This function works as the pthread_mutex_lock() function does, except that it does not block indefinitely. If the mutex is already locked, the calling thread is blocked until the mutex becomes available, but only until the timeout is reached. If the timeout occurs before the mutex becomes available, the function returns.

pthread_mutex_timedlock() Syntax

int  pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, 
          const struct timespec *restrict abs_timeout);
#include <pthread.h>
#include <time.h>

pthread_mutex_t mutex;
timestruct_t abs_timeout;
int ret;

ret = pthread_mutex_timedlock(&mutex,  &abs_timeout); 

pthread_mutex_timedlock() Return Values

The pthread_mutex_timedlock() function return 0 if it locks the mutex successfully. Otherwise, an error number is returned to indicate the error.


EINVAL

Description:

The mutex was created with the protocol attribute having the value PTHREAD_PRIO_PROTECT and the calling thread's priority is higher than the mutex's current priority ceiling.

Description:

The value specified by mutex does not refer to an initialized mutex object.

Description:

The process or thread would have blocked, and the abs_timeout parameter specified a nanoseconds field value less than 0 or greater than or equal to 1000 million.


ETIMEDOUT

Description:

The mutex could not be locked before the specified timeout expired.

See the discussion in pthread_mutex_lock Return Values.

Locking a Mutex Within a Specified Time Interval

Use the pthread_mutex_reltimedlock_np(3C) function to attempt until a specified amount of time elapses to lock a mutex object.

The timeout expires when the time interval specified by rel_timeout passes, as measured by the CLOCK_REALTIME clock, or if the time interval specified by rel_timeout is negative at the time of the call.

pthread_mutex_reltimedlock_np() Syntax

int  pthread_mutex_reltimedlock_np(pthread_mutex_t *restrict mutex, 
          const struct timespec *restrict rel_timeout);
#include <pthread.h>
#include <time.h>

pthread_mutex_t mutex;
timestruct_t rel_timeout;
int ret;

ret = pthread_mutex_reltimedlock_np(&mutex,  &rel_timeout); 

pthread_mutex_reltimedlock_np() Return Values

The pthread_mutex_reltimedlock_np() function returns 0 if it locks the mutex successfully. Otherwise, an error number is returned to indicate the error.


EINVAL

Description:

The mutex was created with the protocol attribute having the value PTHREAD_PRIO_PROTECT and the calling thread's priority is higher than the mutex's current priority ceiling.

Description:

The value specified by mutex does not refer to an initialized mutex object.

Description:

The process or thread would have blocked, and the abs_timeout parameter specified a nanoseconds field value less than 0 or greater than or equal to 1000 million.


ETIMEDOUT

Description:

The mutex could not be locked before the specified timeout expired.

See the discussion in pthread_mutex_lock Return Values.

Destroying a Mutex

Use pthread_mutex_destroy(3C) to destroy any state that is associated with the mutex pointed to by mp .

pthread_mutex_destroy Syntax

int pthread_mutex_destroy(pthread_mutex_t *mp); 
#include <pthread.h>

pthread_mutex_t mp;
int ret;

ret = pthread_mutex_destroy(&mp); /* mutex is destroyed */

Note that the space for storing the mutex is not freed.

pthread_mutex_destroy Return Values

pthread_mutex_destroy() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by mp does not refer to an initialized mutex object.

Code Examples of Mutex Locking

Example 4–1 shows some code fragments with mutex locking.


Example 4–1 Mutex Lock Example

#include <pthread.h>

pthread_mutex_t count_mutex;
long long count;

void
increment_count()
{
    pthread_mutex_lock(&count_mutex);
    count = count + 1;
    pthread_mutex_unlock(&count_mutex);
}

long long
get_count()
{
    long long c;
    
    pthread_mutex_lock(&count_mutex);
    c = count;
    pthread_mutex_unlock(&count_mutex);
    return (c);
}

The two functions in Example 4–1 use the mutex lock for different purposes. The increment_count() function uses the mutex lock to ensure an atomic update of the shared variable. The get_count() function uses the mutex lock to guarantee that the 64-bit quantity count is read atomically. On a 32-bit architecture, a long long is really two 32-bit quantities.

When you read an integer value, the operation is atomic because an integer is the common word size on most machines.

Examples of Using Lock Hierarchies

Occasionally, you might want to access two resources at once. Perhaps you are using one of the resources, and then discover that the other resource is needed as well. A problem exists if two threads attempt to claim both resources but lock the associated mutexes in different orders. For example, if the two threads lock mutexes 1 and 2 respectively, a deadlock occurs when each attempts to lock the other mutex. Example 4–2 shows possible deadlock scenarios.


Example 4–2 Deadlock

Thread 1 

Thread 2 

pthread_mutex_lock(&m1);

/* use resource 1 */


pthread_mutex_lock(&m2);


/* use resources 1 and 2 */


pthread_mutex_unlock(&m2);


pthread_mutex_unlock(&m1);
pthread_mutex_lock(&m2);

/* use resource 2 */


pthread_mutex_lock(&m1);


/* use resources 1 and 2 */


pthread_mutex_unlock(&m1);


pthread_mutex_unlock(&m2);


The best way to avoid this problem is to make sure that when threads lock multiple mutexes, the threads do so in the same order. When locks are always taken in a prescribed order, deadlock should not occur. This technique, known as lock hierarchies, orders the mutexes by logically assigning numbers to the mutexes.

Also, honor the restriction that you cannot take a mutex that is assigned n when you are holding any mutex assigned a number that is greater than n.

However, this technique cannot always be used. Sometimes, you must take the mutexes in an order other than prescribed. To prevent deadlock in such a situation, use pthread_mutex_trylock(). One thread must release its mutexes when the thread discovers that deadlock would otherwise be inevitable.


Example 4–3 Conditional Locking

thread1 

thread2 

pthread_mutex_lock(&m1); pthread_mutex_lock(&m2);

 

 

 

 

/* no processing */ 

 

pthread_mutex_unlock(&m2);

pthread_mutex_unlock(&m1);

for (; ;)

{ pthread_mutex_lock(&m2);

 

 

if(pthread_mutex_trylock(&m1)==0)

/* got it */ 

break;

/* didn't get it */ 

pthread_mutex_unlock(&m2);

}

/* get locks; no processing */ 

pthread_mutex_unlock(&m1);

pthread_mutex_unlock(&m2);


In Example 4–3, thread1 locks mutexes in the prescribed order, but thread2 takes the mutexes out of order. To make certain that no deadlock occurs, thread2 has to take mutex1 very carefully. If thread2 blocks while waiting for the mutex to be released, thread2 is likely to have just entered into a deadlock with thread1.

To ensure that thread2 does not enter into a deadlock, thread2 calls pthread_mutex_trylock(), which takes the mutex if available. If the mutex is not available, thread2 returns immediately, reporting failure. At this point, thread2 must release mutex2. Thread1 can now lock mutex2, and then release both mutex1 and mutex2.

Examples of Using Nested Locking With a Singly-Linked List

Example 4–4 and Example 4–5 show how to take three locks at once. Deadlock is prevented by taking the locks in a prescribed order.


Example 4–4 Singly-Linked List Structure

typedef struct node1 {
    int value;
    struct node1 *link;
    pthread_mutex_t lock;
} node1_t;

node1_t ListHead;

This example uses a singly linked list structure with each node that contains a mutex. To remove a node from the list, first search the list starting at ListHead until the desired node is found. ListHead is never removed.

To protect this search from the effects of concurrent deletions, lock each node before any of its contents are accessed. Because all searches start at ListHead, a deadlock cannot occur because the locks are always taken in list order.

When the desired node is found, lock both the node and its predecessor since the change involves both nodes. Because the predecessor's lock is always taken first, you are again protected from deadlock. Example 4–5 shows the C code to remove an item from a singly-linked list.


Example 4–5 Singly-Linked List With Nested Locking

node1_t *delete(int value)
{
    node1_t *prev, *current;

    prev = &ListHead;
    pthread_mutex_lock(&prev->lock);
    while ((current = prev->link) != NULL) {
        pthread_mutex_lock(&current->lock);
        if (current->value == value) {
            prev->link = current->link;
            pthread_mutex_unlock(&current->lock);
            pthread_mutex_unlock(&prev->lock);
            current->link = NULL;
            return(current);
        }
        pthread_mutex_unlock(&prev->lock);
        prev = current;
    }
    pthread_mutex_unlock(&prev->lock);
    return(NULL);
}

Example of Nested Locking With a Circularly-Linked List

Example 4–6 modifies the previous list structure by converting the list structure into a circular list. Because a distinguished head node no longer exists, a thread can be associated with a particular node and can perform operations on that node and its neighbor Lock hierarchies do not work easily here because the obvious hierarchy, following the links, is circular.


Example 4–6 Circular-Linked List Structure

typedef struct node2 {
    int value;
    struct node2 *link;
    pthread_mutex_t lock;
} node2_t;

Here is the C code that acquires the locks on two nodes and performs an operation that involves both locks.


Example 4–7 Circular Linked List With Nested Locking

void Hit Neighbor(node2_t *me) {
    while (1) {
        pthread_mutex_lock(&me->lock);
        if (pthread_mutex_trylock(&me->link->lock)!= 0) {
            /* failed to get lock */             
            pthread_mutex_unlock(&me->lock);              
            continue;         
        }         
        break;     
    }     
    me->link->value += me->value;     
    me->value /=2;     
    pthread_mutex_unlock(&me->link->lock);     
    pthread_mutex_unlock(&me->lock);
}

Using Spin Locks

Spin locks are a low-level synchronization mechanism suitable primarily for use on shared memory multiprocessors. When the calling thread requests a spin lock that is already held by another thread, the second thread spins in a loop to test if the lock has become available. When the lock is obtained, it should be held only for a short time, as the spinning wastes processor cycles. Callers should unlock spin locks before calling sleep operations to enable other threads to obtain the lock.

Spin locks can be implemented using mutexes and conditional variables, but the pthread_spin_* functions are a standardized way to practice spin locking. The pthread_spin_* functions require much lower overhead for locks of short duration.

When performing any lock, a trade-off is made between the processor resources consumed while setting up to block the thread and the processor resources consumed by the thread while it is blocked. Spin locks require few resources to set up the blocking of a thread and then do a simple loop, repeating the atomic locking operation until the lock is available. The thread continues to consume processor resources while it is waiting.

Compared to spin locks, mutexes consume a larger amount of processor resources to block the thread. When a mutex lock is not available, the thread changes its scheduling state and adds itself to the queue of waiting threads. When the lock becomes available, these steps must be reversed before the thread obtains the lock. While the thread is blocked, it consumes no processor resources.

Therefore, spin locks and mutexes can be useful for different purposes. Spin locks might have lower overall overhead for very short-term blocking, and mutexes might have lower overall overhead when a thread will be blocked for longer periods of time.

Initializing a Spin Lock

Use the pthread_spin_init(3C) function to allocate resources required to use a spin lock, and initialize the lock to an unlocked state.

pthread_spin_init() Syntax

int  pthread_spin_init(pthread_spinlock_t *lock, int pshared);
#include <pthread.h>

pthread_spinlock_t lock;
int pshared;
int ret;

/* initialize a spin lock */
ret = pthread_spin_init(&lock, pshared); 

The pshared attribute has one of the following values:


PTHREAD_PROCESS_SHARED

Description:

Permits a spin lock to be operated on by any thread that has access to the memory where the spin lock is allocated. Operation on the lock is permitted even if the lock is allocated in memory that is shared by multiple processes.


PTHREAD_PROCESS_PRIVATE

Description:

Permits a spin lock to be operated upon only by threads created within the same process as the thread that initialized the spin lock. If threads of differing processes attempt to operate on such a spin lock, the behavior is undefined. The default value of the process-shared attribute is PTHREAD_PROCESS_PRIVATE.

pthread_spin_init() Return Values

Upon successful completion, the pthread_spin_init() function returns 0. Otherwise, one of the following error codes is returned.


EAGAIN

Description:

The system lacks the necessary resources to initialize another spin lock.


EBUSY

Description:

The system has detected an attempt to initialize or destroy a spin lock while the lock is in use (for example, while being used in a pthread_spin_lock() call) by another thread.


EINVAL

Description:

The value specified by lock is invalid.

Acquiring a Spin Lock

Use the pthread_spin_lock(3C) to lock a spin lock. The calling thread acquires the lock if it is not held by another thread. Otherwise, the thread does not return from the pthread_spin_lock() call until the lock becomes available. The results are undefined if the calling thread holds the lock at the time the call is made.

pthread_spin_lock() Syntax

int  pthread_spin_lock(pthread_spinlock_t *lock);
#include <pthread.h>

pthread_spinlock_t lock;
int ret;

ret = pthread_ spin_lock(&lock); /* lock the spinlock */

pthread_spin_lock() Return Values

Upon successful completion, the pthread_spin_lock() function returns 0. Otherwise, one of the following error codes is returned.


EDEADLK

Description:

The current thread already owns the spin lock.


EINVAL

Description:

The value specified by lock does not refer to an initialized spin lock object.

Acquiring a Non-Blocking Spin Lock

Use the pthread_spin_trylock(3C) function to lock a spin lock and fail immediately if the lock is held by another thread.

pthread_spin_trylock() Syntax

int  pthread_spin_trylock(pthread_spinlock_t *lock);
#include <pthread.h>

pthread_spinlock_t lock;
int ret;

ret = pthread_spin_trylock(&lock); /* try to lock the spin lock */

pthread_spin_trylock() Return Values

Upon successful completion, the pthread_spin_trylock() function returns 0. Otherwise, one of the following error codes is returned.


EBUSY

Description:

A thread currently owns the spin lock.


EINVAL

Description:

The value specified by lock does not refer to an initialized spin lock object.

Unlocking a Spin Lock

Use the pthread_spin_unlock(3C) function to release a locked spin lock.

pthread_spin_unlock() Syntax

int  pthread_spin_unlock(pthread_spinlock_t *lock);
#include <pthread.h>

pthread_spinlock_t lock;
int ret;

ret = pthread_spin_unlock(&lock); /* spinlock is unlocked */

pthread_spin_unlock() Return Values

Upon successful completion, the pthread_spin_unlock() function returns 0. Otherwise, one of the following error codes is returned.


EPERM

Description:

The calling thread does not hold the lock.


EINVAL

Description:

The value specified by lock does not refer to an initialized spin lock object.

Destroying a Spin Lock

Use the pthread_spin_destroy(3C) function to destroy a spin lock and release any resources used by the lock.

pthread_spin_destroy() Syntax

int  pthread_spin_destroy(pthread_spinlock_t *lock);
#include <pthread.h>

pthread_spinlock_t lock;
int ret;

ret = pthread_spin_destroy(&lock); /* spinlock is destroyed */

The effect of subsequent use of the lock is undefined until the lock is reinitialized by another call to pthread_spin_init(). The results are undefined if pthread_spin_destroy() is called when a thread holds the lock, or if this function is called with an uninitialized thread spin lock.

pthread_spin_destroy() Return Values


EBUSY

Description:

The system has detected an attempt to initialize or destroy a spin lock while the lock is in use (for example, while being used in a pthread_spin_lock() call) by another thread.


EINVAL

Description:

The value specified by lock is invalid.

Condition Variable Attributes

Use condition variables to atomically block threads until a particular condition is true. Always use condition variables together with a mutex lock.

With a condition variable, a thread can atomically block until a condition is satisfied. The condition is tested under the protection of a mutual exclusion lock (mutex).

When the condition is false, a thread usually blocks on a condition variable and atomically releases the mutex waiting for the condition to change. When another thread changes the condition, that thread can signal the associated condition variable to cause one or more waiting threads to perform the following actions:

Condition variables can be used to synchronize threads among processes in the following situations:

The scheduling policy determines how blocking threads are awakened. The default scheduling policy, SCHED_OTHER, does not specify the order in which threads are awakened. Under the SCHED_FIFO and SCHED_RR real-time scheduling policies, threads are awakened in priority order.

The attributes for condition variables must be set and initialized before the condition variables can be used. The functions that manipulate condition variable attributes are listed in Table 4–4.

Table 4–4 Condition Variable Attributes

Operation 

Function Description 

Initialize a condition variable attribute 

pthread_condattr_init Syntax

Remove a condition variable attribute 

pthread_condattr_destroy Syntax

Set the scope of a condition variable  

pthread_condattr_setpshared Syntax

Get the scope of a condition variable 

pthread_condattr_getpshared Syntax

Get the clock selection condition variable attribute 

pthread_condattr_getclock Syntax

Set the clock selection condition variable attribute 

pthread_condattr_setclock Syntax

Initializing a Condition Variable Attribute

Use pthread_condattr_init(3C) to initialize attributes that are associated with this object to their default values. Storage for each attribute object is allocated by the threads system during execution.

pthread_condattr_init Syntax

int pthread_condattr_init(pthread_condattr_t *cattr);
#include <pthread.h>
pthread_condattr_t cattr;
int ret;

/* initialize an attribute to default value */
ret = pthread_condattr_init(&cattr); 

The default value of the pshared attribute when this function is called is PTHREAD_PROCESS_PRIVATE. This value of pshared means that the initialized condition variable can be used within a process.

cattr is an opaque data type that contains a system-allocated attribute object. The possible values of cattr's scope are PTHREAD_PROCESS_PRIVATE and PTHREAD_PROCESS_SHARED . PTHREAD_PROCESS_PRIVATE is the default value.

Before a condition variable attribute can be reused, the attribute must first be reinitialized by pthread_condattr_destroy(3C). The pthread_condattr_init() call returns a pointer to an opaque object. If the object is not destroyed, a memory leak results.

pthread_condattr_init Return Values

pthread_condattr_init() returns zero after completing successfully. Any other return value indicates that an error occurred. When either of the following conditions occurs, the function fails and returns the corresponding value.


ENOMEM

Description:

Insufficient memory allocated to initialize the thread attributes object.


EINVAL

Description:

The value specified by cattr is invalid.

Removing a Condition Variable Attribute

Use pthread_condattr_destroy(3C) to remove storage and render the attribute object invalid.

pthread_condattr_destroy Syntax

int pthread_condattr_destroy(pthread_condattr_t *cattr);
#include <pthread.h>
pthread_condattr_t cattr;
int ret;

/* destroy an attribute */
ret
 = pthread_condattr_destroy(&cattr); 

pthread_condattr_destroy Return Values

pthread_condattr_destroy() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by cattr is invalid.

Setting the Scope of a Condition Variable

pthread_condattr_setpshared(3C) sets the scope of a condition variable to either process private (intraprocess) or system wide (interprocess).

pthread_condattr_setpshared Syntax

int pthread_condattr_setpshared(pthread_condattr_t *cattr, int pshared);
#include <pthread.h> 
pthread_condattr_t cattr; 
int ret; 

/* all processes */ 
ret = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); 

/* within a process */ 
ret = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_PRIVATE);

A condition variable created with the pshared attribute set in shared memory to PTHREAD_PROCESS_SHARED, can be shared among threads from more than one process.

If the mutex pshared attribute is set to PTHREAD_PROCESS_PRIVATE, only those threads created by the same process can operate on the mutex. PTHREAD_PROCESS_PRIVATE is the default value. PTHREAD_PROCESS_PRIVATE behaves like a local condition variable. The behavior of PTHREAD_PROCESS_SHARED is equivalent to a global condition variable.

pthread_condattr_setpshared Return Values

pthread_condattr_setpshared() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value of cattr is invalid, or the pshared value is invalid.

Getting the Scope of a Condition Variable

pthread_condattr_getpshared(3C) gets the current value of pshared for the attribute object cattr.

pthread_condattr_getpshared Syntax

int pthread_condattr_getpshared(const pthread_condattr_t *restrict cattr,
          int *restrict pshared);
#include <pthread.h> 

pthread_condattr_t cattr; 
int pshared; 
int ret; 

/* get pshared value of condition variable */ 
ret = pthread_condattr_getpshared(&cattr, &pshared); 

The value of the attribute object is either PTHREAD_PROCESS_SHARED or PTHREAD_PROCESS_PRIVATE.

pthread_condattr_getpshared Return Values

pthread_condattr_getpshared() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value of cattr is invalid.

Setting the Clock Selection Condition Variable

Use the pthread_condattr_setclock(3C) function to set the clock attribute in an initialized attributes object referenced by attr. If pthread_condattr_setclock() is called with a clock_id argument that refers to a CPU-time clock, the call fails. The clock attribute is the clock ID of the clock that is used to measure the timeout service of pthread_cond_timedwait(). The default value of the clock attribute refers to the system clock, CLOCK_REALTIME. At this time, the only other possible value for the clock attribute is CLOCK_MONOTONIC.

pthread_condattr_setclock Syntax

int pthread_condattr_setclock(pthread_condattr_t attr, 
          clockid_t clock_id);
#include <pthread.h>

pthread_condattr_t attr
clockid_t clock_id
int ret


ret = pthread_condattr_setclock(&attr &clock_id

pthread_condattr_setclock Returns

pthread_condattr_setclock() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by clock_id does not refer to a known clock, or is a CPU-time clock.

Getting the Clock Selection Condition Variable

Use the pthread_condattr_getclock(3C) function to obtain the value of the clock attribute from the attributes object referenced by attr. The clock attribute is the clock ID of the clock that is used to measure the timeout service of pthread_cond_timedwait().

pthread_condattr_getclock Syntax

int pthread_condattr_getclock(const pthread_condattr_t *restrict attr,
          clockid_t *restrict clock_id);
#include <pthread.h>

pthread_condattr_t attr
clockid_t clock_id
int ret


ret = pthread_condattr_getclock(&attr &clock_id

pthread_condattr_getclock Returns

pthread_condattr_getclock() returns zero after completing successfully and stores the value of the clock attribute of attr into the object referenced by the clock_id argument. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value of attr is invalid.

Using Condition Variables

This section explains how to use condition variables. Table 4–5 lists the functions that are available.

Table 4–5 Condition Variables Functions

Operation 

Related Function Description 

Initialize a condition variable 

pthread_cond_init Syntax

Block on a condition variable 

pthread_cond_wait Syntax

Unblock a specific thread 

pthread_cond_signal Syntax

Block until a specified time 

pthread_cond_timedwait Syntax

Block for a specified interval 

pthread_cond_reltimedwait_np Syntax

Unblock all threads 

pthread_cond_broadcast Syntax

Destroy condition variable state 

pthread_cond_destroy Syntax

Initializing a Condition Variable

Use pthread_cond_init(3C) to initialize the condition variable pointed at by cv to its default value, or to specify condition variable attributes that are already set with pthread_condattr_init().

pthread_cond_init Syntax

int pthread_cond_init(pthread_cond_t *restrict cv,
          const pthread_condattr_t *restrict cattr);
#include <pthread.h>

pthread_cond_t cv;
pthread_condattr_t cattr;
int ret;

/* initialize a condition variable to its default value */
ret = pthread_cond_init(&cv, NULL);

/* initialize a condition variable */
ret = pthread_cond_init(&cv, &cattr); 

The effect of cattr set to NULL is the same as passing the address of a default condition variable attribute object, but without the memory overhead.

Use the macro PTHREAD_COND_INITIALIZER to initialize statically defined condition variables to their default attributes. The PTHREAD_COND_INITIALIZER macro has the same effect as dynamically allocating pthread_cond_init() with null attributes. No error checking is done.

Multiple threads must not simultaneously initialize or reinitialize the same condition variable. If a condition variable is reinitialized or is destroyed, the application must be sure that the condition variable is not in use.

pthread_cond_init Return Values

pthread_cond_init() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by cattr is invalid.


EBUSY

Description:

The condition variable is being used.


EAGAIN

Description:

The necessary resources are not available.


ENOMEM

Description:

Insufficient memory exists to initialize the condition variable.

Blocking on a Condition Variable

Use pthread_cond_wait(3C) to atomically release the mutex pointed to by mp and to cause the calling thread to block on the condition variable pointed to by cv.

pthread_cond_wait Syntax

int pthread_cond_wait(pthread_cond_t *restrict cv,pthread_mutex_t *restrict mutex);
#include <pthread.h>

pthread_cond_t cv;
pthread_mutex_t mp;
int ret;

/* wait on condition variable */
ret = pthread_cond_wait(&cv, &
mp); 

The blocked thread can be awakened by a pthread_cond_signal() , a pthread_cond_broadcast(), or when interrupted by delivery of a signal.

Any change in the value of a condition that is associated with the condition variable cannot be inferred by the return of pthread_cond_wait(). Such conditions must be reevaluated.

The pthread_cond_wait() routine always returns with the mutex locked and owned by the calling thread, even when returning an error.

This function blocks until the condition is signaled. The function atomically releases the associated mutex lock before blocking, and atomically acquires the mutex again before returning.

In typical use, a condition expression is evaluated under the protection of a mutex lock. When the condition expression is false, the thread blocks on the condition variable. The condition variable is then signaled by another thread when the thread changes the condition value. The change causes at least one thread that is waiting on the condition variable to unblock and to reacquire the mutex.

The condition that caused the wait must be retested before continuing execution from the point of the pthread_cond_wait(). The condition could change before an awakened thread reacquires the mutes and returns from pthread_cond_wait(). A waiting thread could be awakened spuriously. The recommended test method is to write the condition check as a while() loop that calls pthread_cond_wait().

    pthread_mutex_lock();
        while(condition_is_false)
            pthread_cond_wait();
    pthread_mutex_unlock();

The scheduling policy determines the order in which blocked threads are awakened. The default scheduling policy, SCHED_OTHER, does not specify the order in which threads are awakened. Under the SCHED_FIFO and SCHED_RR real-time scheduling policies, threads are awakened in priority order.


Note –

pthread_cond_wait() is a cancellation point. If a cancel is pending and the calling thread has cancellation enabled, the thread terminates and begins executing its cleanup handlers while continuing to hold the lock.


pthread_cond_wait Return Values

pthread_cond_wait() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by cv or mp is invalid.

Unblocking One Thread

Use pthread_cond_signal(3C) to unblock one thread that is blocked on the condition variable pointed to by cv.

pthread_cond_signal Syntax

int pthread_cond_signal(pthread_cond_t *cv);
#include <pthread.h>

pthread_cond_t cv;
int ret;

/* one condition variable is signaled */
ret = pthread_cond_signal(&cv); 

Modify the associated condition under the protection of the same mutex used with the condition variable being signaled. Otherwise, the condition could be modified between its test and blocking in pthread_cond_wait(), which can cause an infinite wait.

The scheduling policy determines the order in which blocked threads are awakened. The default scheduling policy, SCHED_OTHER, does not specify the order in which threads are awakened. Under the SCHED_FIFO and SCHED_RR real-time scheduling policies, threads are awakened in priority order.

When no threads are blocked on the condition variable, calling pthread_cond_signal() has no effect.


Example 4–8 Using pthread_cond_wait() and pthread_cond_signal()

pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;

decrement_count()
{
    pthread_mutex_lock(&count_lock);
    while (count == 0)
        pthread_cond_wait(&count_nonzero, &count_lock);
    count = count - 1;
    pthread_mutex_unlock(&count_lock);
}

increment_count()
{
    pthread_mutex_lock(&count_lock);
    if (count == 0)
        pthread_cond_signal(&count_nonzero);
    count = count + 1;
    pthread_mutex_unlock(&count_lock);
}

pthread_cond_signal Return Values

pthread_cond_signal() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

cv points to an illegal address.

Example 4–8 shows how to use pthread_cond_wait() and pthread_cond_signal().

Blocking Until a Specified Time

Use pthread_cond_timedwait(3C) as you would use pthread_cond_wait(), except that pthread_cond_timedwait() does not block past the time of day specified by abstime .

pthread_cond_timedwait Syntax

int pthread_cond_timedwait(pthread_cond_t *restrict cv,
          pthread_mutex_t *restrict mp, 
          const struct timespec *restrict abstime);
#include <pthread.h>
#include <time.h>

pthread_cond_t cv;
pthread_mutex_t mp;
timestruct_t abstime;
int ret;

/* wait on condition variable */
ret = pthread_cond_timedwait(&cv, &
mp, &abstime); 

pthread_cond_timedwait() always returns with the mutex locked and owned by the calling thread, even when pthread_cond_timedwait() is returning an error.

The pthread_cond_timedwait() function blocks until the condition is signaled or until the time of day specified by the last argument has passed.


Note –

pthread_cond_timedwait() is also a cancellation point.



Example 4–9 Timed Condition Wait

pthread_timestruc_t to;
pthread_mutex_t m;
pthread_cond_t c;
...
pthread_mutex_lock(&m);
clock_gettime(CLOCK_REALTIME, &to);
to.tv_sec += TIMEOUT;
while (cond == FALSE) {
    err = pthread_cond_timedwait(&c, &m, &to);
    if (err == ETIMEDOUT) {
        /* timeout, do something */
        break;
    }
}
pthread_mutex_unlock(&m);

pthread_cond_timedwait Return Values

pthread_cond_timedwait() returns zero after completing successfully. Any other return value indicates that an error occurred. When either of the following conditions occurs, the function fails and returns the corresponding value.


EINVAL

Description:

cv, mp, or abstime points to an illegal address.


EINVAL

Description:

Different mutexes were supplied for concurrent pthread_cond_timedwait() operations on the same condition variable.


ETIMEDOUT

Description:

The time specified by abstime has passed.


EPERM

Description:

The mutex was not owned by the current thread at the time of the call.

The timeout is specified as a time of day so that the condition can be retested efficiently without recomputing the value, as shown in Example 4–9.

Blocking For a Specified Interval

Use pthread_cond_reltimedwait_np(3C) as you would use pthread_cond_timedwait() with one exception. pthread_cond_reltimedwait_np() takes a relative time interval rather than an absolute future time of day as the value of its last argument.

pthread_cond_reltimedwait_np Syntax

int pthread_cond_reltimedwait_np(pthread_cond_t *cv, 
           pthread_mutex_t *mp, 
          const struct timespec *reltime);
#include <pthread.h>
#include <time.h> 
pthread_cond_t cv; 
pthread_mutex_t mp; 
timestruct_t reltime; 
int ret; 

/* wait on condition variable */ 
ret = pthread_cond_reltimedwait_np(&cv, &mp, &reltime); 

pthread_cond_reltimedwait_np() always returns with the mutex locked and owned by the calling thread, even when pthread_cond_reltimedwait_np() is returning an error. The pthread_cond_reltimedwait_np() function blocks until the condition is signaled or until the time interval specified by the last argument has elapsed.


Note –

pthread_cond_reltimedwait_np() is also a cancellation point.


pthread_cond_reltimedwait_np Return Values

pthread_cond_reltimedwait_np() returns zero after completing successfully. Any other return value indicates that an error occurred. When either of the following conditions occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by reltime is invalid.


ETIMEDOUT

Description:

The time interval specified by reltime has passed.

Unblocking All Threads

Use pthread_cond_broadcast(3C) to unblock all threads that are blocked on the condition variable pointed to by cv, specified by pthread_cond_wait().

pthread_cond_broadcast Syntax

int pthread_cond_broadcast(pthread_cond_t *cv);
#include <pthread.h>

pthread_cond_t cv;
int ret;

/* all condition variables are signaled */
ret = pthread_cond_broadcast(&cv); 

When no threads are blocked on the condition variable, pthread_cond_broadcast() has no effect.

Since pthread_cond_broadcast() causes all threads blocked on the condition to contend again for the mutex lock, use pthread_cond_broadcast() with care. For example, use pthread_cond_broadcast() to allow threads to contend for varying resource amounts when resources are freed, as shown in Example 4–10.


Example 4–10 Condition Variable Broadcast

pthread_mutex_t rsrc_lock;
pthread_cond_t rsrc_add;
unsigned int resources;

get_resources(int amount)
{
    pthread_mutex_lock(&rsrc_lock);
    while (resources < amount) {
        pthread_cond_wait(&rsrc_add, &rsrc_lock);
    }
    resources -= amount;
    pthread_mutex_unlock(&rsrc_lock);
}

add_resources(int amount)
{
    pthread_mutex_lock(&rsrc_lock);
    resources += amount;
    pthread_cond_broadcast(&rsrc_add);
    pthread_mutex_unlock(&rsrc_lock);
}

Note that in add_resources() whether resources are updated first, or if pthread_cond_broadcast() is called first inside the mutex lock does not matter.

Modify the associated condition under the protection of the same mutex that is used with the condition variable being signaled. Otherwise, the condition could be modified between its test and blocking in pthread_cond_wait(), which can cause an infinite wait.

pthread_cond_broadcast Return Values

pthread_cond_broadcast() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

cv points to an illegal address.

Destroying the Condition Variable State

Use pthread_cond_destroy(3C) to destroy any state that is associated with the condition variable pointed to by cv.

pthread_cond_destroy Syntax

int pthread_cond_destroy(pthread_cond_t *cv);
#include <pthread.h>

pthread_cond_t cv;
int ret;

/* Condition variable is destroyed */
ret = pthread_cond_destroy(&cv); 

Note that the space for storing the condition variable is not freed.

pthread_cond_destroy Return Values

pthread_cond_destroy() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by cv is invalid.

Lost Wake-Up Problem

A call to pthread_cond_signal() or pthread_cond_broadcast() when the thread does not hold the mutex lock associated with the condition can lead to lost wake-up bugs.

A lost wake-up occurs when all of the following conditions are in effect:

This can occur only if the condition being tested is modified without holding the mutex lock associated with the condition. As long as the condition being tested is modified only while holding the associated mutex, pthread_cond_signal() and pthread_cond_broadcast() can be called regardless of whether they are holding the associated mutex.

Producer and Consumer Problem

The producer and consumer problem is one of the small collection of standard, well-known problems in concurrent programming. A finite-size buffer and two classes of threads, producers and consumers, put items into the buffer (producers) and take items out of the buffer (consumers).

A producer cannot put something in the buffer until the buffer has space available. A consumer cannot take something out of the buffer until the producer has written to the buffer.

A condition variable represents a queue of threads that wait for some condition to be signaled.

Example 4–11 has two such queues. One (less) queue for producers waits for a slot in the buffer. The other (more) queue for consumers waits for a buffer slot containing information. The example also has a mutex, as the data structure describing the buffer must be accessed by only one thread at a time.


Example 4–11 Producer and Consumer Problem With Condition Variables

typedef struct { 
     char buf[BSIZE]; 
     int occupied; 
     int nextin; 
     int nextout; 
     pthread_mutex_t mutex; 
     pthread_cond_t more; 
     pthread_cond_t less; } 
buffer_t; 

buffer_t buffer;

As Example 4–12 shows, the producer thread acquires the mutex protecting the buffer data structure. The producer thread then makes certain that space is available for the item produced. If space is not available, the producer thread calls pthread_cond_wait() . pthread_cond_wait() causes the producer thread to join the queue of threads that are waiting for the condition less to be signaled. less represents available room in the buffer.

At the same time, as part of the call to pthread_cond_wait(), the thread releases its lock on the mutex. The waiting producer threads depend on consumer threads to signal when the condition is true, as shown in Example 4–12. When the condition is signaled, the first thread waiting on less is awakened. However, before the thread can return from pthread_cond_wait(), the thread must acquire the lock on the mutex again.

Acquire the mutex to ensure that the thread again has mutually exclusive access to the buffer data structure. The thread then must check that available room in the buffer actually exists. If room is available, the thread writes into the next available slot.

At the same time, consumer threads might be waiting for items to appear in the buffer. These threads are waiting on the condition variable more . A producer thread, having just deposited something in the buffer, calls pthread_cond_signal() to wake up the next waiting consumer. If no consumers are waiting, this call has no effect.

Finally, the producer thread unlocks the mutex, allowing other threads to operate on the buffer data structure.


Example 4–12 The Producer and Consumer Problem: the Producer

void producer(buffer_t *b, char item)
{
    pthread_mutex_lock(&b->mutex);
   
    while (b->occupied >= BSIZE)
        pthread_cond_wait(&b->less, &b->mutex);

    assert(b->occupied < BSIZE);

    b->buf[b->nextin++] = item;

    b->nextin %= BSIZE;
    b->occupied++;

    /* now: either b->occupied < BSIZE and b->nextin is the index
       of the next empty slot in the buffer, or
       b->occupied == BSIZE and b->nextin is the index of the
       next (occupied) slot that will be emptied by a consumer
       (such as b->nextin == b->nextout) */

    pthread_cond_signal(&b->more);

    pthread_mutex_unlock(&b->mutex);
}

Note the use of the assert() statement. Unless the code is compiled with NDEBUG defined, assert() does nothing when its argument evaluates to true (nonzero). The program aborts if the argument evaluates to false (zero). Such assertions are especially useful in multithreaded programs. assert() immediately points out runtime problems if the assertion fails. assert() has the additional effect of providing useful comments.

The comment that begins /* now: either b->occupied ... could better be expressed as an assertion, but the statement is too complicated as a Boolean-valued expression and so is given in English.

Both assertions and comments are examples of invariants. These invariants are logical statements that should not be falsified by the execution of the program with the following exception. The exception occurs during brief moments when a thread is modifying some of the program variables mentioned in the invariant. An assertion, of course, should be true whenever any thread executes the statement.

The use of invariants is an extremely useful technique. Even if the invariants are not stated in the program text, think in terms of invariants when you analyze a program.

The invariant in the producer code that is expressed as a comment is always true whenever a thread executes the code where the comment appears. If you move this comment to just after the mutex_unlock(), the comment does not necessarily remain true. If you move this comment to just after the assert() , the comment is still true.

This invariant therefore expresses a property that is true at all times with the following exception. The exception occurs when either a producer or a consumer is changing the state of the buffer. While a thread is operating on the buffer under the protection of a mutex, the thread might temporarily falsify the invariant. However, once the thread is finished, the invariant should be true again.

Example 4–13 shows the code for the consumer. The logic flow is symmetric with the logic flow of the producer.


Example 4–13 The Producer and Consumer Problem: the Consumer

char consumer(buffer_t *b)
{
    char item;
    pthread_mutex_lock(&b->mutex);
    while(b->occupied <= 0)
        pthread_cond_wait(&b->more, &b->mutex);

    assert(b->occupied > 0);

    item = b->buf[b->nextout++];
    b->nextout %= BSIZE;
    b->occupied--;

    /* now: either b->occupied > 0 and b->nextout is the index
       of the next occupied slot in the buffer, or
       b->occupied == 0 and b->nextout is the index of the next
       (empty) slot that will be filled by a producer (such as
       b->nextout == b->nextin) */

    pthread_cond_signal(&b->less);
    pthread_mutex_unlock(&b->mutex);

    return(item);
}

Synchronization With Semaphores

A semaphore is a programming construct designed by E. W. Dijkstra in the late 1960s. Dijkstra's model was the operation of railroads. Consider a stretch of railroad where a single track is present over which only one train at a time is allowed.

A semaphore synchronizes travel on this track. A train must wait before entering the single track until the semaphore is in a state that permits travel. When the train enters the track, the semaphore changes state to prevent other trains from entering the track. A train that is leaving this section of track must again change the state of the semaphore to allow another train to enter.

In the computer version, a semaphore appears to be a simple integer. A thread waits for permission to proceed and then signals that the thread has proceeded by performing a P operation on the semaphore.

The thread must wait until the semaphore's value is positive, then change the semaphore's value by subtracting 1 from the value. When this operation is finished, the thread performs a V operation, which changes the semaphore's value by adding 1 to the value. These operations must take place atomically. These operations cannot be subdivided into pieces between which other actions on the semaphore can take place. In the P operation, the semaphore's value must be positive just before the value is decremented, resulting in a value that is guaranteed to be nonnegative and 1 less than what it was before it was decremented.

In both P and V operations, the arithmetic must take place without interference. The net effect of two V operations performed simultaneously on the same semaphore, should be that the semaphore's new value is 2 greater than it was.

The mnemonic significance of P and V is unclear to most of the world, as Dijkstra is Dutch. However, in the interest of true scholarship: P stands for prolagen, a made-up word derived from proberen te verlagen, which means try to decrease. V stands for verhogen, which means increase. The mnemonic significance is discussed in one of Dijkstra's technical notes, EWD 74.

sem_wait(3RT) and sem_post(3RT) correspond to Dijkstra's P and V operations. sem_trywait(3RT) is a conditional form of the P operation. If the calling thread cannot decrement the value of the semaphore without waiting, the call to returns immediately with a nonzero value.

The two basic sorts of semaphores are binary semaphores and counting semaphores. Binary semaphores never take on values other than zero or one, and counting semaphores take on arbitrary nonnegative values. A binary semaphore is logically just like a mutex.

However, although not always enforced, mutexes should be unlocked only by the thread that holds the lock. Because no notion exists of “the thread that holds the semaphore,” any thread can perform a V or sem_post (3RT) operation.

Counting semaphores are nearly as powerful as conditional variables when used in conjunction with mutexes. In many cases, the code might be simpler when implemented with counting semaphores rather than with condition variables, as shown in Example 4–14, Example 4–15, and Example 4–16.

However, when a mutex is used with condition variables, an implied bracketing is present. The bracketing clearly delineates which part of the program is being protected. This behavior is not necessarily the case for a semaphore, which might be called the go to of concurrent programming. A semaphore is powerful but too easy to use in an unstructured, indeterminate way.

Named and Unnamed Semaphores

POSIX semaphores can be unnamed or named. Unnamed semaphores are allocated in process memory and initialized. Unnamed semaphores might be usable by more than one process, depending on how the semaphore is allocated and initialized. Unnamed semaphores are either private, inherited through fork(), or are protected by access protections of the regular file in which they are allocated and mapped.

Named semaphores are like process-shared semaphores, except that named semaphores are referenced with a pathname rather than a pshared value. Named semaphores are sharable by several processes. Named semaphores have an owner user-id, group-id, and a protection mode.

The functions sem_open, sem_getvalue, sem_close, and sem_unlink are available to open, retrieve, close, and remove named semaphores. By using sem_open, you can create a named semaphore that has a name defined in the file system name space.

For more information about named semaphores, see the sem_open, sem_getvalue, sem_close, and sem_unlink man pages.

Counting Semaphores Overview

Conceptually, a semaphore is a nonnegative integer count. Semaphores are typically used to coordinate access to resources, with the semaphore count initialized to the number of free resources. Threads then atomically increment the count when resources are added and atomically decrement the count when resources are removed.

When the semaphore count becomes zero, no more resources are present. Threads that try to decrement the semaphore when the count is zero block until the count becomes greater than zero.

Table 4–6 Routines for Semaphores

Operation 

Related Function Description 

Initialize a semaphore 

sem_init Syntax

Increment a semaphore 

sem_post Syntax

Block on a semaphore count 

sem_wait Syntax

Decrement a semaphore count 

sem_trywait Syntax

Destroy the semaphore state 

sem_destroy Syntax

Because semaphores need not be acquired and be released by the same thread, semaphores can be used for asynchronous event notification, such as in signal handlers. And, because semaphores contain state, semaphores can be used asynchronously without acquiring a mutex lock as is required by condition variables. However, semaphores are not as efficient as mutex locks.

The scheduling policy determines the order in which blocked threads are awakened. The default scheduling policy, SCHED_OTHER, does not specify the order in which threads are awakened. Under the SCHED_FIFO and SCHED_RR real-time scheduling policies, threads are awakened in priority order.

Semaphores must be initialized before use, however semaphores do not have attributes.

Initializing a Semaphore

Use sem_init(3RT) to initialize the unnamed semaphore variable pointed to by sem to value amount.

sem_init Syntax

int sem_init(sem_t *sem, int pshared, unsigned int value);
#include <semaphore.h>

sem_t sem;
int pshared;
int ret;
int value;

/* initialize a private semaphore */
pshared = 0;
value = 1;
ret = sem_init(&sem, pshared, value); 

If the value of pshared is zero, then the semaphore cannot be shared between processes. If the value of pshared is nonzero, then the semaphore can be shared between processes.

Multiple threads must not initialize the same semaphore.

A semaphore must not be reinitialized while other threads might be using the semaphore.

Initializing Semaphores With Intraprocess Scope

When pshared is 0, the semaphore can be used by all the threads in this process only.

#include <semaphore.h>

sem_t sem;
int ret;
int count = 4;

/* to be used within this process only */
ret = sem_init(&sem, 0, count); 

Initializing Semaphores With Interprocess Scope

When pshared is nonzero, the semaphore can be shared by other processes.

#include <semaphore.h>

sem_t sem;
int ret;
int count = 4;

/* to be shared among processes */
ret = sem_init(&sem, 1, count);

sem_init Return Values

sem_init() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value argument exceeds SEM_VALUE_MAX .


ENOSPC

Description:

A resource that is required to initialize the semaphore has been exhausted. The limit on semaphores SEM_NSEMS_MAX has been reached.


EPERM

Description:

The process lacks the appropriate privileges to initialize the semaphore.

Incrementing a Semaphore

Use sem_post(3RT) to atomically increment the semaphore pointed to by sem.

sem_post Syntax

int sem_post(sem_t *sem);
#include <semaphore.h>

sem_t sem;
int ret;

ret = sem_post(&sem); /* semaphore is posted */

When any threads are blocked on the semaphore, one of the threads is unblocked.

sem_post Return Values

sem_post() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

sem points to an illegal address.

Blocking on a Semaphore Count

Use sem_wait(3RT) to block the calling thread until the semaphore count pointed to by sem becomes greater than zero, then atomically decrement the count.

sem_wait Syntax

int sem_wait(sem_t *sem);
#include <semaphore.h>

sem_t sem;
int ret;

ret = sem_wait(&sem); /* wait for semaphore */

sem_wait Return Values

sem_wait() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occurs, the function fails and returns the corresponding value.


EINVAL

Description:

sem points to an illegal address.


EINTR

Description:

A signal interrupted this function.

Decrementing a Semaphore Count

Use sem_trywait(3RT) to try to atomically decrement the count in the semaphore pointed to by sem when the count is greater than zero.

sem_trywait Syntax

int sem_trywait(sem_t *sem);
#include <semaphore.h>

sem_t sem;
int ret;

ret = sem_trywait(&sem); /* try to wait for semaphore*/

This function is a nonblocking version of sem_wait(). sem_trywait() returns immediately if unsuccessful.

sem_trywait Return Values

sem_trywait() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occurs, the function fails and returns the corresponding value.


EINVAL

Description:

sem points to an illegal address.


EINTR

Description:

A signal interrupted this function.


EAGAIN

Description:

The semaphore was already locked, so the semaphore cannot be immediately locked by the sem_trywait() operation.

Destroying the Semaphore State

Use sem_destroy(3RT) to destroy any state that is associated with the unnamed semaphore pointed to by sem.

sem_destroy Syntax

int sem_destroy(sem_t *sem);
#include <semaphore.h>

sem_t sem;
int ret;

ret = sem_destroy(&sem); /* the semaphore is destroyed */

The space for storing the semaphore is not freed.

sem_destroy Return Values

sem_destroy() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

sem points to an illegal address.

Producer and Consumer Problem Using Semaphores

The data structure in Example 4–14 is similar to the structure used for the condition variables example, shown in Example 4–11. Two semaphores represent the number of full and empty buffers. The semaphores ensure that producers wait until buffers are empty and that consumers wait until buffers are full.


Example 4–14 Producer and Consumer Problem With Semaphores

typedef struct {
    char buf[BSIZE];
    sem_t occupied;
    sem_t empty;
    int nextin;
    int nextout;
    sem_t pmut;
    sem_t cmut;
} buffer_t;

buffer_t buffer;

sem_init(&buffer.occupied, 0, 0);
sem_init(&buffer.empty,0, BSIZE);
sem_init(&buffer.pmut, 0, 1);
sem_init(&buffer.cmut, 0, 1);
buffer.nextin = buffer.nextout = 0;

Another pair of binary semaphores plays the same role as mutexes. The semaphores control access to the buffer when multiple producers use multiple empty buffer slots, and when multiple consumers use multiple full buffer slots. Mutexes would work better here, but would not provide as good an example of semaphore use.


Example 4–15 Producer and Consumer Problem: the Producer

void producer(buffer_t *b, char item) {
    sem_wait(&b->empty);
    sem_wait(&b->pmut);

    b->buf[b->nextin] = item;
    b->nextin++;
    b->nextin %= BSIZE;

    sem_post(&b->pmut);
    sem_post(&b->occupied);
}


Example 4–16 Producer and Consumer Problem: the Consumer

char consumer(buffer_t *b) {
    char item;

    sem_wait(&b->occupied);
   
    sem_wait(&b->cmut);

    item = b->buf[b->nextout];
    b->nextout++;
    b->nextout %= BSIZE;

    sem_post(&b->cmut);

    sem_post(&b->empty);

    return(item);
}

Read-Write Lock Attributes

Read-write locks permit concurrent reads and exclusive writes to a protected shared resource. The read-write lock is a single entity that can be locked in read or write mode. To modify a resource, a thread must first acquire the exclusive write lock. An exclusive write lock is not permitted until all read locks have been released.

Database access can be synchronized with a read-write lock. Read-write locks support concurrent reads of database records because the read operation does not change the record's information. When the database is to be updated, the write operation must acquire an exclusive write lock.

To change the default read-write lock attributes, you can declare and initialize an attribute object. Often, the read-write lock attributes are set up in one place at the beginning of the application. Set up at the beginning of the application makes the attributes easier to locate and modify. The following table lists the functions discussed in this section that manipulate read-write lock attributes.

Table 4–7 Routines for Read-Write Lock Attributes

Operation 

Related Function Description 

Initialize a read-write lock attribute 

pthread_rwlockattr_init Syntax

Destroy a read-write lock attribute 

pthread_rwlockattr_destroy Syntax

Set a read-write lock attribute 

pthread_rwlockattr_setpshared Syntax

Get a read-write lock attribute 

pthread_rwlockattr_getpshared Syntax

Initializing a Read-Write Lock Attribute

pthread_rwlockattr_init(3C) initializes a read-write lock attributes object attr with the default value for all of the attributes defined by the implementation.

pthread_rwlockattr_init Syntax

#include <pthread.h>

int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);

Results are undefined if pthread_rwlockattr_init is called specifying an already initialized read-write lock attributes object. After a read-write lock attributes object initializes one or more read-write locks, any function that affects the object, including destruction, does not affect previously initialized read-write locks.

pthread_rwlockattr_init Return Values

If successful, pthread_rwlockattr_init() returns zero. Otherwise, an error number is returned to indicate the error.


ENOMEM

Description:

Insufficient memory exists to initialize the read-write attributes object.

Destroying a Read-Write Lock Attribute

pthread_rwlockattr_destroy(3C) destroys a read-write lock attributes object.

pthread_rwlockattr_destroy Syntax

#include <pthread.h>

int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);

The effect of subsequent use of the object is undefined until the object is re-initialized by another call to pthread_rwlockattr_init(). An implementation can cause pthread_rwlockattr_destroy() to set the object referenced by attr to an invalid value.

pthread_rwlockattr_destroy Return Values

If successful, pthread_rwlockattr_destroy() returns zero. Otherwise, an error number is returned to indicate the error.


EINVAL

Description:

The value specified by attr is invalid.

Setting a Read-Write Lock Attribute

pthread_rwlockattr_setpshared(3C) sets the process-shared read-write lock attribute.

pthread_rwlockattr_setpshared Syntax

#include <pthread.h>
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);

The pshared lock attribute has one of the following values:


PTHREAD_PROCESS_SHARED

Description:

Permits a read-write lock to be operated on by any thread that has access to the memory where the read-write lock is allocated. Operation on the read-write lock is permitted even if the lock is allocated in memory that is shared by multiple processes.


PTHREAD_PROCESS_PRIVATE

Description:

The read-write lock is only operated upon by threads created within the same process as the thread that initialized the read-write lock. If threads of differing processes attempt to operate on such a read-write lock, the behavior is undefined. The default value of the process-shared attribute is PTHREAD_PROCESS_PRIVATE.

pthread_rwlockattr_setpshared Return Values

If successful, pthread_rwlockattr_setpshared() returns zero. Otherwise, an error number is returned to indicate the error.


EINVAL

Description:

The value specified by attr or pshared is invalid.

Getting a Read-Write Lock Attribute

pthread_rwlockattr_getpshared(3C) gets the process-shared read-write lock attribute.

pthread_rwlockattr_getpshared Syntax

#include <pthread.h> 
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr, 
          int *restrict pshared);

pthread_rwlockattr_getpshared() obtains the value of the process-shared attribute from the initialized attributes object referenced by attr.

pthread_rwlockattr_getpshared Return Values

If successful, pthread_rwlockattr_getpshared() returns zero. Otherwise, an error number is returned to indicate the error.


EINVAL

Description:

The value specified by attr or pshared is invalid.

Using Read-Write Locks

After the attributes for a read-write lock are configured, you initialize the read-write lock. The following functions are used to initialize or destroy, lock or unlock, or try to lock a read-write lock. The following table lists the functions discussed in this section that manipulate read-write locks.

Table 4–8 Routines that Manipulate Read-Write Locks

Operation 

Related Function Description 

Initialize a read-write lock 

pthread_rwlock_init Syntax

Read lock on read-write lock 

pthread_rwlock_rdlock Syntax

Read lock with a nonblocking read-write lock 

pthread_rwlock_tryrdlock Syntax

Write lock on read-write lock 

pthread_rwlock_wrlock Syntax

Write lock with a nonblocking read-write lock 

pthread_rwlock_trywrlock Syntax

Unlock a read-write lock 

pthread_rwlock_unlock Syntax

Destroy a read-write lock 

pthread_rwlock_destroy Syntax

Initializing a Read-Write Lock

Use pthread_rwlock_init(3C) to initialize the read-write lock referenced by rwlock with the attributes referenced by attr.

pthread_rwlock_init Syntax

#include <pthread.h>

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, 
          const pthread_rwlockattr_t *restrict attr);

pthread_rwlock_t  rwlock = PTHREAD_RWLOCK_INITIALIZER;

If attr is NULL, the default read-write lock attributes are used. The effect is the same as passing the address of a default read-write lock attributes object. After the lock is initialized, the lock can be used any number of times without being re-initialized. On successful initialization, the state of the read-write lock becomes initialized and unlocked. Results are undefined if pthread_rwlock_init() is called specifying an already initialized read-write lock. Results are undefined if a read-write lock is used without first being initialized.

In cases where default read-write lock attributes are appropriate, the macro PTHREAD_RWLOCK_INITIALIZER can initialize read-write locks that are statically allocated. The effect is equivalent to dynamic initialization by a call to pthread_rwlock_init() with the parameter attr specified as NULL, except that no error checks are performed.

pthread_rwlock_init Return Values

If successful, pthread_rwlock_init() returns zero. Otherwise, an error number is returned to indicate the error.

If pthread_rwlock_init() fails, rwlock is not initialized and the contents of rwlock are undefined.


EINVAL

Description:

The value specified by attr or rwlock is invalid.

Acquiring the Read Lock on Read-Write Lock

pthread_rwlock_rdlock(3C) applies a read lock to the read-write lock referenced by rwlock.

pthread_rwlock_rdlock Syntax

#include <pthread.h>

int  pthread_rwlock_rdlock(pthread_rwlock_t *rwlock );

The calling thread acquires the read lock if a writer does not hold the lock and no writers are blocked on the lock. Whether the calling thread acquires the lock when a writer does not hold the lock and writers are waiting for the lock is unspecified. If a writer holds the lock, the calling thread does not acquire the read lock. If the read lock is not acquired, the calling thread blocks. The thread does not return from the pthread_rwlock_rdlock() until the thread can acquire the lock. Results are undefined if the calling thread holds a write lock on rwlock at the time the call is made.

Implementations are allowed to favor writers over readers to avoid writer starvation. The Solaris implementation favors writers over readers.

A thread can hold multiple concurrent read locks on rwlock The thread can successfully call pthread_rwlock_rdlock() n times. The thread must call pthread_rwlock_unlock() n times to perform matching unlocks.

Results are undefined if pthread_rwlock_rdlock() is called with an uninitialized read-write lock.

A thread signal handler processes a signal delivered to a thread waiting for a read-write lock. On return from the signal handler, the thread resumes waiting for the read-write lock for reading as if the thread was not interrupted.

pthread_rwlock_rdlock Return Values

If successful, pthread_rwlock_rdlock() returns zero. Otherwise, an error number is returned to indicate the error.


EINVAL

Description:

The value specified by attr or rwlock is invalid.

Acquiring a Read Lock on a Read-Write Lock Before a Specified Absolute Time

The pthread_rwlock_timedrdlock(3C) function applies a read lock to the read-write lock referenced by rwlock as in the pthread_rwlock_rdlock() function.

pthread_rwlock_timedrdlock Syntax

#include <pthread.h>
#include <time.h>

int  pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,
          const struct timespec *restrict abs_timeout);

If the lock cannot be acquired without waiting for other threads to unlock the lock, this wait will be terminated when the specified timeout expires. The timeout expires when the absolute time specified by abs_timeout passes, as measured by the CLOCK_REALTIME clock (that is, when the value of that clock equals or exceeds abs_timeout), or if the absolute time specified by abs_timeout has already been passed at the time of the call.

The resolution of the timeout is the resolution of the CLOCK_REALTIME clock. The timespec data type is defined in the <time.h> header. Under no circumstances does the function fail with a timeout if the lock can be acquired immediately. The validity of the timeout parameter need not be checked if the lock can be immediately acquired.

If a signal that causes a signal handler to be executed is delivered to a thread blocked on a read-write lock with a call to pthread_rwlock_timedrdlock(), upon return from the signal handler the thread resumes waiting for the lock as if it was not interrupted.

The calling thread might deadlock if at the time the call is made it holds a write lock on rwlock.

The pthread_rwlock_reltimedrdlock_np() function is identical to the pthread_rwlock_timedrdlock() function, except that the timeout is specified as a relative time interval.

pthread_rwlock_timedrdlock Return Values

If successful, returns 0 if the lock for writing on the read-write lock object referenced by rwlock is acquired. Otherwise, an error number is returned to indicate the error.


ETIMEDOUT

Description:

The lock could not be acquired before the specified timeout expired.


EAGAIN

Description:

The read lock could not be acquired because the maximum number of read locks for lock would be exceeded.


EDEADLK

Description:

The calling thread already holds the rwlock.


EINVAL

Description:

The value specified by rwlock does not refer to an initialized read-write lock object, or the timeout nanosecond value is less than zero or greater than or equal to 1,000 million.

Acquiring a Non-Blocking Read Lock on a Read-Write Lock

pthread_rwlock_tryrdlock(3C) applies a read lock as in pthread_rwlock_rdlock() with the exception that the function fails if any thread holds a write lock on rwlock or writers are blocked on rwlock.

pthread_rwlock_tryrdlock Syntax

#include <pthread.h>

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

pthread_rwlock_tryrdlock Return Values

pthread_rwlock_tryrdlock() returns zero if the lock for reading on the read-write lock object referenced by rwlock is acquired. If the lock is not acquired, an error number is returned to indicate the error.


EBUSY

Description:

The read-write lock could not be acquired for reading because a writer holds the lock or was blocked on it.

Acquiring the Write Lock on a Read-Write Lock

pthread_rwlock_wrlock(3C) applies a write lock to the read-write lock referenced by rwlock.

pthread_rwlock_wrlock Syntax

#include <pthread.h>

int  pthread_rwlock_wrlock(pthread_rwlock_t *rwlock );

The calling thread acquires the write lock if no other reader thread or writer thread holds the read-write lock rwlock. Otherwise, the thread blocks. The thread does not return from the pthread_rwlock_wrlock() call until the thread can acquire the lock. Results are undefined if the calling thread holds the read-write lock, either a read lock or write lock, at the time the call is made.

Implementations are allowed to favor writers over readers to avoid writer starvation. The Solaris implementation favors writers over readers.

Results are undefined if pthread_rwlock_wrlock() is called with an uninitialized read-write lock.

The thread signal handler processes a signal delivered to a thread waiting for a read-write lock for writing. Upon return from the signal handler, the thread resumes waiting for the read-write lock for writing as if the thread was not interrupted.

pthread_rwlock_wrlock Return Values

pthread_rwlock_rwlock() returns zero if the lock for writing on the read-write lock object referenced by rwlock is acquired. If the lock is not acquired, an error number is returned to indicate the error.

Acquiring a Non-blocking Write Lock on a Read-Write Lock

pthread_rwlock_trywrlock(3C) applies a write lock like pthread_rwlock_wrlock(), with the exception that the function fails if any thread currently holds rwlock, for reading or writing.

pthread_rwlock_trywrlock Syntax

#include <pthread.h>

int pthread_rwlock_trywrlock(pthread_rwlock_t  *rwlock);

Results are undefined if pthread_rwlock_trywrlock() is called with an uninitialized read-write lock.

pthread_rwlock_trywrlock Return Values

If successful, pthread_rwlock_trywrlock() returns zero if the lock for writing on the read-write lock object referenced by rwlock is acquired. Otherwise, an error number is returned to indicate the error.


EBUSY

Description:

The read-write lock could not be acquired for writing because the read-write lock is already locked for reading or writing.

Acquiring a Write Lock on a Read-Write Lock Before a Specified Absolute Time

The pthread_rwlock_timedwrlock(3C) function applies a write lock to the read-write lock referenced by rwlock as in the pthread_rwlock_wrlock() function, but attempts to apply the lock only until a specified absolute time.

pthread_rwlock_timedwrlock Syntax

#include <pthread.h>
#include <time.h>

int  pthread_rwlock_timedwrlock(pthread_rwlock_t   *restrict rwlock, 
     const struct timespec *restrict abs_timeout);

The calling thread acquires the write lock if no other reader thread or writer thread holds the read-write lock rwlock. If the lock cannot be acquired without waiting for other threads to unlock the lock, this wait will be terminated when the specified timeout expires. The timeout expires when the absolute time specified by abs_timeoutpasses, as measured by the CLOCK_REALTIME clock (that is, when the value of that clock equals or exceeds abs_timeout) or if the absolute time specified by abs_timeout has already been passed at the time of the call. The pthread_rwlock_reltimedwrlock_np() function is identical to the pthread_rwlock_timedwrlock() function, except that the timeout is specified as a relative time interval.

pthread_rwlock_timedwrlock Returns

If successful, returns 0 if the lock for writing on the read-write lock object referenced by rwlock is acquired. Otherwise, an error number is returned to indicate the error.


ETIMEDOUT

Description:

The lock could not be acquired before the specified timeout expired.


EDEADLK

Description:

The calling thread already holds the rwlock.


EINVAL

Description:

The value specified by rwlock does not refer to an initialized read-write lock object, or the timeout nanosecond value is less than zero or greater than or equal to 1,000 million.

Unlocking a Read-Write Lock

pthread_rwlock_unlock(3C) releases a lock held on the read-write lock object referenced by rwlock.

pthread_rwlock_unlock Syntax

#include <pthread.h>

int pthread_rwlock_unlock (pthread_rwlock_t  *rwlock);

Results are undefined if the read-write lock rwlock is not held by the calling thread.

If pthread_rwlock_unlock() is called to release a read lock from the read-write lock object, and other read locks are currently held on this lock object, the object remains in the read locked state. If pthread_rwlock_unlock() releases the calling thread's last read lock on this read-write lock object, the calling thread is no longer an owner of the object. If pthread_rwlock_unlock() releases the last read lock for this read-write lock object, the read-write lock object is put in the unlocked state with no owners.

If pthread_rwlock_unlock() is called to release a write lock for this read-write lock object, the lock object is put in the unlocked state with no owners.

If pthread_rwlock_unlock() unlocks the read-write lock object and multiple threads are waiting to acquire the lock object for writing, the scheduling policy determines which thread acquires the object for writing. If multiple threads are waiting to acquire the read-write lock object for reading, the scheduling policy determines the order the waiting threads acquire the object for reading. If multiple threads are blocked on rwlock for both read locks and write locks, whether the readers or the writer acquire the lock first is unspecified.

Results are undefined if pthread_rwlock_unlock() is called with an uninitialized read-write lock.

pthread_rwlock_unlock Return Values

If successful, pthread_rwlock_unlock() returns zero. Otherwise, an error number is returned to indicate the error.

Destroying a Read-Write Lock

pthread_rwlock_destroy(3C) destroys the read-write lock object referenced by rwlock and releases any resources used by the lock.

pthread_rwlock_destroy Syntax

#include <pthread.h>

int pthread_rwlock_destroy(pthread_rwlock_t **rwlock);

The effect of subsequent use of the lock is undefined until the lock is re-initialized by another call to pthread_rwlock_init(). An implementation can cause pthread_rwlock_destroy() to set the object referenced by rwlock to an invalid value. Results are undefined if pthread_rwlock_destroy() is called when any thread holds rwlock. Attempting to destroy an uninitialized read-write lock results in undefined behavior. A destroyed read-write lock object can be re-initialized using pthread_rwlock_init(). The results of otherwise referencing the read-write lock object after the lock object has been destroyed are undefined.

pthread_rwlock_destroy Return Values

If successful, pthread_rwlock_destroy() returns zero. Otherwise, an error number is returned to indicate the error.


EINVAL

Description:

The value specified by attr or rwlock is invalid.

Using Barrier Synchronization

In cases where you must wait for a number of tasks to be completed before an overall task can proceed, barrier synchronization can be used. POSIX threads specifies a synchronization object called a barrier, along with barrier functions. The functions create the barrier, specifying the number of threads that are synchronizing on the barrier, and set up threads to perform tasks and wait at the barrier until all the threads reach the barrier. When the last thread arrives at the barrier, all the threads resume execution.

See Parallelizing a Loop on a Shared-Memory Parallel Computer for more about barrier synchronization.

Initializing a Synchronization Barrier

Use pthread_barrier_init(3C) to allocate resources for a barrier and initialize its attributes.

pthread_barrier_init() Syntax

int pthread_barrier_init(pthread_barrier_t  *barrier, 
          const pthread_barrierattr_t *restrict attr, 
          unsigned count);
#include <pthread.h> 
pthread_barrier_t barrier; 
pthread_barrierattr_t attr;
unsigned count;
int ret; 
ret = pthread_barrier_init(&barrier, &attr, count);

The pthread_barrier_init() function allocates any resources required to use the barrier referenced by barrier and initializes the barrier with attributes referenced by attr. If attr is NULL, the default barrier attributes are used; the effect is the same as passing the address of a default barrier attributes object. The count argument specifies the number of threads that must call pthread_barrier_wait() before any of them successfully return from the call. The value specified by count must be greater than 0.

pthread_barrier_init() Return Values

pthread_barrier_init() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

The value specified by count is equal to 0, or the value specified by attr is invalid


EAGAIN

Description:

The system lacks the necessary resources to initialize another barrier.


ENOMEM

Description:

Insufficient memory exists to initialize the barrier.


EBUSY

Description:

There was an attempt to destroy a barrier while it is in use (for example, while being used in a pthread_barrier_wait() call) by another thread.

Waiting for Threads to Synchronize at a Barrier

Use pthread_barrier_wait(3C) to synchronize threads at a specified barrier. The calling thread blocks until the required number of threads have called pthread_barrier_wait() specifying the barrier. The number of threads is specified in the pthread_barrier_init() function.

When the required number of threads have called pthread_barrier_wait() specifying the barrier, the constant PTHREAD_BARRIER_SERIAL_THREAD is returned to one unspecified thread and 0 is returned to each of the remaining threads. The barrier is then reset to the state it had as a result of the most recent pthread_barrier_init() function that referenced it.

pthread_barrier_wait() Syntax

int pthread_barrier_wait(pthread_barrier_t  *barrier);
#include <pthread.h> 
pthread_barrier_t barrier; 
int ret; 
ret = pthread_barrier_wait(&barrier);

pthread_barrier_wait() Return Values

When pthread_barrier_wait() completes successfully, the function returns PTHREAD_BARRIER_SERIAL_THREAD, which is defined in pthread.h, for one arbitrary thread synchronized at the barrier. The function returns zero for each of the other threads. Otherwise an error code is returned.


EINVAL

Description:

The value specified by barrier does not refer to an initialized barrier object.

Destroying a Synchronization Barrier

When a barrier is no longer needed, it should be destroyed. Use the pthread_barrier_destroy(3C) function to destroy the barrier referenced by barrier and release any resources used by the barrier.

pthread_barrier_destroy Syntax

int pthread_barrier_destroy(pthread_barrier_t *barrier);
#include <pthread.h> 
pthread_barrier_t barrier; 
int ret; 
ret = pthread_barrier_destroy(&barrier);

pthread_barrier_destroy Return Values

pthread_barrier_destroy() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

Indicates that the value of barrier was not valid.


EBUSY

Description:

An attempt was made to destroy a barrier while it is in use (for example, while being used in a pthread_barrier_wait() by another thread.

Initializing a Barrier Attributes Object

The pthread_barrierattr_init(3C) function initializes a barrier attributes object attr with the default values for the attributes defined for the object by the implementation. Currently, only the process-shared attribute is provided, and the pthread_barrierattr_getpshared() and pthread_barrierattr_setpshared() functions are used to get and set the attribute.

After a barrier attributes object has been used to initialize one or more barriers, any function affecting the attributes object (including destruction) does not affect any previously initialized barrier.

pthread_barrierattr_init() Syntax

int pthread_barrierattr_init(pthread_barrierattr_t *attr);
#include <pthread.h> 
pthread_barrierattr_t attr; 
int ret; 
ret = pthread_barrierattr_init(&attr);

pthread_barrierattr_init() Return Values

pthread_barrierattr_init() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


ENOMEM

Description:

Insufficient memory exists to initialize the barrier attributes object.

Setting a Barrier Process-Shared Attribute

The pthread_barrierattr_setpshared() function sets the process-shared attribute in an initialized attributes object referenced by attr. The process-shared attribute can have the following values:

PTHREAD_PROCESS_PRIVATE

The barrier can only be operated upon by threads created within the same process as the thread that initialized the barrier. This is the default value of the process-shared attribute.

PTHREAD_PROCESS_SHARED

The barrier can be operated upon by any thread that has access to the memory where the barrier is allocated.

pthread_barrierattr_setpshared() Syntax

int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared);

pthread_barrierattr_setpshared() Return Values

pthread_barrierattr_setpshared() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

Indicates that the value of attr was not valid, or the new value specified for the pshared is not valid.

Getting a Barrier Process-Shared Attribute

The pthread_barrierattr_getpshared(3C) function obtains the value of the process-shared attribute from the attributes object referenced by attr. The value is set by the pthread_barrierattr_setpshared() function.

pthread_barrierattr_getpshared() Syntax

int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict attr, 
          int *restrict pshared);

pthread_barrierattr_getpshared() Return Values

pthread_barrierattr_getpshared() returns zero after completing successfully, and stores the value of the process-shared attribute of attr into the object referenced by the pshared parameter. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

Indicates that the value of attr was not valid.

Destroying a Barrier Attributes Object

The pthread_barrierattr_destroy() function destroys a barrier attributes object. A destroyed attr attributes object can be reinitialized using pthread_barrierattr_init().

After a barrier attributes object has been used to initialize one or more barriers, destroying the object does not affect any previously initialized barrier.

pthread_barrierattr_destroy() Syntax

#include <pthread.h>

int  pthread_barrierattr_destroy(pthread_barrierattr_t *attr);

pthread_barrierattr_destroy() Return Values

pthread_barrierattr_destroy() returns zero after completing successfully. Any other return value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.


EINVAL

Description:

Indicates that the value of attr was not valid.

Synchronization Across Process Boundaries

Each of the synchronization primitives can be used across process boundaries. The primitives are set up by ensuring that the synchronization variable is located in a shared memory segment and by calling the appropriate init() routine. The primitive must have been initialized with its shared attribute set to interprocess.

Producer and Consumer Problem Example

Example 4–17 shows the producer and consumer problem with the producer and consumer in separate processes. The main routine maps zero-filled memory shared with its child process into its address space.

A child process is created to run the consumer. The parent runs the producer.

This example also shows the drivers for the producer and consumer. The producer_driver() reads characters from stdin and calls producer(). The consumer_driver() gets characters by calling consumer() and writes them to stdout.

The data structure for Example 4–17 is the same as the structure used for the condition variables example, shown in Example 4–4. Two semaphores represent the number of full and empty buffers. The semaphores ensure that producers wait for empty buffers and that consumers wait until the buffers are full.


Example 4–17 Synchronization Across Process Boundaries

main() {
    int zfd;
    buffer_t *buffer;
    pthread_mutexattr_t mattr;
    pthread_condattr_t cvattr_less, cvattr_more;

    zfd = open("/dev/zero", O_RDWR);
    buffer = (buffer_t *)mmap(NULL, sizeof(buffer_t),
        PROT_READ|PROT_WRITE, MAP_SHARED, zfd, 0);
    buffer->occupied = buffer->nextin = buffer->nextout = 0;

    pthread_mutex_attr_init(&mattr);
    pthread_mutexattr_setpshared(&mattr,
        PTHREAD_PROCESS_SHARED);

    pthread_mutex_init(&buffer->lock, &mattr);
    pthread_condattr_init(&cvattr_less);
    pthread_condattr_setpshared(&cvattr_less, PTHREAD_PROCESS_SHARED);
    pthread_cond_init(&buffer->less, &cvattr_less);
    pthread_condattr_init(&cvattr_more);
    pthread_condattr_setpshared(&cvattr_more,   
        PTHREAD_PROCESS_SHARED);
    pthread_cond_init(&buffer->more, &cvattr_more);

    if (fork() == 0)
        consumer_driver(buffer);
    else
        producer_driver(buffer);
}

void producer_driver(buffer_t *b) {
    int item;

    while (1) {
        item = getchar();
        if (item == EOF) {
            producer(b, `\0');
            break;
        } else
            producer(b, (char)item);
    }
}

void consumer_driver(buffer_t *b) {
    char item;

    while (1) {
        if ((item = consumer(b)) == '\0')
            break;
        putchar(item);
    }
}

Comparing Primitives

The most basic synchronization primitive in threads is the mutual exclusion lock. So, mutual exclusion lock is the most efficient mechanism in both memory use and execution time. The basic use of a mutual exclusion lock is to serialize access to a resource.

The next most efficient primitive in threads is the condition variable. The basic use of a condition variable is to block on a change of state. The condition variable provides a thread wait facility. Remember that a mutex lock must be acquired before blocking on a condition variable and must be unlocked after returning from pthread_cond_wait(). The mutex lock must also be held across the change of state that occurs before the corresponding call to pthread_cond_signal().

The semaphore uses more memory than the condition variable. The semaphore is easier to use in some circumstances because a semaphore variable operates on state rather than on control. Unlike a lock, a semaphore does not have an owner. Any thread can increment a semaphore that has blocked.

The read-write lock permits concurrent reads and exclusive writes to a protected resource. The read-write lock is a single entity that can be locked in read or write mode. To modify a resource, a thread must first acquire the exclusive write lock. An exclusive write lock is not permitted until all read locks have been released.