AOSP Expert & Production Engineering
3 min read

Mutex

Mutex Concept and POSIX API

A Mutex (Mutual Exclusion) is the fundamental synchronization primitive used to protect shared data from concurrent access by multiple threads. It ensures that only one thread can execute a "critical section" of code at any given time.

If a thread attempts to acquire a mutex that is already held by another thread, the attempting thread will be blocked (put to sleep by the kernel) until the holding thread releases the mutex.

POSIX API (pthread_mutex_t)

In Android's native C/C++ layer, the underlying implementation is pthread_mutex_t.

#include <pthread.h>

pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
int shared_counter = 0;

void increment_counter() {
    pthread_mutex_lock(&my_mutex);   // Acquire the lock
    
    // Critical Section
    shared_counter++; 
    
    pthread_mutex_unlock(&my_mutex); // Release the lock
}

Recursive Mutex

A standard (fast) mutex will cause a deadlock if a thread attempts to lock it when it already holds it.

A Recursive Mutex solves this. It maintains a reference count. If the thread that already holds the lock attempts to lock it again, the count increments, and the thread is allowed to proceed. The mutex is only fully released (allowing other threads to acquire it) when the thread calls unlock the same number of times it called lock.

Recursive mutexes are useful for complex recursive functions or object-oriented designs where multiple methods of an object acquire the same lock and might call each other.

To create one using pthreads:

pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&my_recursive_mutex, &attr);

Timed Mutex

Sometimes, blocking indefinitely is unacceptable. A timed mutex allows a thread to attempt to acquire a lock, but give up if it cannot acquire it within a specified timeout.

This is implemented using pthread_mutex_timedlock. It returns an error code (like ETIMEDOUT) if the lock isn't acquired in time, allowing the thread to perform fallback logic rather than deadlocking.

std::mutex and std::lock_guard in AOSP

While AOSP relies on pthreads under the hood, modern C++ components in Android heavily utilize the C++11 standard library wrappers: std::mutex and RAII (Resource Acquisition Is Initialization) lock guards.

Using pthread_mutex_lock directly is prone to errors: if an exception is thrown or a function returns early within the critical section, the unlock call might be skipped, causing a permanent deadlock.

std::lock_guard prevents this.

#include <mutex>

std::mutex data_mutex;
std::vector<int> shared_data;

void process_data() {
    // The mutex is locked when the lock_guard is constructed
    std::lock_guard<std::mutex> guard(data_mutex);
    
    // Critical Section
    shared_data.push_back(42);
    
    if (some_condition) {
        return; // Mutex is AUTOMATICALLY unlocked here!
    }
    
    // Mutex is AUTOMATICALLY unlocked when 'guard' goes out of scope here
}

In modern AOSP C++ code, direct calls to .lock() and .unlock() are considered anti-patterns. You should almost always use std::lock_guard or std::unique_lock to guarantee exception safety and clean resource management.