Frontend Labs
⋯

Part 5 — Concurrency and inter-process communication

Parts 2–4 covered a single process's view of the machine. This part is what happens once multiple processes — or multiple threads inside one — touch the same resource at the same time: race conditions first, then the IPC mechanisms (pipes, signals, shared memory) that let them cooperate on purpose instead of corrupting each other by accident.

Chapter 17 — Race conditions at the OS level

In one sentence

A race condition happens when the correctness of a program depends on the precise TIMING or ORDER of events that the OS does not guarantee — and because that timing can vary run to run, the bug can be invisible in testing and catastrophic in production.

Why this matters

Race conditions are one of the hardest classes of bug to find, because they often don't reproduce reliably — "it worked when I tested it" is exactly what you'd expect from a race condition, not evidence it's fixed.

How it actually works

   THE OS MAKES FEW TIMING GUARANTEES. When two threads (Chapter 7) or
   processes run "at the same time," the scheduler (Chapter 8) decides
   the EXACT interleaving of their instructions, and that interleaving
   can change from run to run -- based on system load, CPU count,
   scheduling decisions that have nothing to do with your program's
   logic.

   THE CLASSIC SHAPE OF A RACE CONDITION -- a "check then act" sequence
   that ISN'T ATOMIC (isn't guaranteed to happen as one uninterruptible
   step):

     Thread A: reads shared_counter (sees 5)
     Thread B: reads shared_counter (ALSO sees 5, before A writes back)
     Thread A: writes shared_counter = 5 + 1 = 6
     Thread B: writes shared_counter = 5 + 1 = 6      <- should be 7!

   Both threads read the SAME starting value because the read-modify-
   write wasn't protected -- one of the two increments is SILENTLY LOST.
   This can happen with shared memory (Chapter 7's threads), with
   files (two processes both checking "does this file exist" then
   both creating it), or with any shared, mutable resource touched from
   more than one thread of control without coordination.

   THIS IS WHY THIS GUIDE KEEPS RETURNING TO "ATOMIC" OPERATIONS
   (Chapter 18's locks, and this same theme appears throughout the
   companion security guide's race-condition chapter, in a web-request
   context) -- the fix is always some mechanism that makes the
   check-and-act sequence happen as ONE step nothing else can interleave
   with.

Real-world example

   The Therac-25, a radiation therapy machine built in the 1980s, is
   the canonical, deeply-studied real-world case for why race conditions
   are taken so seriously in safety-critical engineering. Investigated
   in exhaustive detail by Nancy Leveson and Clark Turner (their 1993
   report remains the standard reference, taught in most software-
   engineering curricula), the machine's control software had a RACE
   CONDITION: if an operator entered treatment parameters QUICKLY enough
   -- specifically, by using keyboard edit commands to correct a mistake
   within about 8 seconds -- the software's checks for a dangerous
   configuration could be BYPASSED, because a shared variable
   (essentially a flag indicating whether safety checks had completed)
   could be read by one part of the program before another part had
   finished updating it. Between 1985 and 1987, this bug (and related
   issues in the same system) contributed to several patients receiving
   massive radiation overdoses, with fatal and severely injurious
   consequences.
   THE LESSON FOR THIS CHAPTER, stated as starkly as the real history
   warrants: the bug was HARD TO REPRODUCE (it depended on precise,
   fast operator timing, which most testers and most routine use never
   triggered) and existed in FIELDED, IN-USE equipment for YEARS before
   it was understood. "It passed testing" is not evidence a race
   condition doesn't exist -- it's evidence the specific interleaving
   that triggers it didn't happen to occur during testing.

Try it yourself

# See a race condition happen live, harmlessly, in a tiny script
cat > race.py << 'EOF'
import threading

counter = 0
def increment():
    global counter
    for _ in range(100000):
        counter += 1   # NOT atomic: read, add 1, write back -- 3 steps

threads = [threading.Thread(target=increment) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()
print(f"expected 400000, got {counter}")
EOF
python3 race.py   # run it a few times -- the result varies, and is
                  # usually LESS than 400000, because increments were lost
   Note: Python's Global Interpreter Lock (GIL) means this specific
   demo's "lost update" rate may be lower/less dramatic than the same
   code in a language without a GIL (like Go, Java, or C) -- but the
   race condition is real in ALL of them; only its VISIBILITY differs.

Common mistakes

  • "It worked every time I tested it, so it's fine." This is exactly what a race condition looks like from the outside — absence of failure in testing is weak evidence, not proof.
  • Assuming a single line of code is "one step" for the CPU/OS. Even counter += 1 is actually several separate machine instructions (read, add, write) — any of them can be interrupted between.
  • Only worrying about race conditions in explicitly multi-threaded code. Two separate PROCESSES racing on a shared file, a shared database row, or a shared external resource is exactly the same class of bug.

Check yourself

  1. What makes a sequence of operations "not atomic," and why does that matter?
  2. Why can a race condition pass testing repeatedly and still be a real bug?
  3. In the Therac-25 case, what made the bug so hard to catch before it caused real harm?

(Answers: Appendix E.)

Further reading

  • Report: Leveson & Turner, "An Investigation of the Therac-25 Accidents" (IEEE Computer, 1993) — the definitive, sobering account; freely findable online.
  • Book (free): OSTEP, "Concurrency: An Introduction" chapter.
  • Guide: this wiki's security guides cover the same underlying idea in a web-application context (check-then-act request races) if you want the application-layer version of this exact lesson.

Generated from real-life-os-guide.md · 8 Oct 2026 16:36