Concurrency: Lock vs Lock-Free
Prefer higher-level concurrency primitives and lock-based designs unless there is strong evidence they are insufficient.
Use this playbook to make explicit, auditable decisions.
Decision Process
- Identify the shared state and required invariants.
- Start from existing high-level components in the target language/runtime.
- Evaluate lock-based designs first.
- Escalate to lock-free atomics only when all lock-based options are proven inadequate for the target throughput/latency/concurrency profile.
- If lock-free is chosen, require explicit handling of memory ordering, lifetime/reclamation, ABA, and interleavings.
Default Recommendation
- Prefer existing concurrency libraries and runtime primitives.
- Prefer mutexes/locks over custom lock-free algorithms.
- Prefer partitioning/sharding, batching, and thread-local buffering before introducing lock-free shared structures.
Lock-Free Escalation Gate
Do not recommend custom lock-free algorithms unless all are true:
- Performance evidence shows lock contention is the bottleneck.
- Simpler alternatives (sharding, batching, lock scope reduction, library data structures) were evaluated and rejected with evidence.
- The team has memory-model expertise for the target language and hardware.
- A correctness plan exists: formal reasoning/tools + stress/fuzz strategy.
- Ownership/lifetime and reclamation strategy is explicit.
Required Risk Checklist (When Atomics Are Involved)
- Memory ordering:
- Specify why each atomic operation uses that ordering.
- Avoid relaxed ordering unless a proof/explanation exists.
- Cross-platform behavior:
- Do not assume x86 behavior represents ARM/POWER behavior.
- Non-atomic sequences:
- Treat multi-step atomic sequences as interruptible.
- Lifetime and reclamation:
- Define safe reclamation (hazard pointers, epochs/RCU-like patterns, etc.).
- ABA exposure:
- Detect and mitigate ABA risks in CAS loops.
- Progress guarantees:
- State lock-free/wait-free/obstruction-free claim precisely.
- Maintainability:
- Keep algorithm and invariants understandable to non-authors.
Performance Guidance
Before recommending lock-free structures, prioritize:
- Data layout and cache locality improvements.
- Lock partitioning (shard by key/hash/core).
- Reduced critical section scope.
- Batched updates and per-thread/local aggregation.
- Existing concurrent containers in mature libraries.
Testing and Verification Guidance
Do not treat unit tests as sufficient for lock-free correctness.
Require:
- Stress testing under heavy concurrency and long runs.
- Race detection tooling where applicable.
- Variation across compilers, optimization levels, and architectures.
- Invariant assertions and linearizability reasoning where feasible.
- Formal/specialized checkers for non-trivial lock-free algorithms.
Response Format
When advising, produce:
- Decision:
Use locks/high-level primitivesorLock-free justified.
- Rationale:
- Evidence-backed tradeoffs, not preferences.
- Safer alternative:
- Concrete lock-based/library-based option first.
- If lock-free:
- Required ordering model, reclamation plan, ABA mitigation, and test plan.
- Validation:
- Benchmarks + correctness checks needed before adoption.
Language-Specific Mapping
For concrete primitive choices by language, use
references/language-mapping.md.
Related Terminology
The following terms are closely related to this skill's scope and intent:
- lockfree
- lock free
- lock-free
- lockless
- compare-and-swap
- CAS loop
- atomic operations
- mutex vs lock-free
- synchronization strategy
- memory ordering
- ABA problem
- linearizability
- hazard pointers
Source Credit
Primary source guidance adapted from Abseil:
https://abseil.io/docs/cpp/atomic_danger