Lua Coroutine Scheduler
A coroutine scheduler is a small loop that resumes several coroutines in turn until all of them are dead. Each turn gives one coroutine a short run and then moves to the next.
Manual resumes show turns in order
Two coroutines print one step each and then yield. Four resumes run them in turn.
local a = coroutine.create(function()
print("A1")
coroutine.yield()
print("A2")
end)
local b = coroutine.create(function()
print("B1")
coroutine.yield()
print("B2")
end)
coroutine.resume(a)
coroutine.resume(b)
coroutine.resume(a)
coroutine.resume(b)A loop can schedule every task until all are dead
The loop resumes each live task once per round. It stops when no task is left alive.
local t1 = coroutine.create(function()
print("task1 step1")
coroutine.yield()
print("task1 step2")
end)
local t2 = coroutine.create(function()
print("task2 step1")
coroutine.yield()
print("task2 step2")
end)
local tasks = {t1, t2}
local alive = true
while alive do
alive = false
for i = 1, #tasks do
if coroutine.status(tasks[i]) ~= "dead" then
alive = true
coroutine.resume(tasks[i])
end
end
endA scheduler can print values yielded on each turn
Two workers yield numbers on each turn. The scheduler prints each yielded number in turn order.
local w1 = coroutine.create(function()
coroutine.yield(1)
coroutine.yield(2)
end)
local w2 = coroutine.create(function()
coroutine.yield(10)
coroutine.yield(20)
end)
local tasks = {w1, w2}
local alive = true
while alive do
alive = false
for i = 1, #tasks do
if coroutine.status(tasks[i]) ~= "dead" then
alive = true
local ok, value = coroutine.resume(tasks[i])
if value then
print(value)
end
end
end
endExample
local a = coroutine.create(function()
print("A1")
coroutine.yield()
print("A2")
end)
local b = coroutine.create(function()
print("B1")
coroutine.yield()
print("B2")
end)
local c = coroutine.create(function()
print("C1")
coroutine.yield()
print("C2")
end)
local tasks = {a, b, c}
local turns = 0
local alive = true
while alive do
alive = false
for i = 1, #tasks do
if coroutine.status(tasks[i]) ~= "dead" then
alive = true
turns = turns + 1
coroutine.resume(tasks[i])
end
end
end
print(turns)Key points
- Scheduler: the scheduler is a loop that holds a list of coroutines and resumes them in turn.
- Round robin: round robin means each coroutine gets one turn before any coroutine gets a second turn.
- Turn: a turn is one resume of one coroutine.
- Yield: yield ends a turn and gives control back to the scheduler.
- Dead: the scheduler skips a coroutine once its status is dead.
In this example, the scheduler holds three tasks and resumes each live task once per round. Each task prints two steps across two turns. The scheduler stops after 6 turns when all three tasks are dead.
Note
a scheduler never runs two coroutines at once, so turns never overlap.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
When does a round robin scheduler stop?