How to Use Abseil C++ `absl::Mutex`: A Complete Guide with Examples
Use absl::Mutex with RAII guards like MutexLock and ReaderMutexLock for exclusive and shared access, and employ Condition with LockWhen or Await for predicate-based waiting.
absl::Mutex is the primary synchronization primitive in the Abseil C++ library (abseil/abseil-cpp). It provides non-recursive exclusive locking, shared reader-writer semantics, and efficient condition-based waiting APIs. This guide covers the essential patterns for thread-safe data access using the implementation found in absl/synchronization/mutex.h.
Declaring and Basic Locking
The absl::Mutex class defined in absl/synchronization/mutex.h provides a non-reentrant lock that must be acquired before accessing shared state.
Static Initialization with ABSL_CONST_INIT
For global or static mutexes, use the ABSL_CONST_INIT macro to ensure constant initialization and avoid dynamic initialization order issues:
#include "absl/synchronization/mutex.h"
ABSL_CONST_INIT absl::Mutex global_mu;
int global_counter ABSL_GUARDED_BY(global_mu) = 0;
Exclusive Locking with MutexLock
Always prefer RAII guards over manual lock() and unlock() calls. The absl::MutexLock class acquires the mutex in its constructor and releases it in its destructor, guaranteeing exception-safe unlocking:
void IncrementCounter() {
absl::MutexLock lock(&global_mu); // Acquires exclusive lock
++global_counter; // Safe mutation
} // Automatic unlock
Direct manual locking is available via mu.lock() and mu.unlock(), but these are discouraged in modern C++ except when implementing custom guard classes.
Reader-Writer Locking Patterns
absl::Mutex supports shared ownership for concurrent reads. According to the source in absl/synchronization/mutex.cc, the implementation optimizes for the common case of uncontended locks while allowing reader parallelism.
Shared Locks with ReaderMutexLock
Use absl::ReaderMutexLock (or mu.lock_shared()) when multiple threads need concurrent read access:
int ReadCounter() {
absl::ReaderMutexLock lock(&global_mu); // Acquires shared lock
return global_counter; // Safe concurrent read
} // Automatic unlock_shared
Exclusive Locks with WriterMutexLock
Use absl::WriterMutexLock (or mu.lock()) for write operations that require exclusive access. This blocks until all readers have exited:
void ResetCounter() {
absl::WriterMutexLock lock(&global_mu); // Acquires exclusive lock
global_counter = 0; // Exclusive write
}
The header absl/base/thread_annotations.h provides macros like ABSL_GUARDED_BY to document which mutex protects which data, enabling static analysis tools.
Condition-Based Waiting
absl::Mutex provides high-level condition variable abstractions that are safer than raw CondVar (also available in absl/synchronization/mutex.h).
Building a Condition Predicate
A Condition is a lightweight callable that evaluates a predicate. It must be stateless and return a boolean. You can construct it from a function pointer, member method, or lambda:
int target_value = 100;
absl::Condition ready([](int* counter, int target) { return *counter >= target; },
&global_counter, target_value);
Blocking with LockWhen and Await
LockWhen atomically waits for the condition to become true and acquires the mutex. This is more efficient than spinning:
void WaitForTarget(int target) {
absl::Condition ready([](int* counter, int target) { return *counter >= target; },
&global_counter, target);
global_mu.LockWhen(ready); // Blocks until predicate true AND lock acquired
// Predicate is guaranteed true here, lock held
ProcessThresholdReached();
global_mu.Unlock();
}
Await is similar but requires you to already hold the mutex. It releases the mutex while waiting and reacquires it upon return:
global_mu.Lock();
while (global_counter < target) {
global_mu.Await(absl::Condition([](int* c, int t) { return *c >= t; },
&global_counter, target));
}
global_mu.Unlock();
Timed Waits with LockWhenWithTimeout
For time-bounded blocking, use LockWhenWithTimeout or AwaitWithTimeout. These return true if the predicate became true, or false if the timeout elapsed first:
bool success = global_mu.LockWhenWithTimeout(
absl::Condition([](int* c) { return *c > 0; }, &global_counter),
absl::Seconds(5));
The timeout implementation uses absl/synchronization/internal/kernel_timeout.h to handle platform-specific deadline management.
Debug Utilities and Annotations
The absl::Mutex API includes built-in debugging features defined in absl/synchronization/mutex.h to detect deadlocks and logic errors.
Thread Safety Annotations
Include absl/base/thread_annotations.h to use annotations that document locking requirements:
int shared_data ABSL_GUARDED_BY(mu);
void UpdateData() ABSL_EXCLUSIVE_LOCK_FUNCTION(mu);
void ReadData() ABSL_SHARED_LOCK_FUNCTION(mu);
Runtime Debugging Checks
Enable diagnostic checks to verify locking assumptions:
mu.AssertHeld(): Verifies the current thread holds an exclusive lock.mu.AssertReaderHeld(): Verifies the current thread holds a shared lock.mu.AssertNotHeld(): Verifies the current thread does not hold the mutex.
For advanced debugging, use EnableInvariantDebugging to register a function that validates invariants on every lock/unlock, and EnableDebugLog to trace all operations:
mu.EnableInvariantDebugging([](void*) { return CheckInvariants(); }, nullptr);
mu.EnableDebugLog("my_mutex");
Summary
- Declare mutexes in
absl/synchronization/mutex.husingABSL_CONST_INITfor static storage. - Lock using RAII guards:
MutexLockfor exclusive access,ReaderMutexLockfor shared read access. - Wait for predicates using
ConditionwithLockWhenorAwaitto avoid busy-waiting. - Timeout operations using
LockWhenWithTimeoutandAwaitWithTimeoutwithabsl::Duration. - Debug with
AssertHeld,EnableInvariantDebugging, and thread annotations fromabsl/base/thread_annotations.h.
Frequently Asked Questions
Is absl::Mutex recursive?
No, absl::Mutex is non-recursive (non-reentrant). Attempting to acquire a lock that the current thread already holds results in undefined behavior or deadlock. Use separate mutexes or restructure your code to avoid recursive locking.
What is the difference between MutexLock and WriterMutexLock?
Both acquire exclusive locks, but absl::MutexLock is the standard RAII guard for exclusive access, while absl::WriterMutexLock explicitly communicates intent in codebases mixing reader and writer locks. Functionally, they are identical; prefer MutexLock for exclusive access unless you need the semantic clarity of "writer" in a reader-writer context.
How does LockWhen differ from a standard condition variable?
LockWhen combines the wait and lock acquisition into a single atomic operation. Unlike traditional condition variables where you wait while holding the mutex, LockWhen waits for the predicate before acquiring the lock, reducing contention. According to absl/synchronization/mutex.h, this is more efficient than the manual while (!pred) wait() loop pattern.
Can I use absl::Mutex with std::lock_guard?
No, absl::Mutex is not compatible with std::lock_guard or other standard C++ mutex wrappers because it does not implement the standard Lockable interface. Always use Abseil's provided RAII classes: absl::MutexLock, absl::ReaderMutexLock, and absl::WriterMutexLock.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →