Posted on

This part goes through a pile of locking patterns and designs, from most favourable and easiest to adjust and hence resulting in a long term maintainable code base, to the least favourable since hardest to ensure it works correctly and stays that way while the code evolves.

Level 0: No Locking

  • Locking Pattern: Immutable State
  • Locking Pattern: Single Owner
  • Locking Pattern: Reference Counting

Level 1: Big Dumb Lock

Level 2: Fine-grained Locking

  • Locking Pattern: Object Tracking Lists
  • Locking Pattern: Interrupt Handler State
  • Locking Pattern: Async Processing
  • Locking Pattern: Weak References
  • Locking Antipattern: Confusing Object Lifetime and Data Consistency

Level 2.5: Splitting Locks for Performance Reasons

We’ve looked at a pile of functional reasons for complicating the locking design, but sometimes you need to add more fine-grained locking for performance reasons. This is already getting dangerous, because it’s very tempting to tune some microbenchmark just because we can, or maybe delude ourselves that it will be needed in the future. Therefore only complicate your locking if:

  • You have actual real world benchmarks with workloads relevant to users that show measurable gains outside of statistical noise.
  • You’ve fully exhausted architectural changes to outright avoid the overhead, like io_uring pre-registering file descriptors locally to avoid manipulating the file descriptor table.
  • You’ve fully exhausted algorithm improvements like batching up operations to amortize locking overhead better.

Only then make your future maintenance pain guaranteed worse by applying more tricky locking than the bare minimum necessary for correctness. Still, go with the simplest approach, often converting a lock to its read-write variant is good enough.

Level 3: Lockless Tricks

  • Locking Antipattern: Using RCU
  • Locking Antipattern: Atomics
  • Locking Antipattern: preempt/local_irq/bh_disable() and Friends …
  • Locking Antipattern: Memory Barriers

Closing Thoughts

Simple, dumb locking is good locking, since with that you have a fighting chance to make it correct locking.

Link

An introduction to lockless algorithms