Skip Navigation Links | |
Exit Print View | |
Multithreaded Programming Guide Oracle Solaris 11 Express 11/10 |
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
Synchronization With Semaphores
Initializing Semaphores With Intraprocess Scope
Initializing Semaphores With Interprocess Scope
Decrementing a Semaphore Count
Destroying the Semaphore State
Producer and Consumer Problem Using Semaphores
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 Solaris Software
6. Programming With Solaris Threads
This section explains how to use condition variables. Table 4-5 lists the functions that are available.
Table 4-5 Condition Variables Functions
|
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().
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() 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.
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.
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() 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.
Use pthread_cond_signal(3C) to unblock one thread that is blocked on the condition variable pointed to by cv.
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() 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().
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 .
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.
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() 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.
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.
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() 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.
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().
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() 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.
Use pthread_cond_destroy(3C) to destroy any state that is associated with the condition variable pointed to by cv.
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() 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.
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:
A thread calls pthread_cond_signal() or pthread_cond_broadcast()
Another thread is between the test of the condition and the call to pthread_cond_wait()
No threads are waiting
The signal has no effect, and therefore is lost
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.
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); }