30.1.5 / 30.1.6 / 30.1.7 — Dynamic Mutex, Attributes & Types
pthread_mutex_init, destroy, and choosing the right mutex type
30.1.5 — Dynamically Initializing a Mutex
The static initializer PTHREAD_MUTEX_INITIALIZER only works for globally or statically declared mutexes with default attributes. In many real programs, you need to create mutexes at runtime. For this, you use pthread_mutex_init().
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/* Both return 0 on success, or a positive error number on error */
You must use pthread_mutex_init() (instead of the static initializer) in these situations:
- The mutex is allocated on the heap (via
malloc()) — e.g., each node in a linked list has its own mutex. - The mutex is a local variable on the stack (automatic storage).
- You want to set non-default attributes — e.g., a different mutex type (recursive, errorcheck).
Rules for pthread_mutex_destroy()
When a dynamically or automatically allocated mutex is no longer needed, you must destroy it to free its resources:
- Only destroy an unlocked mutex.
- After destroying, no thread should try to lock it again.
- If the mutex is in a dynamically allocated memory block, destroy it before freeing that memory.
- A stack-allocated mutex must be destroyed before the function returns.
- You do NOT need to call
pthread_mutex_destroy()on a mutex initialized withPTHREAD_MUTEX_INITIALIZER. - After destroying, a mutex can be reinitialized with
pthread_mutex_init().
Code Example 1: Heap-Allocated Mutex (Linked List Nodes)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
/* Each node in the linked list has its own mutex for fine-grained locking */
typedef struct Node {
int value;
pthread_mutex_t lock; /* Protects this node's data */
struct Node *next;
} Node;
/* Create a new node with its own initialized mutex */
Node *create_node(int value)
{
Node *node = malloc(sizeof(Node));
if (node == NULL) return NULL;
node->value = value;
node->next = NULL;
/* MUST use pthread_mutex_init() for heap-allocated mutex */
int s = pthread_mutex_init(&node->lock, NULL); /* NULL = default attrs */
if (s != 0) {
free(node);
return NULL;
}
return node;
}
/* Update a node's value safely */
void update_node(Node *node, int new_value)
{
pthread_mutex_lock(&node->lock);
node->value = new_value;
pthread_mutex_unlock(&node->lock);
}
/* Read a node's value safely */
int read_node(Node *node)
{
pthread_mutex_lock(&node->lock);
int v = node->value;
pthread_mutex_unlock(&node->lock);
return v;
}
/* Free a node — destroy mutex BEFORE freeing memory */
void free_node(Node *node)
{
if (node == NULL) return;
/* Step 1: destroy the mutex */
pthread_mutex_destroy(&node->lock);
/* Step 2: free the memory */
free(node);
}
int main(void)
{
Node *n1 = create_node(10);
Node *n2 = create_node(20);
n1->next = n2;
update_node(n1, 42);
printf("n1 value = %d\n", read_node(n1)); /* 42 */
printf("n2 value = %d\n", read_node(n2)); /* 20 */
/* Clean up: destroy and free in order */
free_node(n2);
free_node(n1);
return 0;
}
/*
* Compile: gcc -o heap_mutex heap_mutex.c -lpthread
*/
30.1.6 / 30.1.7 — Mutex Attributes and Types
The attr argument of pthread_mutex_init() allows you to configure the mutex’s behavior. The most important attribute is the mutex type.
POSIX defines four mutex types. The type determines what happens in edge cases like re-locking or unlocking by a non-owner:
| Type | Re-lock by same thread | Unlock by non-owner | Unlock unlocked mutex | Use case |
|---|---|---|---|---|
PTHREAD_MUTEX_NORMAL |
Deadlock (hangs) | Undefined (may succeed on Linux) | Undefined | Default — maximum performance |
PTHREAD_MUTEX_ERRORCHECK |
Returns EDEADLK | Returns error | Returns error | Debugging — catches misuse |
PTHREAD_MUTEX_RECURSIVE |
Increments lock count (allowed) | Returns error | Returns error | Re-entrant code — same thread may lock multiple times |
PTHREAD_MUTEX_DEFAULT |
Undefined | Undefined | Undefined | Default if NULL attrs — same as NORMAL on Linux |
PTHREAD_MUTEX_RECURSIVE — Lock Count
A recursive mutex tracks how many times the same thread has locked it. Each additional lock by the same thread increments a counter. Each unlock decrements it. The mutex is only truly released when the count reaches zero.
This is useful when a function that holds a mutex calls another function that also tries to acquire the same mutex. With a normal mutex this would deadlock; with a recursive mutex it works.
Code Example 2: Setting Mutex Type (Errorcheck)
#include <stdio.h>
#include <pthread.h>
int main(void)
{
pthread_mutex_t mtx;
pthread_mutexattr_t mtxAttr;
int s;
/* Step 1: Initialize the attribute object */
s = pthread_mutexattr_init(&mtxAttr);
if (s != 0) { perror("mutexattr_init"); return 1; }
/* Step 2: Set the type to ERRORCHECK */
s = pthread_mutexattr_settype(&mtxAttr, PTHREAD_MUTEX_ERRORCHECK);
if (s != 0) { perror("mutexattr_settype"); return 1; }
/* Step 3: Initialize mutex with these attributes */
s = pthread_mutex_init(&mtx, &mtxAttr);
if (s != 0) { perror("mutex_init"); return 1; }
/* Step 4: Destroy the attribute object (no longer needed) */
s = pthread_mutexattr_destroy(&mtxAttr);
if (s != 0) { perror("mutexattr_destroy"); return 1; }
/* === Use the mutex === */
pthread_mutex_lock(&mtx);
printf("Locked successfully\n");
/* Try to re-lock — with NORMAL this would deadlock.
* With ERRORCHECK, returns EDEADLK */
int r = pthread_mutex_lock(&mtx);
if (r != 0)
printf("Re-lock returned error %d (EDEADLK=%d) — good!\n",
r, EDEADLK);
pthread_mutex_unlock(&mtx);
printf("Unlocked\n");
/* === Clean up === */
pthread_mutex_destroy(&mtx);
return 0;
}
/*
* Compile: gcc -o errorcheck errorcheck.c -lpthread
* Output:
* Locked successfully
* Re-lock returned error 35 (EDEADLK=35) — good!
* Unlocked
*/
Code Example 3: Recursive Mutex for Re-Entrant Code
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t rec_mtx;
/* This function locks the mutex itself */
void inner_function(void)
{
printf("inner_function: locking mutex\n");
pthread_mutex_lock(&rec_mtx); /* Re-lock by same thread */
printf("inner_function: locked (lock count now 2)\n");
/* Do work */
pthread_mutex_unlock(&rec_mtx); /* Decrement to 1 */
printf("inner_function: unlocked (lock count now 1)\n");
}
/* This function also locks the mutex, then calls inner_function */
void outer_function(void)
{
printf("outer_function: locking mutex\n");
pthread_mutex_lock(&rec_mtx); /* First lock (count = 1) */
printf("outer_function: locked\n");
inner_function(); /* Re-enters the same mutex (count = 2) */
/* After inner returns, count is back to 1 */
pthread_mutex_unlock(&rec_mtx); /* Count = 0, mutex released */
printf("outer_function: unlocked, mutex fully released\n");
}
int main(void)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&rec_mtx, &attr);
pthread_mutexattr_destroy(&attr);
outer_function();
pthread_mutex_destroy(&rec_mtx);
return 0;
}
/*
* Output:
* outer_function: locking mutex
* outer_function: locked
* inner_function: locking mutex
* inner_function: locked (lock count now 2)
* inner_function: unlocked (lock count now 1)
* outer_function: unlocked, mutex fully released
*
* With PTHREAD_MUTEX_NORMAL, inner_function() would deadlock.
*/
