Lua Coroutines
Coroutine Basics
Create, resume, yield
-- Coroutines are cooperative (not preemptive) threads
local co = coroutine.create(function(name)
print("Hello, " .. name)
coroutine.yield() -- suspend here
print("Welcome back, " .. name)
coroutine.yield()
print("Goodbye, " .. name)
end)
-- Resume advances to next yield
coroutine.resume(co, "Evan") -- "Hello, Evan"
print(coroutine.status(co)) -- "suspended"
coroutine.resume(co) -- "Welcome back, Evan"
coroutine.resume(co) -- "Goodbye, Evan"
print(coroutine.status(co)) -- "dead"
Data Exchange
Passing values through yield/resume
local producer = coroutine.create(function()
local ports = { 22, 80, 443, 8080 }
for _, port in ipairs(ports) do
coroutine.yield(port) -- send port to consumer
end
end)
-- Consumer
while true do
local ok, port = coroutine.resume(producer)
if not ok or port == nil then break end
print("Scanning port: " .. port)
end
-- Bidirectional communication
local echo = coroutine.create(function(initial)
local value = initial
while true do
value = coroutine.yield("echo: " .. tostring(value))
end
end)
local _, resp1 = coroutine.resume(echo, "hello") -- "echo: hello"
local _, resp2 = coroutine.resume(echo, "world") -- "echo: world"
Coroutine as Iterator
wrap creates a function-style coroutine
-- coroutine.wrap returns a function (no resume needed)
local function fibonacci(max)
return coroutine.wrap(function()
local a, b = 0, 1
while a <= max do
coroutine.yield(a)
a, b = b, a + b
end
end)
end
for n in fibonacci(100) do
print(n) -- 0, 1, 1, 2, 3, 5, 8, ...
end
-- File line reader as coroutine
local function lines_from(filename)
return coroutine.wrap(function()
local f = io.open(filename, "r")
if not f then return end
for line in f:lines() do
coroutine.yield(line)
end
f:close()
end)
end
for line in lines_from("/etc/hosts") do
if line:match("^%d") then
print(line)
end
end
Coroutine States
Status checking
-- States: "suspended", "running", "dead", "normal"
local co = coroutine.create(function()
-- "running" while executing
print(coroutine.status(coroutine.running()))
coroutine.yield()
-- resumes here
end)
print(coroutine.status(co)) -- "suspended"
coroutine.resume(co) -- prints "running"
print(coroutine.status(co)) -- "suspended"
coroutine.resume(co) -- finishes
print(coroutine.status(co)) -- "dead"
-- Error handling
local ok, err = coroutine.resume(co)
print(ok, err) -- false, "cannot resume dead coroutine"
Practical Pipeline
Coroutine-based data pipeline
-- Producer -> Filter -> Consumer pipeline
local function port_scanner(targets)
return coroutine.wrap(function()
for _, target in ipairs(targets) do
-- simulate scan result
coroutine.yield({
host = target.host,
port = target.port,
open = (target.port == 22 or target.port == 443),
})
end
end)
end
local function filter_open(source)
return coroutine.wrap(function()
for result in source do
if result.open then
coroutine.yield(result)
end
end
end)
end
local targets = {
{ host = "10.50.1.40", port = 22 },
{ host = "10.50.1.40", port = 80 },
{ host = "10.50.1.40", port = 443 },
}
for result in filter_open(port_scanner(targets)) do
print(result.host .. ":" .. result.port .. " OPEN")
end