Review, Exam and Learning Path

This module is your final check: a structured review, worked exam questions of every type, and a plan for what to study next.

Learning Objectives

Review core vocabulary; identify the right concurrency model; predict interleavings; diagnose data races; select synchronization primitives; analyse deadlock graphs; reason about memory ordering; trace promises and async/await; explain event-loop scheduling; compare thread and process pools; design cancellation and timeouts; analyse saturation and backpressure; debug failures; interpret performance graphs; review security; evaluate capstones; plan further study.

Plain-Language Explanation

This module is your final check: a structured review, worked exam questions of every type, and a plan for what to study next.

Precise Technical Explanation

Core vocabulary review Concurrency vs parallelism vs asynchrony; process/thread/task/coroutine; blocking/non-blocking; race condition vs data race; atomicity/visibility/ordering; deadlock/livelock/starvation/priority inversion; future/promise/continuation; backpressure; structured concurrency; linearizability (glossary below).

Identify the right concurrency model Many concurrent network waits async I/O (event loop) or virtual threads/goroutines CPU-bound, native language thread pool / fork-join / parallel algorithms CPU-bound, CPython (GIL build) background workers + main-thread dispatch RTOS tasks with priority analysis Many independent services message passing / queues Predict possible interleavings

List each thread's atomic steps; enumerate merges (or reason about which matter); identify which outcomes are possible (Module 24's explorer).

Diagnose data races Find shared mutable state accessed without happens-before ordering (locks, atomics, joins, channels); confirm with TSan/-race.

Select synchronization primitives Module 4's decision table; prefer higher-level tools (queues, executors, task groups).

Analyze deadlock graphs Draw wait-for edges; find cycles; fix with ordering or combined acquisition (Module 5).

Reason about memory ordering Identify release/acquire (or lock/unlock, join, channel) pairs that order the data accesses; flag relaxed or plain accesses that publish data (Modules 6–7).

Trace promises and async/await Synchronous code first, then microtasks, then tasks (JS); gather result order = argument order; awaits suspend, not block (Modules 9–11).

Explain event-loop scheduling Tasks vs microtasks, Node phases, starvation (Module 11).

Compare thread pools and process pools Shared memory and low overhead vs isolation and (in CPython) CPU parallelism with serialization cost (Modules 15–16).

Design cancellation and timeout handling Tokens/contexts propagated; deadlines not fixed timeouts; cleanup in finally; idempotent retries (Module 14).

Analyze queue saturation and backpressure Little's Law, utilisation, bounded queues, load shedding (Modules 8, 22).

Debug a concurrent failure Reproduce → dump stacks → identify the invariant → fix the root cause → add a regression test (Module 23).

Interpret performance graphs Throughput flattening (saturation), latency knee near high utilisation, throughput dropping with threads (contention/oversubscription), p99 spikes (stalls, GC, fan-out) (Module 22).

Atomic checks for security state, bounded resources, per-request context, no locks around untrusted work (Module 25).

Module 27 rubric.

Final assessment and revision plan The questions below; revisit the modules of any wrong answers; complete one capstone.

Assessment question types Where in this module Multiple choice with explanations pick answers; every option explains why it is right or wrong final assessment quizzes Predict-the-output with interleavings order events from scheduling rules; list all possible outcomes for nondeterministic programs predict program, Module 24 explorer Find-the-race and fix-the-code locate unsynchronised shared state and repair it Deadlock graph analysis find wait-for cycles and break them Happens-before ordering questions decide what a read may return Async task timeline tracing trace tasks, microtasks and awaits API selection scenarios choose the right API for a requirement, e.g. "fan out 3 HTTP calls, fail fast" → asyncio.TaskGroup / Promise.all / errgroup; "CPU-bound in CPython" → ProcessPoolExecutor; "one-shot start signal for N threads" → CountDownLatch / threading.Event model table above spot blocking calls in async code, unbounded queues, swallowed cancellation Module 25 checklist Performance and queueing calculations capacity, utilisation, Little's Law, Amdahl present the design, run the tests and benchmarks, explain limitations Module 27 rubric

Why It Matters in Real Systems

Interviews, design reviews and incident response all test exactly these skills — fast recognition of the model, the hazard and the fix.

Key Terms

See the Glossary at the end of the course.

Syntax and API Overview

This is a review module; see each module's syntax section.

Complete Code Example

Predict the output — a mixed microtask/timer/await program, verified on Node.js 24 (try to predict before reading the output):

Queueing calculation — checked numerically (CPython 3.13.1):

console.log("1");
setTimeout(() => console.log("2"), 0);
(async () => {
  console.log("3");
  await null;
  console.log("4");
  setTimeout(() => console.log("5"), 0);
  queueMicrotask(() => console.log("6"));
})();
Promise.resolve().then(() => console.log("7"));
console.log("8");
1
3
8
4
7
6
2
5
service_ms, workers = 40, 8                      # each request needs 40 ms of a worker
capacity = workers / (service_ms / 1000)
print(f"capacity = {workers} / {service_ms / 1000} s = {capacity:.0f} requests/s")
for rate in (100, 160, 190):
    rho = rate / capacity
    print(f"arrival {rate}/s -> utilisation {rho:.0%}, in service on average L = rate x 40 ms = {rate * service_ms / 1000:.1f} workers busy")
capacity = 8 / 0.04 s = 200 requests/s
arrival 100/s -> utilisation 50%, in service on average L = rate x 40 ms = 4.0 workers busy
arrival 160/s -> utilisation 80%, in service on average L = rate x 40 ms = 6.4 workers busy
arrival 190/s -> utilisation 95%, in service on average L = rate x 40 ms = 7.6 workers busy

Line-by-Line Explanation

Predict: synchronous logs first (1, 3, 8); then microtasks in queue order — the await null continuation (logs 4, queues microtask 6 and timer 5) and the .then (7) — microtask 6 runs before any timer; then timers in order of scheduling (2, then 5).

Calculation: 8 workers × 25 requests/s each = 200/s capacity; Little's Law gives average busy workers = λ × service time.

Interleaving or State Trace

Microtask queue after the script: [continuation(4), then(7)] → run 4 (queues 6, timer 5) → run 7 → run 6 → timers [2, 5].

Correctness and Safety Analysis

Each exam answer should state *why* — which happens-before edge, invariant or queueing assumption justifies it.

Common Mistakes and Misconceptions

The course's recurring ones: confusing concurrency and parallelism; trusting a single run; volatile as synchronization; unbounded queues; blocking event loops; swallowing cancellation; retries without idempotency.

Failure Modes and Debugging Steps

Use Module 23's flow for every debugging question.

Performance and Scalability Implications

Use Module 22's laws for every capacity question.

Security Considerations

Use Module 25's threat-model questions for every design question.

Platform and Runtime Caveats

State the language, runtime and version in every answer that depends on them.

Real-World Applications

Design interviews, architecture reviews, postmortems.

Best Practices

Answer with model → hazard → mechanism → evidence (test or measurement).

Exercises

E1 · Find the race and fix the code. if cache.get(k) is None: cache[k] = expensive(k) called from many threads.

Answer: Check-then-act: several threads may compute and overwrite. Fix with a lock around the check-and-insert plus single-flight futures so expensive runs once per key (Module 18).

E2 · Deadlock graph analysis. T1 holds L1 waits L2; T2 holds L2 waits L3; T3 holds L3 waits L1. Deadlocked? Fix?

Answer: Yes — cycle T1 → T2 → T3 → T1. Impose a global order L1 < L2 < L3: T3 must acquire L1 before L3, breaking the cycle.

E3 · Happens-before. Thread A writes data = 42 then ready.store(true, release); thread B spins on ready.load(acquire) then reads data. Is B guaranteed to read 42? What if both use relaxed?

Answer: With release/acquire, yes: the store synchronizes-with the load that reads true. With relaxed, no — there is no happens-before edge, so B's read of data is a data race (UB in C++) and may see the old value.

Quiz

Final assessment — beginner:

Final assessment — intermediate:

Final assessment — advanced:

? A single-threaded event loop handling 1,000 sockets is an example of...
+ concurrency without parallelism :: Overlapping progress on one thread.
- parallelism without concurrency :: One thread cannot run in parallel.
- multiprocessing :: One process.
- a data race :: Run-to-completion prevents races between callbacks.

? Which primitive limits a resource to N simultaneous users?
+ Counting semaphore :: N permits.
- Mutex :: One user.
- Latch :: One-shot countdown.
- Spinlock :: One user, busy-waiting.

? join() guarantees...
+ the thread finished and its writes are visible to the joiner :: Happens-before.
- that shared state is protected while the thread runs :: Join only orders completion.
- that output from different threads never interleaves :: Not during their execution.
- that the thread succeeded :: It may have failed.
? Which ordering does the predict-the-output program print?
+ 1 3 8 4 7 6 2 5 :: Sync, microtasks (including 6 queued by 4), then timers.
- 1 2 3 4 5 6 7 8 :: Ignores scheduling.
- 1 3 8 7 4 6 2 5 :: The await continuation was queued before the .then.
- 1 3 8 4 7 2 6 5 :: Microtask 6 runs before any timer.

? A Python service is CPU-bound in pure Python on the standard build. What speeds it up on 8 cores?
+ A process pool :: Bypasses the GIL.
- A thread pool of 64 :: The GIL serialises bytecode.
- asyncio :: Single-threaded.
- Making functions async :: No parallelism.

? Which are correct uses of a timeout? (select all that apply)
+ Bounding the wait for a downstream call :: Prevents hangs.
+ Propagating the remaining deadline to inner calls :: Deadline propagation.
- Assuming the remote side did nothing after it fires :: Unknown outcome.
+ Combining it with cancellation of the operation :: Stops wasted work.
? With 8 workers and 40 ms per request, what happens at 250 requests/s with an unbounded queue?
+ Arrivals exceed the 200/s capacity, so the queue and latency grow without bound. :: Unstable system.
- Latency stays at 40 ms. :: Ignores queueing.
- Throughput rises to 250/s. :: Capacity caps it at 200/s.
- Little's Law gives a stable L. :: The system is not stable.

? Which make a lock-free stack safe in C++? (select all that apply)
+ A safe memory-reclamation scheme such as hazard pointers or epochs :: Prevents use-after-free.
+ ABA protection (tags) or reclamation that prevents address reuse :: CAS compares values only.
- Using memory_order_relaxed everywhere :: Breaks publication of node contents.
+ Testing with stress, sanitizers and model checking :: Necessary evidence.

? What does structured concurrency guarantee at scope exit?
+ All child tasks have completed, and their failures have been propagated. :: No orphans.
- All tasks in the program completed. :: Only the scope's children.
- No exceptions were thrown. :: They are propagated, not prevented.
- Children ran in parallel. :: Not implied.

Summary

Review the vocabulary, practise each question type — interleavings, races, primitives, deadlock graphs, memory ordering, async traces, pools, cancellation, queueing, debugging, performance and security — and complete a capstone. Revisit any module where an answer surprised you.

Further Reading

Revisit the Further Reading of each module; the books below are the recommended next steps.