Capstone Projects

Learning Objectives

Choose a capstone at your level and deliver it with an explicit concurrency model, correctness invariants, failure and cancellation behavior, tests (including race/stress cases), resource and shutdown management, reproducible performance measurements, security/overload considerations and a README.

Assess your work against the rubric.

Plain-Language Explanation

Capstones turn knowledge into evidence. Each project below is small enough to finish but realistic enough to hit real concurrency problems — overload, cancellation, shutdown, races — and every one must be shipped with the same engineering discipline a production service would need.

Precise Technical Explanation

Threaded counter with correct synchronization race demo → lock → atomic → per-thread counters, with a stress test bounded buffer with condition variables, close/shutdown, exactly-once test Async timer and task visualizer asyncio/JS timers, task states, a text or web timeline Promise/future chaining playground chaining, combinators, error paths, timeouts

Concurrent file processor with bounded workers pool sizing, bounded queue, progress, cancellation

Async HTTP client with timeouts and cancellation deadlines, AbortSignal/CancellationToken, retries Thread-safe cache with expiry locks or concurrent maps, single-flight, TTL, eviction Worker pool with graceful shutdown lifecycle, draining, deadlines, leak checks tasks vs microtasks, timers, host differences

Bounded channel and backpressure simulation full policies, throughput vs latency

Concurrent web crawler with domain rate limits per-domain token buckets, dedup, politeness, cancellation

Parallel data-processing pipeline with measured performance stages, backpressure, scaling curves

Async service client with retries, deadlines, circuit breaker, cancellation Concurrent queue with correctness tests linearizability checking, stress, TSan Runtime/task profiler dashboard task counts, loop lag, pool metrics

Deadlock detection and lock-order analyzer (educational) wait-for graphs, lock hierarchy checker RTOS-style task scheduler simulation priorities, periods, PI, deadline misses

Lock-based vs atomic counters under multiple workloads benchmark methodology, contention, false sharing

Structured task scope with cancellation and failure propagation scope semantics, exception groups, shielding Work-stealing scheduler simulation deques, steal policies, granularity

Model-check a small synchronization protocol state-space exploration, invariants, liveness

Concurrent service with bounded queues, overload protection, tracing, graceful shutdown the whole course

Real-time producer/consumer with priority and deadline analysis RM/EDF, WCET, PI Every capstone must include

Problem statement and architecture diagram.

Explicit concurrency model (threads/tasks/processes, who owns what, how they communicate).

Correctness invariants and how they are enforced.

Failure and cancellation behavior.

Tests, including race/stress cases where applicable (with timeouts).

Resource and shutdown management.

Performance measurements with reproducible methodology.

Security and overload considerations.

A README with setup, run instructions and known limitations.

Insufficient (0) invariants stated and enforced; race detector/stress clean works under normal load races, lost work or hangs explicit ownership and communication; simplest correct design implicit/shared everything Failure & cancellation timeouts, cancellation, cleanup, tested Shutdown & resources bounded everything; graceful drain with deadline; no leaks leaks or hangs on exit deterministic + stress + failure-path tests happy path + some stress reproducible measurements, percentiles, scaling curves Security & overload limits, backpressure, threat model README, diagram, limitations

Why It Matters in Real Systems

The checklist above is exactly what code reviewers and on-call engineers look for; practising it on small projects builds the habit.

Key Terms

Who runs what, who owns which data, how they communicate Property that must always hold

Draining work within a deadline and releasing resources Benchmark whose setup and method others can repeat

Diagram or Execution Timeline

flowchart LR
  P[problem + invariants] --> M[concurrency model]
  M --> I[implementation]
  I --> T[tests: deterministic, stress, failure paths]
  T --> B[benchmarks: reproducible]
  B --> R[README + limitations]

Syntax and API Overview

This is a concurrency concept, not a language construct; each project uses the APIs of its chosen language (Module 17).

Complete Code Example

A reference skeleton for the intermediate "worker pool with graceful shutdown" capstone — bounded queue, cancellation, per-task errors, deadline-bounded drain and a leak check (CPython 3.13.1 asyncio):

import asyncio


class WorkerPool:
    def __init__(self, workers, capacity):
        self.queue = asyncio.Queue(capacity)                 # bounded: backpressure
        self.workers = [asyncio.create_task(self._worker(i), name=f"worker-{i}") for i in range(workers)]
        self.results, self.errors = [], []

    async def submit(self, job, timeout=1.0):
        await asyncio.wait_for(self.queue.put(job), timeout) # caller waits (bounded) when full

    async def _worker(self, i):
        while True:
            job = await self.queue.get()
            try:
                self.results.append(await job())
            except asyncio.CancelledError:
                raise
            except Exception as e:                           # one failing job does not kill the worker
                self.errors.append(f"{type(e).__name__}: {e}")
            finally:
                self.queue.task_done()

    async def shutdown(self, drain_deadline):
        try:
            async with asyncio.timeout(drain_deadline):
                await self.queue.join()                      # graceful: finish queued work
            drained = True
        except TimeoutError:
            drained = False
 
…
graceful drain: True | results: [0, 1, 4, 9, 16, 25, 36, 64, 81] | errors: ['ValueError: job 7 failed']
slow job, 0.1 s drain deadline -> drained: False (remaining work cancelled)
tasks still alive after shutdown: []

Line-by-Line Explanation

The bounded queue makes submit wait (up to a timeout) when workers fall behind — backpressure.

Workers catch ordinary exceptions per job (recorded, not fatal) but re-raise cancellation.

shutdown first drains within a deadline (queue.join() under asyncio.timeout), then cancels and awaits the workers; it reports whether the drain completed.

The leak check confirms no task outlives the pool.

Output

(see the program output above)

Interleaving or State Trace

10 jobs, 3 workers, capacity 4: submits 0–6 fill the workers and queue; submit 7 waits until a worker takes a job; job 7 fails and is recorded; after all task_dones, join() returns, workers are cancelled, gathered, and the pool is empty.

Correctness and Safety Analysis

Invariants: every submitted job is either completed (result), failed (error), or — after a missed deadline — cancelled; workers never die from job errors; no tasks leak after shutdown.

Common Mistakes and Misconceptions

Unbounded queues, workers that die on the first exception, shutdown that cancels before draining (losing work) or drains without a deadline (hangs), and no leak check.

Failure Modes and Debugging Steps

Shutdown hangs → a job ignoring cancellation; lost results → exceptions escaping workers; memory growth → unbounded queues — Modules 13, 14, 23.

Performance and Scalability Implications

Measure throughput and latency percentiles versus worker count and queue capacity; include the drain time distribution.

Security Considerations

Bound the queue and per-job time; never let untrusted jobs run without timeouts.

Platform and Runtime Caveats

The skeleton uses Python 3.11+ (asyncio.timeout); ports to other languages use their pools and cancellation tokens.

Real-World Applications

Every job runner, crawler and background-processing service has this shape.

Best Practices

Ship the checklist items with the code; measure before and after changes; keep the design as simple as the requirements allow.

Exercises

E1 · Beginner. Add a progress counter to the pool safely.

Answer: Increment it in the worker after each job (single-threaded asyncio: no lock needed between awaits) and expose it read-only.

E2 · Intermediate. Add per-job timeouts.

Answer: Wrap await job() in asyncio.timeout(job_timeout) inside the worker and record TimeoutError as a job error.

E3 · Advanced challenge. Port the pool to threads with queue.Queue and a threading.Event for shutdown.

Answer: Workers loop on q.get(timeout=…) checking the event; shutdown sets the event after q.join() (bounded with a timeout by polling unfinished_tasks), then joins worker threads with timeouts; errors are collected in a lock-protected list.

Quiz

? What should graceful shutdown do first?
+ Stop accepting work and drain in-flight work within a deadline. :: Then cancel what remains.
- Cancel every worker immediately. :: That loses queued work.
- Wait forever for all work. :: A slow job would hang shutdown.
- Exit the process. :: Resources and work would be lost.

? Why do workers catch ordinary exceptions but re-raise CancelledError?
+ One bad job should not kill a worker, but cancellation must still stop it. :: Correct separation.
- CancelledError is not an exception. :: It is a BaseException subclass.
- To hide errors. :: Errors are recorded.
- asyncio requires it. :: It is a design choice.

? Which belong in every capstone README? (select all that apply)
+ Concurrency model and invariants :: Required.
+ Known limitations :: Required.
- Only screenshots :: Not sufficient.
+ How to run tests and benchmarks :: Required.

Summary

Capstones at four levels apply the whole course; each must state its concurrency model and invariants, handle failure, cancellation, overload and shutdown, test races and failure paths, measure reproducibly and document limitations — assessed with the rubric.

Further Reading

The references of every module.

"The Twelve-Factor App" (disposability: fast startup and graceful shutdown).