Master the fundamental concepts of threads & concurrency through this focused micro-challenge.
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 worksGreen threads are cooperatively scheduled coroutines living entirely in userspace. No kernel clone(), no preemption timer: each thread yields explicitly. Languages like early Ruby and Go prototypes used this model before OS threads matured.
Each green thread needs:
For example, three threads printing A, B, C with round-robin yields produce ABCABC... only if every thread calls yield() before looping again; forget one yield and the others starve.
cLoading…
Go's goroutine runtime, Ruby's Fiber, and pre-1.5 Java's green threads all started from exactly this design: cooperative user-space stacks switched with saved register state instead of kernel involvement. The single-kernel-thread limitation you'll hit here is precisely why early Java green threads were dropped in favor of native threads once multi-core CPUs became common in the early 2000s.
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 thread creation, cooperative yield(), and a scheduler dispatch loop. Building the stack switch yourself shows why modern runtimes moved to preemptive kernel threads for CPU-bound work.
Build the scheduler core of a green-thread library. It manages user-space threads that switch only when they choose to, each running on its own stack taken from a fixed pool. Implement create, yield, join and exit with pthread-like semantics. An exited thread stays a zombie that holds its stack until someone joins it, and the scheduler must notice when every thread is blocked.
cLoading…
main runs first, on the process stack. The ready queue is FIFO. yield moves the current thread to the back of the queue and runs the head, or keeps running if the queue is empty.i covers 0x10000000 + i*SIZE up to the byte before the next slot. The new thread joins the back of the ready queue. The creator keeps running.ESRCH. Joining yourself gives EDEADLK. A thread that someone else is already joining gives EINVAL (already being joined).deadlock: no runnable thread, followed by , X waits for Y for each blocked thread in definition order, and stop.no such thread (undefined, or main), already exists (created and not yet reaped; a reaped thread may be created again), EAGAIN (no free stack).cLoading…
Every change of the running thread prints a -- switch line. The last line is always the statistics: created threads, the peak number of live threads (created and not yet reaped), and stacks still in use.
Input:
cLoading…
Output:
cLoading…
Hidden tests cover stack exhaustion and reuse after a join, re-creating a reaped thread, join errors (ESRCH, EDEADLK, EINVAL), a join deadlock, threads that exit before they are joined, main exiting with zombies left over, and nested creation.