Multithreaded Programming Guide

Using Mutual Exclusion Locks

Table 4-3 lists the functions discussed in this chapter that manipulate mutex locks.

Table 4-3 Routines for Mutual Exclusion Locks

Operation 

Destination Discussion 

 

Initialize a mutex 

"pthread_mutex_init(3THR)"

Make mutex consistent 

"pthread_mutex_consistent_np(3T)"

Lock a mutex 

"pthread_mutex_lock(3THR)"

Unlock a mutex 

"pthread_mutex_unlock(3THR)"

Lock with a nonblocking mutex 

"pthread_mutex_trylock(3THR)"

Destroy a mutex 

"pthread_mutex_destroy(3THR)"

The default scheduling policy, SCHED_OTHER, does not specify the order in which threads can acquire a lock. When multiple threads are waiting for a mutex, the order of acquisition is undefined. When there is contention, the default behavior is to unblock threads in priority order.

Initialize a Mutex

pthread_mutex_init(3THR)

Use pthread_mutex_init(3THR) to initialize the mutex pointed at by mp to its default value (mattr is NULL), or to specify mutex attributes that have already been set with pthread_mutexattr_init(). (For Solaris threads, see "mutex_init(3THR)".)

Prototype:
int	pthread_mutex_init(pthread_mutex_t *mp,
    const pthread_mutexattr_t *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, it is in an unlocked state. The mutex can be in memory shared between processes or in memory private to a process.


Note -

The mutex memory must be cleared to zero before initialization.


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

Statically defined mutexes can be initialized directly to have default attributes with the macro PTHREAD_MUTEX_INITIALIZER.

A mutex lock must not be reinitialized or destroyed while other threads might be using it. Program failure will result 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.

Return Values

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


EBUSY

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


EINVAL

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


EFAULT

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

Make Mutex Consistent

pthread_mutex_consistent_np(3T)

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

Note -

pthread_mutex_consistent_np() applies only if the symbol _POSIX_THREAD_PRIO_INHERIT is defined and for mutexes that are initialized with the protocol attribute value PTHREAD_PRIO_INHERIT.


If the owner of a mutex dies, the mutex can become inconsistent.

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

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

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

Because the critical section protected by the mutex might have been left in an inconsistent state by the dead owner, make the mutex consistent only if you are able to 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 that is not held, is undefined.

Return Values

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

pthread_mutex_consistent_np() fails if:


ENOSYS

The option _POSIX_THREAD_PRIO_INHERIT is not defined or the implementation does not support pthread_mutex_consistent_np().

pthread_mutex_consistent_np() might fail if:


EINVAL

The value specified by mutex is invalid.

Lock a Mutex

pthread_mutex_lock(3THR)

Prototype:
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 */

Use pthread_mutex_lock(3THR) to lock the mutex pointed to by mutex. When pthread_mutex_lock() returns, the mutex is locked and 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. (For Solaris threads, see "mutex_lock(3THR)".)

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 that it has not locked or a mutex that is unlocked, undefined behaviour results.

If the mutex type is PTHREAD_MUTEX_ERRORCHECK, then error checking is provided. If a thread attempts to relock a mutex that it has already locked, an error will be returned. If a thread attempts to unlock a mutex that it has not locked or a mutex that is unlocked, an error will be 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 one. Every time a thread relocks this mutex, the lock count is incremented by one. Each time the thread unlocks the mutex, the lock count is decremented by one. When the lock count reaches zero, the mutex becomes available for other threads to acquire. If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, an error will be returned.

If the mutex type is PTHREAD_MUTEX_DEFAULT, attempting to recursively lock the mutex results in undefined behavior. Attempting to unlock the mutex if it was not locked by the calling thread results in undefined behavior. Attempting to unlock the mutex if it is not locked results in undefined behavior.

Return Values

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


EAGAIN

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


EDEADLK

The current thread already owns the mutex.

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


EOWNERDEAD

The last owner of this mutex died 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() will behave normally.

If the caller is unable to make the state consistent, do not call pthread_mutex_init() for the mutex, but unlock the mutex. 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 dies, the next owner acquires the lock with EOWNERDEAD.


ENOTRECOVERABLE

The mutex you are trying to acquire is protecting state left irrecoverable by the mutex's previous owner that died while holding the lock. The mutex has not been acquired. This condition can occur when the lock was previously acquired with EOWNERDEAD and the owner was unable to cleanup the state and had unlocked the mutex without making the mutex state consistent.


ENOMEM

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

Unlock a Mutex

pthread_mutex_unlock(3THR)

Usepthread_mutex_unlock(3THR) to unlock the mutex pointed to by mutex. (For Solaris threads, see "mutex_unlock(3THR)".)

Prototype:
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 there are threads blocked on the mutex object referenced by mutex when pthread_mutex_unlock() is called, resulting in the mutex becoming available, the scheduling policy is used to determine which thread shall acquire the mutex. (In the case of 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).

Return Values

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


EPERM

The current thread does not own the mutex.

Lock With a Nonblocking Mutex

pthread_mutex_trylock(3THR)

Use pthread_mutex_trylock(3THR) to attempt to lock the mutex pointed to by mutex. (For Solaris threads, see "mutex_trylock(3THR)".)

Prototype:
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.

Return Values

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


EBUSY

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


EAGAIN

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, and the robustness argument of pthread_mutexattr_setrobust_np() is PTHREAD_MUTEX_ROBUST_NP the function fails and returns:


EOWNERDEAD

The last owner of this mutex died 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() will behave normally.

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

If the owner that acquired the lock with EOWNERDEAD dies, the next owner acquires the lock with EOWNERDEAD.


ENOTRECOVERABLE

The mutex you are trying to acquire is protecting state left irrecoverable by the mutex's previous owner that died while holding the lock. The mutex has not been acquired. This condition can occur when the lock was previously acquired with EOWNERDEAD and the owner was unable to cleanup the state and had unlocked the mutex without making the mutex state consistent.


ENOMEM

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

Destroy a Mutex

pthread_mutex_destroy(3THR)

Use pthread_mutex_destroy(3THR) to destroy any state associated with the mutex pointed to by mp. (For Solaris threads, see "mutex_destroy(3THR)".)

Prototype:
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.

Return Values

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


EINVAL

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

Mutex Lock Code Examples

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 simply 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.

Reading an integer value is an atomic operation because integer is the common word size on most machines.

Using Locking Hierarchies

You will occasionally 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. There could be a problem 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, then 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 resources1 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 whenever threads lock multiple mutexes, they do so in the same order. When locks are always taken in a prescribed order, deadlock should not occur. This technique is known as lock hierarchies: order the mutexes by logically assigning numbers to them.

Also, honor the restriction that you cannot take a mutex that is assigned n when you are holding any mutex assigned a number 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 it discovers that deadlock would otherwise be inevitable.


Example 4-3 Conditional Locking

Thread 1 

Thread 2 

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, thread 1 locks mutexes in the prescribed order, but thread 2 takes them out of order. To make certain that there is no deadlock, thread 2 has to take mutex 1 very carefully; if it were to block waiting for the mutex to be released, it is likely to have just entered into a deadlock with thread 1.

To ensure this does not happen, thread 2 calls pthread_mutex_trylock(), which takes the mutex if it is available. If it is not, thread 2 returns immediately, reporting failure. At this point, thread 2 must release mutex 2, so that thread 1 can lock it, and then release both mutex 1 and mutex 2.

Nested Locking With a Singly Linked List

Example 4-4 and Example 4-5 show how to take three locks at once, but prevent deadlock 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 containing a mutex. To remove a node from the list, first search the list starting at ListHead (which itself is never removed) until the desired node is found.

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, there is never a deadlock 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);
}

Nested Locking With a Circular Linked List

Example 4-6 modifies the previous list structure by converting it into a circular list. There is no longer a distinguished head node; now a thread might be associated with a particular node and might perform operations on that node and its neighbor. Note that 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 involving both of them.


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_lock(&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);
}