Read/Write Locks, Spinlocks, Critical Sections
This chapter covers read/write locks, spinlocks, and the idea of a critical section — more specialized synchronization tools.
Why This Matters
Mutexes are general but not always optimal. Read/write locks allow concurrent readers; spinlocks avoid context-switch overhead for very short critical sections. Knowing when each is right is a systems-programming skill.
Prerequisites
c.conc.3— mutexes and condition variables.
Core Concept
Read/write lock (rwlock)
Allows multiple readers *or* one writer:
rdlock(&rw); /* many readers concurrently */
wrlock(&rw); /* exclusive writer */
Spinlock
A lock that busy-waits (spins) instead of sleeping. Useful when the critical section is very short and the lock is rarely contended — spinning avoids a context switch.
Critical section
The code region that must not run concurrently with another conflicting region. It is protected by a lock (mutex, rwlock, or spinlock).
Examples
C11 does not standardize rwlocks/spinlocks
These come from POSIX pthreads:
#include <pthread.h>
static pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
void read_path(void)
{
pthread_rwlock_rdlock(&rw);
/* read shared state */
pthread_rwlock_unlock(&rw);
}
void write_path(void)
{
pthread_rwlock_wrlock(&rw);
/* write shared state */
pthread_rwlock_unlock(&rw);
}
POSIX spinlock
#include <pthread.h>
static pthread_spinlock_t sp;
pthread_spin_init(&sp, PTHREAD_PROCESS_PRIVATE);
pthread_spin_lock(&sp);
/* very short critical section */
pthread_spin_unlock(&sp);
pthread_spin_destroy(&sp);
How It Works
A rwlock tracks the number of readers and whether a writer is active; writers block until no readers remain. A spinlock uses an atomic test-and-set in a loop, never yielding the CPU. Critical sections are the regions delimited by lock/unlock.
Variations
Reader-preference vs. writer-preference
Rwlocks may prefer readers (risk writer starvation) or writers. The policy is implementation-defined.
Trylock variants
pthread_rwlock_tryrdlock, pthread_spin_trylock, and mutex trylock attempt to acquire without blocking.
Common Mistakes
- Using a spinlock for a long or I/O-bound critical section (wastes CPU).
- Using a spinlock in user code where the OS may preempt the holder.
- Using a rwlock when reads are short (the extra bookkeeping may not pay off).
- Forgetting to unlock on all paths.
Undefined Behavior
- Unlocking a lock not held.
VERIFIED - Destroying a lock while in use.
VERIFIED
Portability
- Spinlocks and rwlocks are POSIX, not ISO C. C11
<threads.h>has only
mutexes and condition variables.
Under the Hood
A spinlock is a single atomic instruction loop (lock xchg on x86). A rwlock is a mutex-protected counter plus a condition variable for writer waiting.
Practical Usage
- Use rwlocks when reads greatly outnumber writes.
- Use spinlocks only in kernels or very short, non-preemptible critical
sections; prefer mutexes in user code.
- Keep critical sections as short as possible.
Exercises
1. Use a rwlock for a shared cache with many readers and one writer. 2. Benchmark a mutex vs. spinlock for a very short critical section. 3. Implement a reader-preference rwlock using a mutex and condition variable.
Deep Challenge
Implement a writer-preference rwlock and explain how you prevent writer starvation. Then compare its behavior under a heavy-reader workload.
Related Concepts
c.conc.3— mutexes.c.conc.9— deadlock/starvation.c.emb.critical-section— critical sections in embedded.
References
- POSIX threads (pthread_rwlock, pthread_spinlock).
Verification
- rwlock/spinlock semantics are POSIX.
PLATFORM-SPECIFIC - Unlocking unheld lock is UB.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Read/write lock
- [ ] Spinlock
- [ ] Critical section
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.conc.rwlock | 0 | 5 |
| c.conc.spinlock | 0 | 6 |
| c.conc.critical-section | 0 | 6 |