[ Bottom of Page | Previous Page | Next Page | Contents | Index | Library Home | Legal | Search ]

General Programming Concepts:
Writing and Debugging Programs

Using Mutexes

A mutex is a mutual exclusion lock. Only one thread can hold the lock. Mutexes are used to protect data or other resources from concurrent access. A mutex has attributes, which specify the characteristics of the mutex.

Mutex Attributes Object

Like threads, mutexes are created with the help of an attributes object. The mutex attributes object is an abstract object, containing several attributes, depending on the implementation of POSIX options. It is accessed through a variable of type pthread_mutexattr_t. In AIX, the pthread_mutexattr_t data type is a pointer; on other systems, it may be a structure or another data type.

Mutex Attributes Object Creation and Destruction

The mutex attributes object is initialized to default values by the pthread_mutexattr_init subroutine. The attributes are handled by subroutines. The thread attributes object is destroyed by the pthread_mutexattr_destroy subroutine. This subroutine may free storage dynamically allocated by the pthread_mutexattr_init subroutine, depending on the implementation of the threads library.

In the following example, a mutex attributes object is created and initialized with default values, then used and finally destroyed:

pthread_mutexattr_t attributes;
                /* the attributes object is created */
...
if (!pthread_mutexattr_init(&attributes)) {
                /* the attributes object is initialized */
        ...
                /* using the attributes object */
        ...
        pthread_mutexattr_destroy(&attributes);
                /* the attributes object is destroyed */
}

The same attributes object can be used to create several mutexes. It can also be modified between mutex creations. When the mutexes are created, the attributes object can be destroyed without affecting the mutexes created with it.

Mutex Attributes

The following mutex attributes are defined:

Protocol Specifies the protocol used to prevent priority inversions for a mutex. This attribute depends on either the priority inheritance or the priority protection POSIX option (Threads Library Options).

The following mutex attributes are not supported:

Prioceiling Specifies the priority ceiling of a mutex. This attribute depends on the priority protection POSIX option (Threads Library Options).
Process-shared Specifies the process sharing of a mutex. This attribute depends on the process sharing POSIX option (Threads Library Options).

The default values for these attributes are sufficient for most simple cases. See Synchronization Scheduling for more information about the protocol and prioceiling attributes; see Advanced Attributes for more information about the process-shared attribute.

Creating and Destroying Mutexes

A mutex is created by calling the pthread_mutex_init subroutine. You may specify a mutex attributes object. If you specify a NULL pointer, the mutex will have the default attributes. Thus, the code fragment:

pthread_mutex_t mutex;
pthread_mutex_attr_t attr;
...
pthread_mutexattr_init(&attr);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);

is equivalent to:

pthread_mutex_t mutex;
...
pthread_mutex_init(&mutex, NULL);

The ID of the created mutex is returned to the calling thread through the mutex parameter. The mutex ID is an opaque object; its type is pthread_mutex_t. In AIX, the pthread_mutex_t data type is a structure; on other systems, it might be a pointer or another data type.

A mutex must be created once. Calling the pthread_mutex_init subroutine more than once with the same mutex parameter (for example, in two threads concurrently executing the same code) should be avoided. Ensuring the uniqueness of a mutex creation can be done in three ways:

Once the mutex is no longer needed, it should be destroyed by calling the pthread_mutex_destroy subroutine. This subroutine may reclaim any storage allocated by the pthread_mutex_init subroutine. After having destroyed a mutex, the same pthread_mutex_t variable can be reused for creating another mutex. For example, the following code fragment is legal, although not very realistic:

pthread_mutex_t mutex;
...
for (i = 0; i < 10; i++) {
 
        /* creates a mutex */
        pthread_mutex_init(&mutex, NULL);
 
        /* uses the mutex */
 
        /* destroys the mutex */
        pthread_mutex_destroy(&mutex);
}

Like any system resource that can be shared among threads, a mutex allocated on a thread's stack must be destroyed before the thread is terminated. The threads library maintains a linked list of mutexes; thus if the stack where a mutex is allocated is freed, the list will be corrupted.

Types of Mutexes

There are 3 types of mutex:

The type of mutex determines how the mutex behaves when it is operated on.

The default type, PTHREAD_MUTEX_NORMAL or PTHREAD_MUTEX_DEFAULT, results in a deadlock if the same pthread tries to lock it a second time using the pthread_mutex_lock subroutine without first unlocking it.

PTHREAD_MUTEX_ERRORCHECK avoids deadlocks by returning a non-zero value if the same thread attempts to lock the same mutex more than once without first unlocking the mutex.

PTHREAD_MUTEX_RECURSIVE allows the same pthread to recursively lock the mutex using the pthread_mutex_lock subroutine without resulting in a deadlock or getting a non-zero return value from pthread_mutex_lock. The same pthread has to call the pthread_mutex_unlock subroutine the same number of times as it did pthread_mutex_lock subroutine in order to unlock the mutex for other pthreads to use.

When a mutex is first created, it has a default type of PTHREAD_MUTEX_NORMAL. After creating the mutex, the type can be changed using the pthread_mutexattr_settype API library call.

The following is an example of creating and using a recursive mutex type:

pthread_mutex_attr_t    attr;
pthread_mutex_t         mutex;

pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &attr);

struct {
        int a;
        int b;
        int c;
} A;

f()
{
        pthread_mutex_lock(&mutex);
        A.a++;
        g();
        A.c = 0;
        pthread_mutex_unlock(&mutex);
}

g()
{
        pthread_mutex_lock(&mutex);
        A.b += A.a;
        pthread_mutex_unlock(&mutex);
}

Locking and Unlocking Mutexes

A mutex is a simple lock, having two states: locked and unlocked. When it is created, a mutex is unlocked. The pthread_mutex_lock subroutine locks the specified mutex:

The pthread_mutex_trylock subroutine acts like the pthread_mutex_lock subroutine without blocking the calling thread:

The thread that locked a mutex is often called the owner of the mutex.

The pthread_mutex_unlock subroutine resets the specified mutex to the unlocked state if it is owned by the calling mutex:

Because locking does not provide a cancellation point, a thread blocked while waiting for a mutex cannot be canceled. Therefore, it is recommended to use mutexes only for short periods of time, like protecting data from concurrent access. For more information, see Cancellation Points and Canceling a Thread.

Protecting Data with Mutexes

Mutexes are intended to serve either as a low level primitive from which other thread synchronization functions can be built or as a data protection lock. Making Complex Synchronization Objects provides more information about implementing long locks and writer-priority readers/writers locks with mutexes.

Mutex Usage Example

Mutexes can be used to protect data from concurrent access. For example, a database application may create several threads to handle several requests concurrently. The database itself is protected by a mutex, called db_mutex.

/* the initial thread */
pthread_mutex_t mutex;
int i;
...
pthread_mutex_init(&mutex, NULL);    /* creates the mutex      */
for (i = 0; i < num_req; i++)        /* loop to create threads */
        pthread_create(th + i, NULL, rtn, &mutex);
...                                  /* waits end of session   */
pthread_mutex_destroy(&mutex);       /* destroys the mutex     */
...

/* the request handling thread */
...                                  /* waits for a request  */
pthread_mutex_lock(&db_mutex);       /* locks the database   */
...                                  /* handles the request  */
pthread_mutex_unlock(&db_mutex);     /* unlocks the database */
...

The initial thread creates the mutex and all the request handling threads. The mutex is passed to the thread using the parameter of the thread's entry point routine. In a real program, the address of the mutex may be a field of a more complex data structure passed to the created thread.

Avoiding Deadlocks

There are a number of ways that a multi-threaded application can deadlock. Following are some examples.

[ Top of Page | Previous Page | Next Page | Contents | Index | Library Home | Legal | Search ]