AOSP Expert & Production Engineering
3 min read

Semaphore

Semaphore vs Mutex

While a mutex is used for mutual exclusion (only one thread can access a resource), a Semaphore is used for signaling and controlling access to a pool of resources.

Think of a mutex as a bathroom key (only one person can hold it). Think of a semaphore as a bouncer at a club with a strict capacity limit.

Counting Semaphore

A counting semaphore maintains an internal counter.

  • When a thread wants to access a resource, it "waits" (or "decrements") the semaphore. If the counter is greater than zero, it decrements the counter and proceeds. If the counter is zero, the thread blocks until another thread increments it.
  • When a thread finishes with a resource, it "posts" (or "increments") the semaphore, potentially waking up a blocked thread.

This is highly useful for producer-consumer problems, such as a bounded buffer queue where you want to allow up to N items to be processed concurrently.

Binary Semaphore

A binary semaphore is a counting semaphore whose value is restricted to 0 or 1. It is similar to a mutex, but with a crucial difference: a mutex must be unlocked by the same thread that locked it. A binary semaphore can be posted (incremented) by a different thread than the one that waited on it. This makes binary semaphores ideal for signaling between two distinct threads (e.g., Thread A completes a task and signals Thread B to wake up).

POSIX Semaphore API (sem_post and sem_wait)

The standard POSIX C API for semaphores revolves around sem_t.

#include <semaphore.h>

sem_t available_buffers;

// Initialize semaphore: pointer, shared between processes (0 = no), initial value
sem_init(&available_buffers, 0, 5); 

void consumer_thread() {
    // Decrement. Blocks if value is 0.
    sem_wait(&available_buffers); 
    
    // Consume a buffer...
}

void producer_thread() {
    // Produce a buffer...
    
    // Increment. Wakes up a waiting consumer if any.
    sem_post(&available_buffers); 
}

Use Cases in Android: Buffer Counting

A classic use case in the Android multimedia framework (e.g., SurfaceFlinger or camera pipelines) is managing graphics buffers.

When a producer (like an app rendering UI) wants to draw, it needs an empty buffer. If the consumer (SurfaceFlinger) hasn't finished compositing the previous frames, there might be no empty buffers.

A counting semaphore is used where the initial count is the total number of buffers (e.g., 3 for triple-buffering).

  • The producer calls sem_wait() to acquire a buffer.
  • The consumer calls sem_post() when it has finished displaying a buffer and it is ready to be reused.