TICKETS 03 OF 3 RUNS LEFTACC --
OUTCRY

← Guide BookPlay it

Quant Dev Lab

Concurrency Clash

A five-level code review of freshly generated concurrent C++ snippets: flag the exact line that carries the defect, then pick the fix worth shipping.

How it works

The game is a code review, deliberately read-only - nothing here compiles or runs C++, because the interview skill being tested is reading concurrent code and saying precisely what is wrong with it. Each case is generated fresh from a template pool and comes with a short production premise and a numbered code listing. Every case compiles, passes review, and works on a laptop; it fails under load, on the wrong interleaving, or on the wrong architecture.

Each case has two stages. First, find the line: click the line number you believe carries the defect and flag it. The game reveals the true bug line, names the bug, and explains it. Second, pick the fix: from a set of candidate fixes, shuffled so the correct one never sits in a fixed slot, choose the one that addresses the defect without paying for more than it needs - more than one candidate may remove the symptom.

Difficulty runs five levels: data races (and why volatile is not the answer), condition variables (spurious wakeups, check-then-act), memory ordering (release/acquire publishing), deadlock (lock ordering, lost wakeups), and subtle bugs (hidden ordering, ABA, red herrings). Clearing a case - right line and right fix - advances you a level; missing it deals a new case at the same level, never a repeat of the one you just saw. Miss twice in a row above level 1 and the game eases you down one level to rebuild ground. Clearing level 5 ends the review with a summary.

How scoring works

The HUD tracks FOUND (lines correctly flagged) and FIXED (correct fixes chosen) as separate counters, plus retries. A case is passed only when both the line pick and the fix pick are correct.

The summary screen reports the level you reached out of 5, your retry count, and your combined total: found plus fixed, out of two points per case seen.

Line picks feed the site-wide coding-implementation skill and fix picks feed the data-structures skill, so both halves of each case count toward progress tracking independently.

Read for interleavings, not for logic

Every case in this game is correct as a sequence of statements - checking the logic line by line will find nothing, because there is nothing sequentially wrong. The defect only exists when two threads run the code at once. So the productive question for each line is not is this right but what happens if another thread runs between this line and the next, or writes this variable while this line reads it.

Concretely: for every shared variable, find every line that touches it and ask which of those touches happen under the same lock. A read outside the lock that guards the writes is a race even if it looks like an innocent check. For every check followed by an action - if not empty, then pop - ask whether the state can change between the check and the act. Check-then-act gaps are the single most common defect family across the levels.

This is a genuinely separate reading skill from correctness review, and the game's summary says so explicitly. Practicing it means consciously switching modes: on a first pass you may read for what the code intends; the graded pass reads only for what the scheduler is allowed to do to it.

Know each level's signature defects

Level 1 is data races, and its signature trap is volatile: in C++ volatile prevents neither reordering by the hardware nor torn or racing access - it is not a synchronization primitive, and a fix candidate offering it is a decoy. Look for unguarded reads or writes of data that another thread mutates, including compound operations like increment that look atomic and are not.

Level 2 is condition variables: waits whose predicate is checked with if instead of while (spurious wakeups make the recheck mandatory), and check-then-act sequences around the wait. Level 3 is memory ordering: publishing an object through a flag or pointer without release/acquire semantics, so a consumer can see the flag set before the data it guards is visible. Level 4 is deadlock: two locks taken in different orders on different paths, and lost wakeups where a notify fires before the corresponding wait. Level 5 mixes subtle ordering bugs, ABA problems, and deliberate red herrings - lines that look suspicious and are fine.

Keeping this map in your head converts each case from an open search into a short checklist: at level 2, go straight to the wait predicate; at level 4, trace lock acquisition order along both code paths before reading anything else.

Pick the minimal sufficient fix

The fix stage warns you directly: more than one candidate may remove the symptom, and the graded answer is the one that addresses the defect without paying for more than it needs. That rules out two decoy families. Overkill fixes - wrapping everything in one giant lock, making every variable sequentially consistent atomic - do stop the race but buy it with contention or lost performance the defect never required. Symptom-hiding fixes - a sleep, a retry loop, volatile - change the timing without closing the interleaving window.

A practical filter: state to yourself, in one sentence, the exact interleaving that breaks the buggy code (the bug reveal from stage one hands you this). Then test each candidate fix against only that sentence: does it make that interleaving impossible, and does it do anything beyond that? The correct answer is usually the narrowest candidate that passes the first test.

This mirrors real code review economics. In production concurrent code, the too-big fix is a real cost - a hot-path lock widened out of fear is a latency regression - and interviewers probe for candidates who can name why the cheap-and-sufficient fix is sufficient.

A worked example

A representative level 2 case: a worker thread pool where the premise says jobs are occasionally processed twice under load. The listing shows a consumer that locks a mutex, then reads: if (queue.empty()) cv.wait(lock); followed by job = queue.front(); queue.pop();.

Stage one, find the line. Sequentially this is fine, which is the point. Reading for interleavings: cv.wait can return spuriously, and with multiple consumers, another thread can drain the queue between this thread's wakeup and its front() call - the if means the predicate is checked once, before the wait, and never again after it. Flag the if line. The reveal names it: a condition-variable wait guarded by if instead of while.

Stage two, the fix candidates might include: change if to while (queue.empty()) cv.wait(lock); add a sleep after wait; make the queue a lock-free structure; hold a second global lock around the whole function. The sleep changes timing without closing the window. The lock-free rewrite and the global lock both exceed what the defect needs. The while recheck makes the breaking interleaving - waking with an empty queue - harmless, and costs nothing. That is the minimal sufficient fix; both counters tick, and the game advances you to level 3.

Common mistakes

Reviewing for logical correctness. Every case is logically correct in a single thread - if your review finds a logic bug, you have found a red herring.

Reaching for volatile as a concurrency fix. It is a decoy by design: it neither orders memory nor makes access atomic in C++.

Picking the biggest fix to be safe. Overkill candidates remove the symptom and still grade as wrong - the target is sufficient and minimal.

Checking wait predicates with if. Spurious wakeups and competing consumers make the while recheck mandatory; this pattern recurs across levels.

Ignoring the ease-down. Dropping a level after two straight misses is the game rebuilding your footing, not punishment - use the easier case to re-anchor the pattern before climbing back.

Why interviews test this

Quant dev interviews at trading firms lean heavily on read-this-concurrent-code-and-find-the-bug questions, because production trading systems are aggressively multithreaded and a candidate who cannot spot a check-then-act gap on a whiteboard will not spot it in a matching engine. The two-part format here - name the line, then name the right fix - is exactly the interview's shape.

The fix-selection half is the differentiator. Many candidates can point at a race once told one exists; far fewer can argue why the minimal fix is sufficient and why the sledgehammer is a cost. That argument is what separates a senior answer from a memorized one.

Play Concurrency Clash · All game guides · The arcade