pthread_mutex_init(3)
๐ NAME ๐
pthread_mutex_init, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutex_destroy โ the whole girl group of operations on mutexes. Besties who lock together stay together, fr fr.
โจ SYNOPSIS โจ
#include <pthread.h>
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
๐ธ DESCRIPTION ๐ธ
okay so. A mutex is a MUTual EXclusion device โ that's where the name comes from, see how clever that is โ and it's useful for protecting shared data structures from concurrent modifications, and for implementing critical sections and monitors. It's giving "bouncer at the club door, one person in at a time." ๐
A mutex has two possible states: unlocked (not owned by any thread) and locked (owned by one thread). A mutex can never be owned by two different threads simultaneously, no cap. If a thread tries to lock a mutex that's already locked by another thread, that thread is justโฆ suspended ๐ด until the owning thread unlocks it first. ghosted by the scheduler basically.
pthread_mutex_init() initializes the mutex object pointed to by mutex according to the mutex attributes specified in mutexattr. And if mutexattr is NULL? Default attributes, bestie, no thoughts head empty.
The LinuxThreads implementation supports only one mutex attribute, the mutex kind, which is either "fast", "recursive", or "error checking". The kind of a mutex decides whether it can be locked again by a thread that already owns it. The default kind is "fast". Wanna know more about mutex attributes? Go see pthread_mutexattr_init(3). ๐
Variables of type pthread_mutex_t can also be initialized statically, using the constants PTHREAD_MUTEX_INITIALIZER (for fast mutexes), PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP (for recursive mutexes), and PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP (for error checking mutexes). Static init girlies rise up ๐ซถ
pthread_mutex_lock() locks the given mutex. If the mutex is currently unlocked, it becomes locked and owned by the calling thread, and pthread_mutex_lock() returns immediately โ instant, no waiting. But if the mutex is already locked by another thread, pthread_mutex_lock() suspends the calling thread until the mutex is unlocked. It's called waiting your turn. ๐
Now if the mutex is already locked by the calling thread itself, the behavior of pthread_mutex_lock() depends on the kind of the mutex:
- If the mutex is of the
"fast"kind, the calling thread is suspended until the mutex is unlocked โ which effectively causes the calling thread to deadlock. ๐ Yeah. It's just standing there waiting for itself. Iconic behavior, terrible outcome. - If the mutex is of the
"error checking"kind,pthread_mutex_lock()returns immediately with the error codeEDEADLK. At least she tells you. โจ - If the mutex is of the
"recursive"kind,pthread_mutex_lock()succeeds and returns immediately, recording the number of times the calling thread has locked the mutex. And then an equal number ofpthread_mutex_unlock()operations must be performed before the mutex returns to the unlocked state. Counting, bestie, it's all counting. ๐ข
pthread_mutex_trylock() behaves identically to pthread_mutex_lock(), except that it does not block the calling thread if the mutex is already locked by another thread (or by the calling thread in the case of a "fast" mutex). Instead, pthread_mutex_trylock() returns immediately with the error code EBUSY. She's the "not gonna beg, gonna ask once and move on" queen. ๐
pthread_mutex_unlock() unlocks the given mutex. On entrance to pthread_mutex_unlock(), the mutex is assumed to be locked and owned by the calling thread. If the mutex is of the "fast" kind, pthread_mutex_unlock() always returns it to the unlocked state. If it's of the "recursive" kind, it decrements the locking count of the mutex (that's the number of pthread_mutex_lock() operations performed on it by the calling thread), and only when this count reaches zero is the mutex actually unlocked. Zero means release, periodt.
On "error checking" and "recursive" mutexes, pthread_mutex_unlock() actually checks at run-time that the mutex is locked on entrance, and that it was locked by the same thread that is now calling pthread_mutex_unlock(). If those conditions aren't met, an error code is returned and the mutex remains unchanged. She's checking receipts. ๐งพ
"Fast" mutexes perform no such checks, which means a locked mutex can be unlocked by a thread other than its owner. This is non-portable behavior and must not be relied upon. Do not do this. Do not build on this. Do not text her back.
pthread_mutex_destroy() destroys a mutex object, freeing the resources it might hold. The mutex must be unlocked on entrance. In the LinuxThreads implementation, no resources are associated with mutex objects, so pthread_mutex_destroy() actually does nothing except checking that the mutex is unlocked. The world's most chill destructor. ๐ฆ
๐ CANCELLATION ๐
None of the mutex functions is a cancelation point โ not even pthread_mutex_lock(), in spite of the fact that it can suspend a thread for arbitrary durations. And why is that lowkey a slay? Because this way the status of mutexes at cancelation points is predictable, which allows cancelation handlers to unlock precisely those mutexes that need to be unlocked before the thread stops executing. Precise. Deliberate. Iconic. ๐
Consequently, threads using deferred cancelation should never hold a mutex for extended periods of time. Don't be clingy with the lock, bestie.
๐ ASYNC-SIGNAL SAFETY ๐
The mutex functions are not async-signal safe. What this means is that they should not be called from a signal handler. And in particular? Calling pthread_mutex_lock() or pthread_mutex_unlock() from a signal handler may deadlock the calling thread. Ma'am. Put it down. ๐ซ
โญ RETURN VALUE โญ
pthread_mutex_init() always returns 0 โ she's unproblematic.
The other mutex functions return 0 on success and a non-zero error code on error.
๐ฅ ERRORS ๐ฅ
The pthread_mutex_lock() function returns the following error code on error:
EINVALโ The mutex has not been properly initialized.EDEADLKโ The mutex is already locked by the calling thread ("error checking"mutexes only).
The pthread_mutex_trylock() function returns the following error codes on error:
EBUSYโ The mutex could not be acquired because it was currently locked.EINVALโ The mutex has not been properly initialized.
The pthread_mutex_unlock() function returns the following error code on error:
EINVALโ The mutex has not been properly initialized.EPERMโ The calling thread does not own the mutex ("error checking"mutexes only).
The pthread_mutex_destroy() function returns the following error code on error:
EBUSYโ The mutex is currently locked.
nobody's perfect, but these codes are very good at telling you what went wrong. read them. love them. ๐
๐ฆ SEE ALSO ๐ฆ
pthread_mutexattr_init(3), pthread_mutexattr_setkind_np(3), pthread_cancel(3). go read them, i'll wait ๐ซถ
๐ EXAMPLE ๐
A shared global variable x can be protected by a mutex as follows:
int x;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
All accesses and modifications to x should be bracketed by calls to pthread_mutex_lock() and pthread_mutex_unlock() as follows:
pthread_mutex_lock(&mut);
/* operate on x */
pthread_mutex_unlock(&mut);
see how clean that is?? lock, do the thing, unlock. the three-step routine. the trinity. ๐ โจ
that's the whole page, bestie โ every flag, every error code, every warning, untouched and unbothered, just with better vibes. shoutout to the Linux man-pages 6.20 girlies, dated 2025-05-17 โ you ate and left no crumbs, pthread_mutex_init(3), mwah ๐๐๐ฆ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms