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 worksCooperative threads fail when one thread never yields. Preemption fixes that by forcing switches on asynchronous events. In userspace you can hijack SIGALRM from setitimer to run a scheduler on every tick.
Setup sequence:
SIGALRM handler that calls schedule()setitimer(ITIMER_REAL, ...) for quantumFor example, a 10 ms timer firing while thread A runs in an infinite loop preempts A, saves its context, and dispatches thread B without A calling yield().
This is how early user-level threading libraries like GNU Pth added preemption on top of cooperative scheduling, and the async-signal-safety constraint you'll hit is a real, frequently-violated rule: calling malloc() or printf() inside a signal handler is a documented source of deadlocks and heap corruption in production C code, which is exactly why POSIX maintains an explicit async-signal-safe function list.
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 wire alarm-driven preemption into your green-thread runtime. The task asks you to compare cooperative-only and preemptive modes on a CPU-bound loop so the starvation difference is visible.
Add preemption to user-space threads. An interval timer (setitimer) raises SIGALRM every Q ticks of CPU time, and the handler forces a switch, so a thread that never yields still loses the CPU. Code that must not be interrupted blocks SIGALRM with sigprocmask. An alarm that arrives during that time stays pending (standard signals do not queue, so repeats merge), and it is delivered the moment the thread unblocks it. Simulate this tick by tick.
cLoading…
work takes time. The other steps take zero ticks and run at once.cLoading…
t=START-END NAME ran, REASON, or t=END NAME REASON if it ran zero ticks. REASON is preempted, yield or finished.print step outputs t=NOW [NAME] TEXT.t=NOW [NAME] bad step: TEXT and is skipped.Input:
cLoading…
Output:
cLoading…
sigprocmask in a single OS thread.unblock.Hidden tests cover a quantum of 1, a thread preempting itself when it is the only one left, several merges inside one long blocked section, block/unblock with no alarm in between, yield with nobody else ready, and bad steps.