Now the runtime comes alive. The scheduler loop pulls a task, steps it, and acts on the status it returns - re-enqueue on Ready, drop on Done - until nothing is left. Today a single self-driving task proves the loop resumes a task across many steps.
Write Run - dequeue a task, step it, re-enqueue on Ready and drop on Done, until the queue is empty.
This is the beating heart of a cooperative runtime, and it is astonishingly small:
take the task at the front, step it once, and look at what it returns. Ready
means “I did a little work and yielded” - so put it at the back of the queue to
run again later. Done means “I finished” - so drop it. Repeat until the queue is
empty. There is no preemption anywhere: the loop never interrupts a step, it only
acts between steps.
A task that needs several steps to finish is a little state machine - it
remembers how far it has got and returns Ready until its last step returns Done.
Stepping such a task three times to completion is the whole idea of resumption, done
with one task so no interleaving distracts from it. (Blocked also comes back from
step, but no task parks itself yet - that is Chapter 3, so the loop can ignore it
for now.)
func (s *Scheduler) Run() {for s.rq.len() > 0 {t := s.rq.dequeue()switch t.step() {case Ready:s.rq.enqueue(t) // yielded - back of the linecase Done:// finished - drop it}}}