The scheduler is the single driver, so every run order is exact and testable - no OS threads, no preemption, no real concurrency. Each lesson pins one deterministic trace: two yielding tasks producing an exact A,B,A,B, a blocked task that is not run until woken, FIFO wake order among several waiters, two sleepers waking in deadline order with the clock advancing only when the run queue is empty, a buffered send that does not block until the buffer is full while an unbuffered send blocks until a receiver arrives, a semaphore permitting exactly N, and an all-blocked-with-no-timer run reported as a deadlock instead of a hang.
Over 32 lessons you build a cooperative green-thread scheduler from scratch: a single-threaded, deterministic user-space runtime that multiplexes many lightweight tasks onto one driver over a virtual clock. A green thread is modelled as a resumable task - a step function that advances a little each time it is run and returns a status: Ready (it yielded and should run again), Blocked (it is parked on a resource), or Done. Because the scheduler is the only thing running, every interleaving is exact and reproducible, so each lesson pins a concrete step trace, wake order, or virtual-clock value you can assert against.
You start with a FIFO run queue and a scheduler loop that runs one task to completion, then add cooperative yielding and round-robin interleaving (the exact A,B,A,B trace), wait queues so a task can block and later be woken in FIFO order, a virtual clock with sleeping tasks and a timer ordering that advances the clock only when nothing else can run, and a family of synchronization primitives - a counting semaphore, a mutex, buffered and unbuffered channels with close, and a wait group - all built on the same block-and-wake core. The final chapters add a priority policy, deadlock detection so an all-blocked run reports cleanly instead of hanging, task cancellation that wakes a cancelled task's joiners, and a capstone that runs cooperating green threads - a producer and consumer over a channel, two sleepers, and a fan-in join - asserting the exact execution order, the virtual time each task wakes, and the final results.
This is a teaching-grade cooperative runtime, deliberately honest about its model: it is single-threaded and cooperative (a task keeps the CPU until it yields or blocks - there is no preemption), it uses a virtual clock rather than real time, and tasks are resumable step functions rather than OS threads with real saved stacks. That exact model is what keeps every result deterministic and language-neutral - the same design at the heart of Python's asyncio, early Go, and every async runtime, minus the real-time I/O, multi-core parallelism, and stack switching those production systems layer on top.
A green thread in our world is not an OS thread - it is a resumable task, a small step function the scheduler runs a little at a time. Today you define the one type everything else depends on - the status a task hands back each time it is stepped.
Define a Status with the values Ready, Blocked, and Done, and a task whose step reports one of them.
A real green-thread runtime saves and restores machine stacks so a paused thread
can resume exactly where it left off. That is impossible to test with exact values
and different in every language, so we do the honest teaching version: a green
thread is a resumable task, and a task is just a step function. Each time
the scheduler runs it, the function does a little work and returns a Status
saying what to do next - Ready (I yielded, run me again), Blocked (I am parked,
leave me alone), or Done (I finished).
That one return value is the entire contract between a task and the scheduler, so it is where the project starts. Today is deliberately tiny: define the three statuses and a task that returns one when stepped. Every later lesson - yielding, blocking, sleeping, channels - is a task returning one of these three values at the right moment.
// the whole runtime is driven by this one return valuetype Status intconst (Ready Status = iota // did a unit of work; run me againBlocked // parked on a resource; do not run meDone // finished)// a task is just a step function that returns where it standstype Task struct{ step func() Status }
The scheduler's core cooperative / virtual-clock / deadlock-detection model is solid and misuse-hardened - it runs cooperating green threads with an exact, reproducible interleaving and timeline - but it is deliberately single-threaded and cooperative (no preemption, so a task that never yields starves the rest), uses a virtual clock rather than real time, models tasks as resumable step-function state machines rather than real saved stacks, and still lacks generics, a select / timeout primitive, and public task introspection that a production runtime would need.
The clearest modern account of what a coroutine is - symmetric versus asymmetric, stackful versus stackless - and how cooperative multitasking is built from resumable control transfer. The theoretical backbone of modelling a green thread as a resumable task.
Section 1.4.2 introduces coroutines as the fundamental generalization of the subroutine - two routines that resume each other rather than one calling the other. The original rigorous treatment of the cooperative-transfer idea this project rests on.
Dijkstra's EWD123, where P/V semaphores and the discipline of blocking and waking cooperating processes are laid out. The direct ancestor of the semaphore, mutex, and wait-queue primitives built in Chapter 5.
The reference for a real single-threaded cooperative event loop: a run queue of ready callbacks, scheduled timers, and coroutines that suspend at await points. The production system whose core this project reconstructs from scratch.
A from-scratch walkthrough building an async runtime out of generators, a scheduler loop, and a wait/wake queue - the practical companion to this project, showing how yields, sleeps, and blocking I/O become a cooperative event loop.
A concise survey of cooperative (non-preemptive) multitasking and its history, contrasting a task that voluntarily yields the CPU with preemptive scheduling - the exact distinction that defines this project's honest scope.