Skip Navigation Links | |
Exit Print View | |
Multithreaded Programming Guide Oracle Solaris 11.1 Information Library |
1. Covering Multithreading Basics
4. Programming with Synchronization Objects
Mutual Exclusion Lock Attributes
Initializing a Mutex Attribute Object
pthread_mutexattr_init Return Values
Destroying a Mutex Attribute Object
pthread_mutexattr_destroy Syntax
pthread_mutexattr_destroy Return Values
pthread_mutexattr_setpshared Syntax
pthread_mutexattr_setpshared Return Values
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
pthread_mutex_init Return Values
pthread_mutex_consistent_np Syntax
pthread_mutex_consistent_np Return Values
pthread_mutex_lock Return Values
pthread_mutex_unlock Return Values
Locking a Mutex Without Blocking
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
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
pthread_spin_init() Return Values
pthread_spin_lock() Return Values
Acquiring a Non-Blocking Spin Lock
pthread_spin_trylock() Return Values
pthread_spin_unlock() Return Values
pthread_spin_destroy() Return Values
Initializing a Condition Variable Attribute
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
Initializing a Condition Variable
pthread_cond_init Return Values
Blocking on a Condition Variable
pthread_cond_wait Return Values
pthread_cond_signal Return Values
Blocking Until a Specified Time
pthread_cond_timedwait Return Values
Blocking For a Specified Interval
pthread_cond_reltimedwait_np Syntax
pthread_cond_reltimedwait_np Return Values
pthread_cond_broadcast Return Values
Destroying the Condition Variable State
pthread_cond_destroy Return Values
Synchronization With Semaphores
Initializing Semaphores With Intraprocess Scope
Initializing Semaphores With Interprocess Scope
Decrementing a Semaphore Count
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
Initializing a Read-Write Lock
pthread_rwlock_init Return Values
Acquiring the Read Lock on Read-Write Lock
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 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
pthread_rwlock_unlock Return Values
pthread_rwlock_destroy Return Values
Initializing a Synchronization Barrier
pthread_barrier_init() Return Values
Waiting for Threads to Synchronize at a Barrier
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
5. Programming With the Oracle Solaris Software
6. Programming With Oracle Solaris Threads
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.
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.
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
|
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.
Use sem-init to initialize the unnamed semaphore variable pointed to by sem to value amount.
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.
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);
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() 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.
Use sem_post to atomically increment the semaphore pointed to by sem.
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() 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.
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.
int sem_wait(sem_t *sem);
#include <semaphore.h> sem_t sem; int ret; ret = sem_wait(&sem); /* wait for semaphore */
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.
Use sem_trywait to try to atomically decrement the count in the semaphore pointed to by sem when the count is greater than zero.
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() 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.
Use sem_destroy to destroy any state that is associated with the unnamed semaphore pointed to by sem.
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() 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.
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); }