JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Multithreaded Programming Guide     Oracle Solaris 11.1 Information Library
search filter icon
search icon

Document Information

Preface

1.  Covering Multithreading Basics

2.  Basic Threads Programming

3.  Thread Attributes

4.  Programming with Synchronization Objects

Mutual Exclusion Lock Attributes

Initializing a Mutex Attribute Object

pthread_mutexattr_init Syntax

pthread_mutexattr_init Return Values

Destroying a Mutex Attribute Object

pthread_mutexattr_destroy Syntax

pthread_mutexattr_destroy Return Values

Setting the Scope of a Mutex

pthread_mutexattr_setpshared Syntax

pthread_mutexattr_setpshared Return Values

Getting the Scope of a Mutex

pthread_mutexattr_getpshared Syntax

pthread_mutexattr_getpshared Return Values

Setting the Mutex Type Attribute

pthread_mutexattr_settype Syntax

pthread_mutexattr_settype Return Values

Getting the Mutex Type Attribute

pthread_mutexattr_gettype Syntax

pthread_mutexattr_gettype Return Values

Setting the Mutex Attribute's Protocol

pthread_mutexattr_setprotocol Syntax

pthread_mutexattr_setprotocol Return Values

Getting the Mutex Attribute's Protocol

pthread_mutexattr_getprotocol Syntax

pthread_mutexattr_getprotocol Return Values

Setting the Mutex Attribute's Priority Ceiling

pthread_mutexattr_setprioceiling Syntax

pthread_mutexattr_setprioceiling Return Values

Getting the Mutex Attribute's Priority Ceiling

pthread_mutexattr_getprioceiling Syntax

pthread_mutexattr_getprioceiling Return Values

Setting the Mutex's Priority Ceiling

pthread_mutex_setprioceiling Syntax

pthread_mutex_setprioceiling Return Values

Getting the Mutex's Priority Ceiling

pthread_mutex_getprioceiling Syntax

pthread_mutex_getprioceiling Return Values

Setting the Mutex's Robust Attribute

pthread_mutexattr_setrobust_np Syntax

pthread_mutexattr_setrobust_np Return Values

Getting the Mutex's Robust Attribute

pthread_mutexattr_getrobust_np Syntax

pthread_mutexattr_getrobust_np Return Values

Using Mutual Exclusion Locks

Initializing a Mutex

pthread_mutex_init Syntax

pthread_mutex_init Return Values

Making a Mutex Consistent

pthread_mutex_consistent_np Syntax

pthread_mutex_consistent_np Return Values

Locking a Mutex

pthread_mutex_lock Syntax

pthread_mutex_lock Return Values

Unlocking a Mutex

pthread_mutex_unlock Syntax

pthread_mutex_unlock Return Values

Locking a Mutex Without Blocking

pthread_mutex_trylock Syntax

pthread_mutex_trylock Return Values

Locking a Mutex Before a Specified Absolute Time

pthread_mutex_timedlock() Syntax

pthread_mutex_timedlock() Return Values

Locking a Mutex Within a Specified Time Interval

pthread_mutex_reltimedlock_np() Syntax

pthread_mutex_reltimedlock_np() Return Values

Destroying a Mutex

pthread_mutex_destroy Syntax

pthread_mutex_destroy Return Values

Code Examples of Mutex Locking

Examples of Using Lock Hierarchies

Examples of Using Nested Locking With a Singly-Linked List

Example of Nested Locking With a Circularly-Linked List

Using Spin Locks

Initializing a Spin Lock

pthread_spin_init() Syntax

pthread_spin_init() Return Values

Acquiring a Spin Lock

pthread_spin_lock() Syntax

pthread_spin_lock() Return Values

Acquiring a Non-Blocking Spin Lock

pthread_spin_trylock() Syntax

pthread_spin_trylock() Return Values

Unlocking a Spin Lock

pthread_spin_unlock() Syntax

pthread_spin_unlock() Return Values

Destroying a Spin Lock

pthread_spin_destroy() Syntax

pthread_spin_destroy() Return Values

Condition Variable Attributes

Initializing a Condition Variable Attribute

pthread_condattr_init Syntax

pthread_condattr_init Return Values

Removing a Condition Variable Attribute

pthread_condattr_destroy Syntax

pthread_condattr_destroy Return Values

Setting the Scope of a Condition Variable

pthread_condattr_setpshared Syntax

pthread_condattr_setpshared Return Values

Getting the Scope of a Condition Variable

pthread_condattr_getpshared Syntax

pthread_condattr_getpshared Return Values

Setting the Clock Selection Condition Variable

pthread_condattr_setclock Syntax

pthread_condattr_setclock Returns

Getting the Clock Selection Condition Variable

pthread_condattr_getclock Syntax

pthread_condattr_getclock Returns

Using Condition Variables

Initializing a Condition Variable

pthread_cond_init Syntax

pthread_cond_init Return Values

Blocking on a Condition Variable

pthread_cond_wait Syntax

pthread_cond_wait Return Values

Unblocking One Thread

pthread_cond_signal Syntax

pthread_cond_signal Return Values

Blocking Until a Specified Time

pthread_cond_timedwait Syntax

pthread_cond_timedwait Return Values

Blocking For a Specified Interval

pthread_cond_reltimedwait_np Syntax

pthread_cond_reltimedwait_np Return Values

Unblocking All Threads

pthread_cond_broadcast Syntax

pthread_cond_broadcast Return Values

Destroying the Condition Variable State

pthread_cond_destroy Syntax

pthread_cond_destroy Return Values

Lost Wake-Up Problem

Producer and Consumer Problem

Synchronization With Semaphores

Named and Unnamed Semaphores

Counting Semaphores Overview

Initializing a Semaphore

sem_init Syntax

Initializing Semaphores With Intraprocess Scope

Initializing Semaphores With Interprocess Scope

sem_init Return Values

Incrementing a Semaphore

sem_post Syntax

sem_post Return Values

Blocking on a Semaphore Count

sem_wait Syntax

sem_wait Return Values

Decrementing a Semaphore Count

sem_trywait Syntax

sem_trywait Return Values

Destroying the Semaphore State

sem_destroy Syntax

sem_destroy Return Values

Producer and Consumer Problem Using Semaphores

Read-Write Lock Attributes

Initializing a Read-Write Lock Attribute

pthread_rwlockattr_init Syntax

pthread_rwlockattr_init Return Values

Destroying a Read-Write Lock Attribute

pthread_rwlockattr_destroy Syntax

pthread_rwlockattr_destroy Return Values

Setting a Read-Write Lock Attribute

pthread_rwlockattr_setpshared Syntax

pthread_rwlockattr_setpshared Return Values

Getting a Read-Write Lock Attribute

pthread_rwlockattr_getpshared Syntax

pthread_rwlockattr_getpshared Return Values

Using Read-Write Locks

Initializing a Read-Write Lock

pthread_rwlock_init Syntax

pthread_rwlock_init Return Values

Acquiring the Read Lock on Read-Write Lock

pthread_rwlock_rdlock Syntax

pthread_rwlock_rdlock Return Values

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

pthread_rwlock_timedrdlock Syntax

pthread_rwlock_timedrdlock Return Values

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

pthread_rwlock_tryrdlock Syntax

pthread_rwlock_tryrdlock Return Values

Acquiring the Write Lock on a Read-Write Lock

pthread_rwlock_wrlock Syntax

pthread_rwlock_wrlock Return Values

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

pthread_rwlock_trywrlock Syntax

pthread_rwlock_trywrlock Return Values

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

pthread_rwlock_timedwrlock Syntax

pthread_rwlock_timedwrlock Returns

Unlocking a Read-Write Lock

pthread_rwlock_unlock Syntax

pthread_rwlock_unlock Return Values

Destroying a Read-Write Lock

pthread_rwlock_destroy Syntax

pthread_rwlock_destroy Return Values

Using Barrier Synchronization

Initializing a Synchronization Barrier

pthread_barrier_init() Syntax

pthread_barrier_init() Return Values

Waiting for Threads to Synchronize at a Barrier

pthread_barrier_wait() Syntax

pthread_barrier_wait() Return Values

Destroying a Synchronization Barrier

pthread_barrier_destroy Syntax

pthread_barrier_destroy Return Values

Initializing a Barrier Attributes Object

pthread_barrierattr_init() Syntax

pthread_barrierattr_init() Return Values

Setting a Barrier Process-Shared Attribute

pthread_barrierattr_setpshared() Syntax

pthread_barrierattr_setpshared() Return Values

Getting a Barrier Process-Shared Attribute

pthread_barrierattr_getpshared() Syntax

pthread_barrierattr_getpshared() Return Values

Destroying a Barrier Attributes Object

pthread_barrierattr_destroy() Syntax

pthread_barrierattr_destroy() Return Values

Synchronization Across Process Boundaries

Producer and Consumer Problem Example

Comparing Primitives

5.  Programming With the Oracle Solaris Software

6.  Programming With Oracle Solaris Threads

7.  Safe and Unsafe Interfaces

8.  Compiling and Debugging

9.  Programming Guidelines

A.  Extended Example: A Thread Pool Implementation

Index

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
Increment a semaphore
Block on a semaphore count
Decrement a semaphore count
Destroy the semaphore state

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 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 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 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 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 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);
}