Master the fundamental concepts of threads & concurrency through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksThread pools keep a fixed set of worker threads alive, pulling tasks from a shared queue. Spawning a thread per request wastes milliseconds on stack setup and scheduler registration; pools trade memory for steady latency under bursty load.
Components:
For example, a pool of 4 workers draining 100 tiny jobs finishes near 25 batches instead of paying 100 thread creation costs.
This is the exact architecture behind nginx's worker processes, Java's ThreadPoolExecutor, and Python's concurrent.futures.ThreadPoolExecutor , bounding concurrency to avoid the 'thread-per-request' collapse that took down early 2000s web servers under load spikes (the C10K problem). Getting shutdown wrong here mirrors a real operational hazard: failing to drain the queue before joining workers causes submitted work to be silently dropped in production job schedulers.
Before you call the implementation done, walk failure modes on purpose. Test empty structures, single-element edge cases, maximum concurrency, and errno paths that must not crash the program. OS code usually fails in production when happy-path tests pass but invariants break under contention or memory pressure.
Keep structures small and name fields after kernel counterparts when possible. That lets you read man pages and kernel source side by side while you work. Print observable events during development; remove noisy logs once tests pass reliably.
You will implement enqueue, worker threads, and graceful shutdown. This exercise requires backpressure when the queue exceeds a max depth, matching how server frameworks cap pending work.
Simulate a fixed-size thread pool: W worker threads take tasks from one FIFO queue. The queue may be bounded, and when it is full the submitting thread blocks (back-pressure). shutdown either drains the queue (graceful) or throws the queued tasks away (now). This is a discrete-event simulation with integer time, so the result is exact and repeatable.
cLoading…
Process each time point t in this order:
t frees its worker. Then idle workers take tasks from the head of the queue, the lowest-numbered worker first.max(T, t) once the submitter is free. If the queue is full, the submitter blocks. It resumes as soon as a worker takes a task, and that same task is submitted then. Each queued task can be taken at once by an idle worker.now, discard everything still queued. A blocked submitter wakes up. Every later submission (including the one it was blocked on) is rejected.The simulation ends when no event is left.
cLoading…
queue unbounded when there is no capacity.busy * 100 / last finish time, rounded down (0 if nothing finished).Input:
cLoading…
Output:
cLoading…
Hidden tests cover more workers than tasks, a bounded queue that blocks the submitter repeatedly, a graceful shutdown with a full queue, a shutdown that rejects a blocked submission, submissions after shutdown, and a run with no shutdown at all.