ℹ️ Select 'Choose Exercise', or randomize 'Next Random Exercise' in selected language.

Choose Exercise:
Timer 00:00
WPM --
Score --
Acc --
Correct chars --

Lua Coroutine-Based Task Scheduler

Lua

Goal -- WPM

Ready
Exercise Algorithm Area
1local Scheduler = {}
2
3function Scheduler:new()
4local self = {
5tasks = {}, -- Stores active coroutines
6yielded_values = {},
7is_running = false
8}
9setmetatable(self, {__index = Scheduler})
10return self
11end
12
13function Scheduler:add_task(coro_func, ...)
14if self.is_running then
15error("Cannot add task while scheduler is running.")
16end
17local coro, err = pcall(coroutine.create, coro_func, ...)
18if not coro then
19print("Error creating coroutine: " .. tostring(err))
20return
21end
22table.insert(self.tasks, {coro = coro, status = "ready"})
23end
24
25function Scheduler:run()
26if self.is_running then
27return
28end
29self.is_running = true
30
31while #self.tasks > 0 do
32local tasks_to_remove = {}
33for i, task_data in ipairs(self.tasks) do
34if task_data.status == "ready" or task_data.status == "running" then
35task_data.status = "running"
36local success, result = pcall(coroutine.resume, task_data.coro)
37
38if not success then
39print("Coroutine error: " .. tostring(result))
40task_data.status = "error"
41table.insert(tasks_to_remove, i)
42elseif result == coroutine.yield() then
43-- Coroutine yielded, store its result for potential future use
44task_data.status = "suspended"
45table.insert(self.yielded_values, {task_index = i, value = result})
46-- Keep task in list, it's not finished
47elseif coroutine.status(task_data.coro) == "dead" then
48-- Coroutine finished successfully
49task_data.status = "finished"
50table.insert(tasks_to_remove, i)
51else
52-- Coroutine resumed but didn't yield or finish (unexpected)
53print("Unexpected coroutine state after resume.")
54task_data.status = "error"
55table.insert(tasks_to_remove, i)
56end
57end
58end
59
60-- Remove finished or errored tasks from the end to avoid index issues
61table.sort(tasks_to_remove, function(a, b) return a > b end)
62for _, index in ipairs(tasks_to_remove) do
63table.remove(self.tasks, index)
64end
65
66-- Clear yielded values for the next tick if needed, or manage them
67-- For this simple scheduler, we just process them once.
68self.yielded_values = {}
69
70-- Prevent busy-waiting if no tasks are active but some are suspended
71if #self.tasks == 0 and #self.yielded_values > 0 then
72-- This scenario implies tasks yielded and are waiting for input,
73-- but the scheduler loop condition is based on active tasks.
74-- A more advanced scheduler would have a way to re-queue suspended tasks.
75-- For now, we assume tasks either finish or error.
76end
77end
78
79self.is_running = false
80end
81
82-- Helper to check if scheduler is empty
83function Scheduler:is_empty()
84return #self.tasks == 0
85end
86
87-- Example Usage:
88-- local scheduler = Scheduler:new()
89--
90-- local function task1(name, delay)
91-- print(name .. " started")
92-- for i = 1, 3 do
93-- print(name .. " working... " .. i)
94-- coroutine.yield(i * 10)
95-- wait(delay) -- Assume wait is defined elsewhere
96-- end
97-- print(name .. " finished")
98-- return "Task1 Done"
99-- end
100--
101-- local function task2(message)
102-- print("Task2 received: " .. message)
103-- coroutine.yield("ACK")
104-- print("Task2 continuing")
105-- return "Task2 OK"
106-- end
107--
108-- scheduler:add_task(task1, "JobA", 0.1)
109-- scheduler:add_task(task2, "Hello from main")
110--
111-- scheduler:run()
112
113-- print("Scheduler finished.")
Algorithm description viewbox

Lua Coroutine-Based Task Scheduler

Algorithm description:

This Lua code implements a basic coroutine-based task scheduler. It allows adding functions that run as coroutines, managing their execution cycles. The scheduler iteratively resumes tasks, handling yielded values and detecting task completion or errors. This is useful for managing asynchronous operations or complex game AI behaviors without blocking the main thread.

Algorithm explanation:

The scheduler uses Lua's coroutine API to manage concurrent execution of tasks. `coroutine.create` sets up a coroutine from a function, and `coroutine.resume` runs it until it yields or finishes. The `tasks` table holds coroutines and their states ('ready', 'running', 'suspended', 'finished', 'error'). The `run` loop continues as long as there are active tasks. When a task yields, its result is stored, and its status is updated to 'suspended'. If `coroutine.resume` returns an error, the task is marked 'error'. Finished tasks are removed. The scheduler's time complexity is roughly O(N*M) where N is the number of tasks and M is the average number of yields per task, as each yield requires a resume and check. Space complexity is O(N) for storing task states and yielded values. Edge cases include attempting to add tasks while running, errors during coroutine creation or execution, and handling the 'dead' status.

Pseudocode:

Initialize a scheduler with an empty task list and a running flag.
Add a task by creating a coroutine from a function and storing it with 'ready' status.
Run the scheduler: set running flag to true.
While there are tasks:
  Iterate through tasks:
    If task is 'ready' or 'running':
      Resume the coroutine.
      If resume fails (error): mark task as 'error', add to removal list.
      If coroutine yields: mark task as 'suspended', store yielded value.
      If coroutine status is 'dead': mark task as 'finished', add to removal list.
  Remove finished/errored tasks from the list.
Set running flag to false.