coroutine
Cooperative threads of Lua 5.1 / LuaJIT 2.1: a coroutine has its own stack and runs until it yields or finishes. Nothing is removed by the client; to pause over several frames, use xiao.timer.spawn and wait.
At a glance
Functions
coroutine.create(f): thread
coroutine.create(f: function): thread
Creates a coroutine that will run f. It starts suspended.
Returns: the new coroutine.
Errors: bad argument #1 to 'create' (function expected, got number).
coroutine.resume(co, ...): boolean, any
coroutine.resume(co: thread, ...: any): boolean, ...any
Starts or continues co. The arguments are passed to the body on the first resume, then become the results of coroutine.yield.
Returns: true and the values passed to yield (or returned by the body), or false and the error value; after an error the coroutine is dead. A dead coroutine gives false, "cannot resume dead coroutine", a running or normal one false, "cannot resume running coroutine".
Errors: bad argument #1 to 'resume' (coroutine expected).
local co = coroutine.create(function(a, b)
local c = coroutine.yield(a + b)
return c * 2
end)
print(coroutine.resume(co, 1, 2))
print(coroutine.resume(co, 10))
coroutine.yield(...): any
coroutine.yield(...: any): ...any
Suspends the running coroutine.
Returns: the arguments of the next resume.
Errors: attempt to yield across C-call boundary outside a coroutine, or inside a function called from C, such as a table.sort comparator or a string.gsub replacement function. LuaJIT can yield across pcall, xpcall and metamethods.
coroutine.status(co): string
coroutine.status(co: thread): string
Returns:
Errors: bad argument #1 to 'status' (coroutine expected).
coroutine.wrap(f): function
coroutine.wrap(f: function): function(...: any): ...any
Creates a coroutine and returns a function that resumes it at each call.
Returns: a function. Its arguments go to resume, its results are the values yielded or returned. Errors, including cannot resume dead coroutine, are raised in the caller instead of being returned.
local gen = coroutine.wrap(function()
for i = 1, 3 do coroutine.yield(i) end
end)
print(gen(), gen(), gen())
coroutine.running(): thread | nil
coroutine.running(): thread | nil
Returns: the running coroutine, or nil in the main thread (Lua 5.1 form, no second value).
coroutine.isyieldable(): boolean
coroutine.isyieldable(): boolean
Returns: true when coroutine.yield can be called here: inside a coroutine and not inside a function called from C. false in the main chunk, in a frame callback or in a hook.
print(coroutine.isyieldable())
coroutine.wrap(function() print(coroutine.isyieldable()) end)()
Coroutines and xiao.timer New
xiao.timer.spawn runs a function as a coroutine driven by the client clock, and xiao.timer.wait pauses it.
spawn(fn, ...)resumes the coroutine at once, in the caller, until its firstwait, its firstcoroutine.yield()or its end. Later steps run with the timers, step 6 of the order of a frame, in the 100 ms frame budget.- A plain
coroutine.yield()inside a spawned coroutine waits one frame. The values given toyieldare ignored, andyieldreturns the seconds elapsed since it yielded, likewait. waitworks only in the coroutine created byspawnitself. In the main chunk, in a callback, or in a coroutine made withcoroutine.createorcoroutine.wrap, even inside a spawned one, it raisesxiao.timer.wait must be called inside xiao.timer.spawn.- A coroutine of your own inside a spawned one behaves as usual: its
yieldreturns to its ownresume, not to the timer. - An error in a spawned coroutine removes it and prints
timer <id> coroutine removed:and a traceback. A loop that never yields hits the frame time limit and the VM is cleared. - Do not resume a spawned coroutine yourself (through
coroutine.running()): the timer still resumes it at its due time. xiao.timer.cancel(id)stops a spawned coroutine, it is never resumed again.
Packet hooks and cmd hooks
xiao.timer.spawn raises xiao.timer.spawn is not available inside a packet hook or a cmd hook. Plain coroutines work there, but everything they run counts in the hook budget. See Execution contexts.
xiao.timer.spawn(function()
for step = 1, 3 do
print("step", step)
coroutine.yield()
end
end)
Complete example
Spread a long job over several frames, first with a hand-driven coroutine in a paint listener (see xiao.paint), then with xiao.timer.spawn.
local worker = coroutine.wrap(function()
for step = 1, 3 do
print("paint step", step)
coroutine.yield()
end
end)
xiao.paint.add("worker", function()
if not pcall(worker) then
xiao.paint.remove("worker")
end
end)
local job = xiao.timer.spawn(function(label)
local total = 0
for chunk = 1, 5 do
for i = 1, 10000 do total = total + i * chunk end
local waited = xiao.timer.wait(0.5)
print(label, chunk, ("%.2f s"):format(waited))
end
print(label, "done", total)
end, "job")
print("spawned", job)