Document revision date: 19 July 1999
[Compaq] [Go to the documentation home page] [How to order documentation] [Help on this site] [How to contact us]
[OpenVMS documentation]

Guide to DECthreads


Previous Contents Index

2.4.1.4 Errorcheck Mutex

An errorcheck mutex is locked exactly once by a thread, like a normal mutex. If a thread tries to lock the mutex again without first unlocking it, the thread receives an error. If a thread other than the owner tries to unlock an errorcheck mutex, an error is returned. Thus, errorcheck mutexes are more informative than normal mutexes because normal mutexes deadlock in such a case, leaving you to determine why the thread no longer executes. Errorcheck mutexes are useful during development and debugging. Errorcheck mutexes can be replaced with normal mutexes when the code is put into production use, or left to provide the additional checking.

Errorcheck mutexes are slower than normal mutexes. They cannot be locked without generating a call into DECthreads, and they do more internal tracking.

2.4.1.5 Mutex Operations

To lock a mutex, use one of the following routines, depending on what you want to happen after the mutex is locked:

When a thread is finished accessing a piece of shared data, it unlocks the associated mutex by calling the pthread_mutex_unlock() routine. If other threads are waiting on the mutex, one is placed in the ready state. If more than one thread is waiting on the mutex, the scheduling policy (see Section 2.3.2.2) and the scheduling priority (see Section 2.3.2.3) determine which thread is readied, and the next running thread that requests it locks the mutex.

The mutex is not automatically granted to the first waiter. If the unlocking thread attempts to relock the mutex before the first waiter gets a chance to run, the unlocking thread will succeed in relocking the mutex, and the first waiter may be forced to reblock.

You can destroy a mutex---that is, reclaim its storage---by calling the pthread_mutex_destroy() routine. Use this routine only after the mutex is no longer needed by any thread. It is invalid to attempt to destroy a mutex while it is locked.

Warning

DECthreads does not currently detect deadlock conditions involving more than one mutex, but may in the future. Never write code that depends upon DECthreads not reporting a particular error condition.

2.4.1.6 Mutex Attributes

A mutex attributes object allows you to specify values other than the defaults for mutex attributes when you initialize a mutex with the pthread_mutex_init() routine.

The mutex type attribute specifies whether a mutex is default, normal, recursive, or errorcheck. Use the pthread_mutexattr_settype() routine to set the mutex type attribute in an initialized mutex attributes object. Use the pthread_mutexattr_gettype() routine to obtain the mutex type from an initialized mutex attributes object.

The pthread_mutexattr_settype() and pthread_mutexattr_gettype() routines replace (and are equivalent to) the pthread_mutexattr_settype_np() and pthread_mutexattr_gettype_np() routines, respectively, that were available in previous DECthreads releases. The new routines provide a standardized interface; however, the older routines remain supported.

If you do not use a mutex attributes object to select a mutex type, calling the pthread_mutex_init() routine initializes a normal (default) mutex by default.

The process-shared attribute specifies whether a mutex can be operated upon by threads in one process or in more than one process, as follows:

2.4.2 Condition Variables

A condition variable is a synchronization object used in conjunction with a mutex. It allows a thread to block its own execution until some shared data object reaches a particular state. A mutex controls access to shared data; a condition variable allows threads to wait for that data to enter a defined state.

The state is defined by a predicate in the form of a Boolean expression. A predicate may be a Boolean variable in the shared data or the predicate may be indirect; testing whether a counter has reached a certain value, or whether a queue is empty.

Each predicate should have its own unique condition variable. Sharing a single condition variable between more than one predicate can introduce inefficiency or errors unless you use extreme care.

Cooperating threads test the predicate and wait on the condition variable while the predicate is not in the desired state. For example, one thread in a program produces work-to-do packets and another thread consumes these packets (does the work). If there are no work-to-do packets when the consumer thread checks, that thread waits on a work-to-do condition variable. When the producer thread produces a packet, it signals the work-to-do condition variable.

You must associate a mutex with a condition variable.

A thread uses a condition variable as follows:

  1. A thread locks a mutex for some shared data and then tests the relevant predicate. If it is not in the proper state, the thread waits on a condition variable associated with the predicate. Waiting on the condition variable automatically unlocks the mutex. It is essential that the mutex be unlocked, because another thread needs to acquire the mutex in order to put the data in the state required by the waiting thread.
  2. When the thread that acquires the mutex puts the data in the appropriate state, it wakes a waiting thread by signaling the condition variable.
  3. One thread comes out of its wait state with the mutex locked (the condition wait relocks the mutex before returning from the thread). Other threads waiting on the condition variable remain blocked.

It is important to wait on the condition variable and evaluate the predicate in a while loop. This ensures that the program checks the predicate after it returns from the condition wait. This is due to the fact that, because threads execute asynchronously, another thread might consume the state before an awakened thread can run. Also, the test protects against spurious wake-ups and provides clearer program documentation.

For example, a thread A may need to wait for a thread B to finish a task X before thread A proceeds to execute a task Y. Thread B can tell thread A that it has finished task X by putting a TRUE or FALSE value in a shared variable (the predicate). When thread A is ready to execute task Y, it looks at the shared variable to see if thread B is finished (see Figure 2-5).

Figure 2-5 Thread A Waits on Condition Ready


First, thread A locks the mutex named mutex_ready that is associated with the shared variable named ready. Then it reads the value in ready. This test is called the predicate. If the predicate indicates that thread B has finished task X, then thread A can unlock the mutex and proceed with task Y. If the predicate indicates that thread B has not yet finished task X, however, then thread A waits for the predicate to change by calling the pthread_cond_wait() routine. This automatically unlocks the mutex, allowing thread B to lock the mutex when it has finished task X. Thread B updates the shared data (predicate) to the state thread A is waiting for and signals the condition variable by calling the pthread_cond_signal() routine (see Figure 2-6).

Figure 2-6 Thread B Signals Condition Ready


Thread B releases its lock on the shared variable's mutex. As a result of the signal, thread A wakes up, implicitly regaining its lock on the condition variable's mutex. It then verifies that the predicate is in the correct state, and proceeds to execute task Y (see Figure 2-7).

Figure 2-7 Thread A Wakes and Proceeds


Note that although the condition variable is used for communication among threads, the communication is anonymous. Thread B does not necessarily know that thread A is waiting on the condition variable that thread B signals, and thread A does not know that it was thread B that awakened it from its wait on the condition variable.

Use the pthread_cond_init() routine to initialize a condition variable. To create condition variables as part of your program's one-time initialization code, see Section 3.8. You can also statically initialize condition variables using the PTHREAD_COND_INITIALIZER macro provided in the pthread.h header file.

Use the pthread_cond_wait() routine to cause a thread to wait until the condition is signaled or broadcasted. This routine specifies a condition variable and a mutex that you have locked. If you have not locked the mutex, the results of pthread_cond_wait() are unpredictable.

The pthread_cond_wait() routine automatically unlocks the mutex and causes the calling thread to wait on the condition variable until another thread calls one of the following routines:

If a thread signals or broadcasts on a condition variable and there are no threads waiting at that time, the signal or broadcast has no effect. The next thread to wait on that condition variable blocks until the next signal or broadcast. (Alternatively, the nonportable pthread_cond_signal_int_np() routine creates a pending wake condition, which causes the next wait on the condition variable to complete immediately.)

If you want to limit the time that a thread waits for a condition to be signaled or broadcasted, use the pthread_cond_timedwait() routine. This routine specifies the condition variable, mutex, and absolute time at which the wait should expire if the condition variable has not been signaled or broadcasted.

You can destroy a condition variable and reclaim its storage by calling the pthread_cond_destroy() routine. Use this routine only after the condition variable is no longer needed by any thread. A condition variable cannot be destroyed while one or more threads are waiting on it.

2.4.3 Condition Variable Attributes

Currently, no attributes affecting condition variables are defined. You cannot change any attributes in the condition variable attributes object.

The pthread_condattr_init() and pthread_condattr_destroy() routines are provided for future expandability of the DECthreads pthread interface and to conform with the POSIX.1c standard. In this DECthreads release these routines offer no useful function, because there are no DECthreads routines available at this time for setting the attributes of condition variable attributes objects.

A condition variable attributes object allows you to specify values other than the defaults for condition variable attributes when you initialize a condition variable with the pthread_cond_init() routine.

The process-shared attribute specifies whether a condition variable can be operated upon by threads in one process or in more than one process, as follows:

2.4.4 Read-Write Locks

A read-write lock is a synchronization object for protecting a data object that can be accessed concurrently by more than one of the program's threads. Unlike a mutex, a read-write lock distinguishes between shared read and exclusive write operations on the shared data object.

Use a read-write lock to protect a shared data object that is read frequently but less frequently modified. For example, when you build a cache of recently accessed information, many threads might simultaneously examine the cache without conflict, but when a thread must update the cache it must have exclusive access.

When a thread locks a read-write lock, it must specify either shared read access or exclusive write access. A thread that wants read access cannot acquire the read-write lock while any thread has already acquired the read-write lock for write access; such a thread will block (wait) on the read-write lock. A thread trying to acquire the read-write lock for write access cannot continue if another thread has already acquired the read-write lock for either write access or read access; such a thread will block (wait) on the read-write lock.

2.4.4.1 Thread Priority and Writer Precedence for Read-Write Locks

If more than one thread is waiting for read access to a read-write lock, when the lock becomes available the thread in that group with the highest priority will acquire the lock for read access.

If more than one thread is waiting for write access to a read-write lock, when the lock becomes available the thread in that group with the highest priority will acquire the lock for write access.

If both reader threads and writer threads are waiting for access to a read-write lock at the time the lock becomes available, one of the writer threads is given precedence based on the thread's priority. That is, if a thread wants to acquire the read-write lock for read access and there is another thread that wants to acquire the lock for write access, then the thread wanting read access will block.

Given DECthreads' assumption that a read-write lock guards a data object that is read often but written rarely, DECthreads' write precedence for read-write locks favors throughput of access by allowing the writer threads to accomplish their work with less chance of blocking. A writer thread owning the lock causes all other threads that want access to block. A reader thread owning the lock causes only writer threads that want access to block.

2.4.4.2 Initializing and Destroying a Read-Write Lock

Use the pthread_rwlock_init() routine to create and initialize a new read-write lock object.

Use the pthread_rwlock_destroy() routine to destroy a previously initialized read-write lock object.

You can statically initialize a read-write lock object using the PTHREAD_RWLOCK_INITIALIZER macro provided in the pthread.h header file.

2.4.4.3 Read-Write Lock Attributes

By default, a new read-write lock object's attributes have default values. To create a new read-write lock object with nondefault attributes, call the pthread_rwlock_init() routine and specify a read-write lock attributes object. Use the pthread_rwlockattr_init() routine to create a new read-write lock attributes object, and use the pthread_rwlockattr_destroy() routine to destroy a read-write lock attributes object.

There is one settable attribute for a read-write lock object, the process-shared attribute. To set and access the value of the process-shared attribute of a read-write lock attributes object, use the pthread_rwlockattr_getpshared() and pthread_rwlockattr_setpshared() routines, respectively.

Note

On OpenVMS Alpha and OpenVMS VAX systems:

The process-shared attribute is not supported by DECthreads on OpenVMS platforms. The pthread_rwlockattr_getpshared() and pthread_rwlockattr_setpshared() routines are not available on OpenVMS platforms.

2.5 Process-Shared Synchronization Objects

You can create synchronization objects (that is, mutexes, condition variables, and read-write locks) that protect a data object that is shared among threads running in different processes. This is called a process-shared synchronization object.

Note

This version of DECthreads supports process-shared synchronization objects for DIGITAL UNIX systems only.

The following DECthreads routines are not supported on OpenVMS Alpha and OpenVMS VAX systems:

  • pthread_condattr_getpshared()
  • pthread_condattr_setpshared()
  • pthread_mutexattr_getpshared()
  • pthread_mutexattr_setpshared()
  • pthread_rwlockattr_getpshared()
  • pthread_rwlockattr_setpshared()

2.5.1 Programming Considerations

On DIGITAL UNIX systems, a process-shared synchronization object is a kernel object. Performing any operation on such an object requires a call into the kernel and thus is of higher cost than the same operation on a process-specific synchronization object.

When debugging a process-shared synchronization object, the debugger cannot examine the object's list of waiting threads because the kernel does not maintain that information in user space.

As is the case for process-specific synchronization objects, a process-shared synchronization object must be initialized only once; you cannot initialize it in each process that uses it. For independent processes that share a common synchronization protocol using process-shared synchronization objects, there must be a non-process-shared mechanism to determine which single process will initialize those objects.

For example, if multiple processes connect to a named memory section, all but one will fail, and the one successful process should have the responsibility of initializing any global process-shared synchronization objects in that memory section. (The other processes must also use some mechanism for waiting until the process-shared object is initialized before attempting to use the shared memory section.)

2.5.2 Process-Shared Mutexes

You can create a mutex that protects a data object that is shared among threads running in different processes. This is called a process-shared mutex.

Create a process-shared mutex by using the pthread_mutexattr_setpshared() routine to set the process-shared attribute in an initialized mutex attributes object, then using that attributes object in a call to pthread_mutex_init().

Note

On OpenVMS Alpha and OpenVMS VAX systems:

The process-shared attribute for mutexes is not supported by DECthreads on OpenVMS platforms. The pthread_mutexattr_getpshared() and pthread_mutexattr_setpshared() routines are not available on OpenVMS platforms.

2.5.3 Process-Shared Condition Variables

You can create a condition variable that protects a data object that is shared among threads running in different processes. This is called a process-shared condition variable.

Create a process-shared condition variable by using the pthread_condattr_setpshared() routine to set the process-shared attribute in an initialized condition variable attributes object, then using that attributes object in a call to pthread_cond_init().

Note

On OpenVMS Alpha and OpenVMS VAX systems:

The process-shared attribute for condition variables is not supported by DECthreads on OpenVMS platforms. The pthread_condattr_getpshared() and pthread_condattr_setpshared() routines are not available on OpenVMS platforms.

2.5.4 Process-Shared Read-Write Locks

You can create a read-write lock that protects a data object that is shared among threads running in different processes. This is called a process-shared read-write lock.

Create a process-shared read-write lock by using the pthread_rwlockattr_setpshared() routine to set the process-shared attribute in an initialized read-write lock attributes object, then using that attributes object in a call to pthread_rwlock_init().

Note

On OpenVMS Alpha and OpenVMS VAX systems:

The process-shared attribute for read-write locks is not supported by DECthreads on OpenVMS platforms. The pthread_rwlockattr_getpshared() and pthread_rwlockattr_setpshared() routines are not available on OpenVMS platforms.

2.6 Thread-Specific Data

Each thread can use an area of memory private to DECthreads where DECthreads stores thread-specific data objects. Use this memory to associate arbitrary data with a thread's context. Consider this as the ability to add user-specified fields to the current thread's context or as global variables that have private values in each thread.

A thread-specific data key is shared by all threads within the process---each thread has its own unique value for that shared key.

Use the following routines to create and access thread-specific data:


Previous Next Contents Index

  [Go to the documentation home page] [How to order documentation] [Help on this site] [How to contact us]  
  privacy and legal statement  
6101PRO_005.HTML