Linux Synchronization Primitive Selection¶
Purpose¶
This note provides practical guidance for selecting synchronization primitives in Linux.
It focuses on:
- synchronization goals
- execution constraints
- performance characteristics
- common tradeoffs
This note does not explain API details.
Refer to API references for syntax.
Synchronization Goals¶
Synchronization usually solves one or more of the following problems.
| Goal | Description |
|---|---|
| Mutual exclusion | Prevent concurrent modification |
| Atomic update | Guarantee single operation correctness |
| Visibility | Ensure updates become observable |
| Ordering | Control operation ordering |
| Waiting | Suspend until condition becomes true |
| Throughput | Reduce synchronization overhead |
Choosing the correct primitive starts from identifying the actual goal.
Primitive Overview¶
| Primitive | Protect Multiple Fields | Sleep Allowed | IRQ Safe | Ordering | Waiting |
|---|---|---|---|---|---|
| mutex | Yes | Yes | No | Yes | No |
| spinlock | Yes | No | Yes | Yes | No |
| atomic | No | No | Yes | Partial | No |
| READ_ONCE / WRITE_ONCE | No | Yes | Yes | No | No |
| condition variable | No | Yes | No | Through mutex | Yes |
| wait queue | No | Context dependent | Yes | Through wakeup | Yes |
Selection Workflow¶
Need synchronization?
├── No
│ └── No synchronization
│
└── Yes
│
├── Need to wait?
│ └── condition / wait queue
│
├── Multiple fields?
│ └── mutex
│
├── IRQ context?
│ └── spinlock
│
├── Single shared value?
│ └── atomic
│
└── High-performance lock-free?
└── atomic + memory ordering
Primitive Selection¶
Mutex¶
Use when:
- protecting complex state
- operation may sleep
- consistency is more important than latency
Typical examples:
- I2C transaction
- SPI transaction
- cache update
- application state
Characteristics:
Default recommendation.
Spinlock¶
Use when:
- critical section is short
- sleep is forbidden
- interrupt path
Typical examples:
- IRQ event queue
- ring metadata
- scheduler internals
Characteristics:
Avoid long holding time.
Atomic¶
Use when:
- operating on one logical value
- lock overhead dominates
Typical examples:
- counters
- state machine
- ready flag
- reference count
Characteristics:
Avoid using multiple atomics to emulate structure locking.
READ_ONCE / WRITE_ONCE¶
Use when:
- prevent compiler optimization
- publish simple state
Typical examples:
- stop flag
- polling loop
- statistics
Characteristics:
Does NOT replace mutex or atomic ordering.
Memory Ordering¶
Use when:
- synchronization without lock
- visibility matters
Typical examples:
- lock-free queue
- state publish
- internal synchronization
Examples:
Usually hidden inside higher-level primitives.
Practical Examples¶
| Scenario | Recommendation |
|---|---|
| Userspace threads | pthread_mutex |
| Kernel worker thread | mutex |
| IRQ handler | spinlock |
| Shared counter | atomic |
| Ready flag | atomic + release/acquire |
| Event queue | mutex |
| Ring buffer | spinlock |
| Shared memory queue | mutex + condition |
| Statistics | atomic |
| Sensor cache | mutex |
| Pollable driver | wait queue |
| Reference counter | atomic |
Rules of Thumb¶
Rule 1
Rule 2
Rule 3
Rule 4
Rule 5
Common Mistakes¶
Wrong:
Wrong:
Wrong:
Wrong:
Related Notes¶
- atomics-memory-ordering.md
- pthread-condition-variable.md
- futex-foundation.md
Related APIs¶
Userspace:
- pthread_mutex
- pthread_cond
- stdatomic
Kernel:
- mutex
- spinlock
- atomic_t
- READ_ONCE
- wait_queue