This simulator runs two threads of one process on a single CPU core. Each thread increments the same shared counter using three separate instructions, and the scheduler switches between them after a time slice. You control the slice length and whether a mutex protects the update.
• A 3D scene with the process address-space boundary, two thread contexts with private registers, a single-core scheduler, the shared counter, a mutex and an execution timeline. • An Instructions per time slice slider (1 to 3) and a Hold a mutex across load / increment / store checkbox. • Readouts for the shared counter, completed increments, thread switches, the last running thread, the mutex owner (−1 when unlocked) and the expected final counter. • Restart demonstration and Advance event buttons, and three experiments.
Each increment is decomposed into local ← shared, local ← local + 1 and shared ← local. The two threads share the counter but each has private registers. If the scheduler switches after both threads have loaded zero, each writes back 1 and the final counter is 1 instead of 2 — a lost update. Holding the mutex across the complete read-modify-write serializes the two increments, so the result is always 2. A schedule that happens to avoid the interleaving also gives 2 without the lock, but that does not prove the code is safe: another schedule exposes the bug.
Two threads in one process, one CPU core and sequentially consistent educational memory. Only this six-instruction trace is modeled; there is no general operating system scheduler, multicore cache model or language-specific memory ordering. A data race can exist even on a single core, because interleaving alone can break a non-atomic update. Each event lasts 0.8 animation seconds.
Yes. The scheduler can switch between the load and the store of a non-atomic update, so interleaving alone is enough to lose an increment.
No. Other valid schedules can lose updates. The lucky unlocked schedule experiment ends at 2, yet changing the time slice exposes the bug.
The complete read-modify-write sequence — load, increment and store. Locking only part of it leaves a window for the other thread to interleave.
They share the process address space, including the counter and the mutex, but each thread has its own private registers and program position.