Find the Race

You are the scheduler. Step each actor in whatever order you like, and try to produce an interleaving that breaks the stated invariant. Nothing is revealed until you have tried — because the skill being practised is enumerating schedules in your head, not recognising a race you were shown.

Two Increments, One Counter

Shared State

The invariant you are trying to break

Synchronization exists to preserve invariants. If you cannot name one, you are not protecting anything.

the counter equals the number of increments that have completed

Your goal · Find an ordering in which both threads run all three of their steps, both return successfully, and the counter ends at 1 instead of 2. Then find a second, differently-shaped ordering that loses the same increment — the point is that there is not one narrow window but a whole family of them.

counter
0
increments completed
0
Distinct schedules
20

20 orderings exist for these actors — an exact count, not a simulation. Your test suite exercises one of them, repeatedly, and calls it passing.

Step the actors

Click an actor to run its next indivisible operation. Any order is legal — that is precisely the problem.

No schedule yet

Step an actor above. The trace builds as you go, and the invariant is checked after every step.

Invariant holds so far. 0 of 6 operations scheduled.

The schedules that break it

Revealed after you have tried. Each one is an ordering a real scheduler is free to produce, and one of them is what your users hit.

Try it yourself first

Step at least one actor above. Being handed the failing interleaving before you have hunted for it teaches nothing.

What fixes it, and what the fix costs

Every one of these works. None of them is free — that is why the choice is a design decision rather than a lookup.

Locked until you have driven the scheduler

Fixes only mean something once you have seen what they are fixing.