This is the moment cooperative scheduling comes alive. Two tasks that each yield once will hand the CPU back and forth, and the scheduler weaves them into a single exact interleaving. Pinning that A,B,A,B trace is the heart of the whole project.
Run two tasks that each yield once, and assert the exact interleaved trace.
Yielding is a task voluntarily giving up the CPU: it returns Ready instead of
running to the end, trusting the scheduler to come back to it. Because a yielded task
goes to the back of the run queue, two tasks that both yield naturally take
turns. Task 1 runs and yields, task 2 runs and yields, task 1 resumes and finishes,
task 2 resumes and finishes - the interleaving [1, 2, 1, 2] falls straight out of
the FIFO rule with no extra machinery.
Trace it by hand once: the queue starts [1, 2]; step 1 yields so it goes to the
back, leaving [2, 1]; step 2 yields, leaving [1, 2]; step 1 finishes, leaving
[2]; step 2 finishes. That exact alternation is what people mean by “green threads
running concurrently” on a single driver - not two things at once, but two things
taking precise, fair turns. Everything else in this project is a variation on this
one trace.
// a task that yields once then finishesfunc twoStep() func() Status {done := falsereturn func() Status {if !done { done = true; return Ready } // step 1: yieldreturn Done // step 2: finish}}// spawn two of these; the scheduler already does the rest