The finale runs a full set of cooperating green threads at once - a producer streaming over a channel to a consumer, two sleepers on the virtual clock, and a coordinator that joins the consumer - and asserts the exact step order, the virtual time each task wakes, and the final results. Every layer you built proves itself together.
Run producer, consumer, two sleepers, and a joiner, and assert the exact trace, wake times, and results.
This is the promise the whole project was built to keep: a real cooperative runtime running several green threads that block on each other and on time, all woven into one exact, reproducible schedule. Five tasks with distinct roles - a producer, a consumer that yields between values, two sleepers with different deadlines, and a coordinator joining the consumer - and yet the entire interleaving is a single deterministic trace you can assert to the element.
Read the timeline it produces. At virtual time 0 the producer streams 10, 20, 30
and closes; the consumer receives and yields after each, summing to 60; the joiner
blocks until the consumer’s final receive completes and wakes it; the two sleepers
park. Only when nothing is left to run does the clock move - jumping to 5 to wake the
nearer sleeper, then to 10 for the farther one, ending at Now() 10. The trace [1, 2, 3, 4, 5, 2, 2, 2, 5, 4, 3] is the run queue, the wait queues, the channel, the
join, and the virtual clock all agreeing. From a single cursor bumping through a byte
of work, you have built the honest core of a green-thread scheduler - tasks, yielding,
blocking and waking, timers, semaphores, mutexes, channels, wait groups, priority,
deadlock detection, and cancellation - the same cooperative design at the heart of
asyncio and every async runtime, minus the real-time I/O and multi-core parallelism
they layer on top. That is a real scheduler, and it is yours.
ch := NewChan(3)s.Spawn(producer(ch, []int{10, 20, 30})) // sends then Closes.Spawn(consumer(ch)) // recv-yield loop, sums to 60s.Spawn(sleeper(10)); s.Spawn(sleeper(5)) // wake at 10 and 5s.Spawn(joiner(consumerTask)) // joins the consumererr := s.Run() // trace [1,2,3,4,5,2,2,2,5,4,3], now=10