Operating Systems · Module 4 — Synchronisation
The race condition, traced line by line
That single line of code is three separate machine instructions:
Sign in to track your score
The browser is downloading a page. Two of its threads each finish a chunk at almost the same moment, and each adds 1 to a shared counter on the heap.
bytes_loaded = bytes_loaded + 1
The counter starts at 200. Two threads, one each. The answer should be 202.
Run it a thousand times and it will usually say 202. Occasionally it will say 201.
No crash. No error. No warning. Just a wrong number, sometimes.
Why & what
One line is not one step. That single line of code is three separate machine instructions:
- Read bytes_loaded from RAM into a CPU register.
- Add 1 to the register.
- Write the register back to RAM.
The register is private to the thread. The RAM is shared. Between step 1 and step 3, the thread is holding a value that anybody else can change underneath it.
Where the scheduler comes in. Module 3 said the timer interrupt can preempt a thread between any two instructions. Not between any two lines of your code — between any two machine instructions.
So the interrupt can land squarely in the middle of that read-add-write.
The definition. A race condition is when the result of a program depends on the exact order in which threads happen to run.
The word "race" is literal: two threads are racing to write, and the loser's update is thrown away. Why it is so nasty.
- It does not happen every time. The interleaving has to be unlucky.
- It gets rarer under a debugger, because slowing things down changes the timing.
- It produces wrong data, not a crash, so it can survive into production unnoticed.
This is why the topic exists. You cannot test your way out of a race condition. You have to prevent it by design.
What Aisha actually sees. Nothing dramatic. The browser's progress bar reads 201 KB instead of 202 KB and then keeps counting. If the same slip happens a few hundred times during a large download, the bar finishes at 97% and never reaches the end. The page loads perfectly. Only the counter is wrong, and only sometimes.
How it works
The unlucky interleaving, step by step:
- The network thread reads 200 into its register.
- It adds 1, so its register holds 201. RAM still says 200.
- The timer interrupt fires. The network thread is preempted mid-update. Its register value is saved into its PCB — Module 2 — and forgotten about.
- The render thread runs the whole line. It reads 200, adds 1, writes 201. RAM now says 201. Correct so far.
- The network thread resumes with its stale register still holding 201, and writes 201. RAM says 201.
Two increments happened. One survived.

Common confusion
"This only happens on multi-core machines." No. Everything above happened on one core, purely because of the timer interrupt. More cores make races more likely and more varied, but a single core with preemption is quite enough to produce them.
"Making the operation faster fixes it." It shrinks the window; it does not close it. A race that happens once in ten million runs still happens.
"Two threads reading the same variable is a race." It is not. Reading is safe. A race needs at least one writer. Any number of threads may read a value that nobody is changing.
Interview angle
"What is a race condition?" Define it by the dependency: the output depends on the timing of threads rather than on the program's logic. Then give the three-instruction example, because interviewers want to know you understand that one line of code is not atomic.
"Can a race condition happen on a single-core CPU?" Say yes and explain why: preemption can occur between any two machine instructions. Many candidates say no, and it is an easy way to stand out.
- 1.
Why can bytes_loaded = bytes_loaded + 1 be interrupted halfway?
- 2.
Three threads only ever read a shared value; none writes to it. Is this a race condition?