🎓 A1Learner
🔍
0 of 49 lessons completed

Lua Priority Coroutine Scheduler

A priority scheduler is a scheduler whose run order comes from a priority value on each task. A task with a higher priority runs before a task with a lower priority, whatever order the tasks were created in.

Sorting by priority changes the order

Three tasks are created low, high, then mid. Sorting by priority puts high first.

local tasks = {
  {name = "low", priority = 1},
  {name = "high", priority = 3},
  {name = "mid", priority = 2}
}
table.sort(tasks, function(a, b) return a.priority > b.priority end)
print(tasks[1].name)
print(tasks[2].name)
print(tasks[3].name)

The scheduler runs high priority first

Each task prints its name once. The scheduler sorts first, so the print order follows priority, not creation order.

local function make(name)
  return coroutine.create(function() print(name) end)
end
local tasks = {
  {name = "low", priority = 1, co = make("low")},
  {name = "high", priority = 3, co = make("high")},
  {name = "mid", priority = 2, co = make("mid")}
}
table.sort(tasks, function(a, b) return a.priority > b.priority end)
for _, task in ipairs(tasks) do
  coroutine.resume(task.co)
end

A high priority task runs to completion first

Each round picks the highest priority task that is not dead. The high task prints both its steps before the mid task starts.

local function make(name, steps)
  return coroutine.create(function()
    for i = 1, steps do
      print(name .. i)
      coroutine.yield()
    end
  end)
end
local tasks = {
  {priority = 1, co = make("C", 1)},
  {priority = 3, co = make("A", 2)},
  {priority = 2, co = make("B", 1)}
}
local running = true
while running do
  running = false
  table.sort(tasks, function(a, b) return a.priority > b.priority end)
  for _, task in ipairs(tasks) do
    if coroutine.status(task.co) ~= "dead" then
      running = true
      coroutine.resume(task.co)
      break
    end
  end
end

Example

local function make(name, steps)
  return coroutine.create(function()
    for i = 1, steps do
      print(name .. i)
      coroutine.yield()
    end
  end)
end
local tasks = {
  {priority = 2, co = make("mid", 2)},
  {priority = 1, co = make("low", 1)},
  {priority = 3, co = make("high", 2)}
}
local running = true
while running do
  running = false
  table.sort(tasks, function(a, b) return a.priority > b.priority end)
  for _, task in ipairs(tasks) do
    if coroutine.status(task.co) ~= "dead" then
      running = true
      coroutine.resume(task.co)
      break
    end
  end
end

Key points

  • Priority: a number stored on each task that ranks how urgently it should run.
  • Queue order: the scheduler sorts its tasks by priority before it runs any of them.
  • Creation order: the order tasks were created in does not decide the run order here.
  • Pick rule: on each round the scheduler resumes the unfinished task with the highest priority.
  • Completion: a task that is dead is skipped, so lower priority tasks still get their turn.

In this example, the tasks are created mid, low, then high, but the scheduler sorts by priority on every round. It resumes the highest priority unfinished task once per round, so high prints both its steps, then mid prints both of its steps, and low prints last.

Note

priority here is cooperative, since a task only stops when it yields or finishes.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Tasks are created in the order P (priority 2), Q (priority 5), R (priority 1). A priority scheduler that sorts highest first runs them in which order?