Table of Contents
Introduction to Coroutines in Lua
Basics of Coroutines
Creating a Coroutine:
A coroutine in Lua is created using the coroutine.create function, which takes a Lua function as an argument and returns a coroutine.
function foo() print("Coroutine started") coroutine.yield() print("Coroutine resumed") end
co = coroutine.create(foo)
Running a Coroutine:
To start or resume a coroutine, use coroutine.resume. This function runs the coroutine until it yields or terminates.
coroutine.resume(co) -- Output: Coroutine started coroutine.resume(co) -- Output: Coroutine resumed
Yielding a Coroutine:
Within a coroutine, you can pause its execution using coroutine.yield. This allows the coroutine to yield control back to the caller, who can then resume it later.
function bar() for i = 1, 3 do print("Iteration", i) coroutine.yield() end end co = coroutine.create(bar) for i = 1, 3 do coroutine.resume(co) -- Output: Iteration 1, Iteration 2, Iteration 3 end
Coroutine Status
You can check the status of a coroutine using coroutine.status. The possible statuses are:
- “running”: The coroutine is currently running.
- “suspended”: The coroutine is suspended and can be resumed.
- “normal”: The coroutine is active but not running (it has called yield).
- “dead”: The coroutine has finished its execution.
print(coroutine.status(co)) -- Output: suspended (after first yield) coroutine.resume(co) print(coroutine.status(co)) -- Output: dead (after finishing)
Coroutine Functions
Lua provides several functions to work with coroutines:
- coroutine.create(f): Creates a new coroutine with function f.
- coroutine.resume(co, …): Resumes the coroutine co, passing any arguments to it.
- coroutine.yield(…): Yields the coroutine, optionally passing values back to the caller.
- coroutine.status(co): Returns the status of the coroutine co.
- coroutine.running(): Returns the currently running coroutine.
Example 1: Coroutine with Arguments
You can pass arguments to coroutines and use them within the coroutine function.
function greet(name) print("Hello, " .. name) coroutine.yield() print("Goodbye, " .. name) end co = coroutine.create(greet) coroutine.resume(co, "Alice") -- Output: Hello, Alice coroutine.resume(co) -- Output: Goodbye, Alice
Example 2: Coroutine Communication
Coroutines can yield and return values to the caller.
function add(a, b) coroutine.yield(a + b) end co = coroutine.create(add) success, result = coroutine.resume(co, 5, 7) -- Passes 5 and 7 to the coroutine print(result) -- Output: 12
Example 3: Generating a Sequence with Coroutines
Coroutines can be used to generate sequences, such as the Fibonacci sequence.
function fibonacci() local a, b = 0, 1 while true do coroutine.yield(a) a, b = b, a + b end end co = coroutine.create(fibonacci) for i = 1, 10 do success, value = coroutine.resume(co) print(value) -- Output: First 10 numbers of the Fibonacci sequence end
Example 4: Coroutine as a Producer-Consumer
Coroutines can be used to implement producer-consumer patterns.
function producer() local i = 0 while true do i = i + 1 coroutine.yield(i) end end function consumer() while true do local status, value = coroutine.resume(pro) print("Consumed: " .. value) end end pro = coroutine.create(producer) consumer() -- This will consume and print the produced values indefinitely
Example 5: Simple State Machine with Coroutines
Coroutines can help in creating simple state machines.
function state_a() print("In State A") coroutine.yield("go to B") print("Returning to State A") end function state_b() print("In State B") coroutine.yield("go to A") end state = {A = coroutine.create(state_a), B = coroutine.create(state_b)} current_state = "A" while true do local status, next_state = coroutine.resume(state[current_state]) if not status then break end current_state = (next_state == "go to A") and "A" or "B" end
Example 6: Round-Robin Scheduling
Coroutines can be used for round-robin scheduling to manage multiple tasks.
function task1() for i = 1, 3 do print("Task 1, iteration " .. i) coroutine.yield() end end function task2() for i = 1, 3 do print("Task 2, iteration " .. i) coroutine.yield() end end tasks = {coroutine.create(task1), coroutine.create(task2)} while true do local all_done = true for i = 1, #tasks do if coroutine.status(tasks[i]) ~= "dead" then coroutine.resume(tasks[i]) all_done = false end end if all_done then break end end