coroutineLuaLuau

A thread is a coroutine, not an operating system thread

Coroutines are interleaving (async) primitives and aren't for true parallelism (os multithreading). Coroutines can't use more than one CPU core.

Hosts that want real OS multithreading should expose an API that runs separate Luwu VMs per OS thread and facilitate communication via shared memory or message passing.

The coroutine library creates coroutines, resumes and yields them, and checks their state.

A coroutine is a function that can pause itself. It runs until it calls coroutine.yield and hands values back to the code that resumed it. The next coroutine.resume continues from the same point with every local variable intact. Each coroutine has a separate stack (it's a thread value) and only one coroutine runs at a time.

Coroutines turn a loop into a generator that produces one value per call. Hosts use them to run scripts that wait: a task scheduler resumes a coroutine once the thing it waited for is ready.

Summary

Creating and closing

coroutine.createLua
Creates a coroutine that runs a function
coroutine.wrapLua
Creates a coroutine and returns a function that resumes it
coroutine.closeLuau
Closes a suspended or dead coroutine

Resuming and yielding

coroutine.resumeLua
Starts or continues a coroutine
coroutine.yieldLua
Pauses the running coroutine and hands values to its resumer

Checking state

coroutine.runningLua
Returns the coroutine that's running
coroutine.statusLua
Returns the status of a coroutine
coroutine.isyieldableLuau
Checks whether the running code can yield

Creating and closing

coroutine.createLua

function coroutine.create<A..., R...>(f: (A...) -> R...): thread

Returns a new coroutine that runs f when it's first resumed. The coroutine starts out "suspended" and the arguments of the first coroutine.resume become the arguments of f.

coroutine.wrapLua

function coroutine.wrap<A..., R...>(f: (A...) -> R...): (A...) -> R...

Creates a coroutine and returns a function that resumes it. Calling the function passes its arguments to the coroutine and returns what the coroutine yields or returns. Unlike coroutine.resume, there's no status boolean. The call raises any error from inside the coroutine again and puts the location of the call in front of a string message.

Input
const next_id = coroutine.wrap(function()
    local id = 0
    while true do
        id += 1
        coroutine.yield(id)
    end
end)
print(next_id())
print(next_id())
print(next_id())
Output
1
2
3

coroutine.closeLuau

function coroutine.close(co: thread): (boolean, unknown?)

Closes co and empties its stack. The coroutine becomes "dead" and can't be resumed. Only a suspended or dead coroutine can be closed: closing a running or normal one raises an error. close returns true, or false and the error object when the coroutine had died from an error.

Luau's coroutine.finally isn't part of Luwu's coroutine library.

Resuming and yielding

coroutine.resumeLua

function coroutine.resume<A..., R...>(co: thread, ...: A...): (boolean, R...)

Starts or continues co and passes the remaining arguments to it. They become the arguments of the coroutine's function on the first resume and the results of coroutine.yield after that. When the coroutine yields or returns, resume returns true followed by those values. When it raises an error, resume returns false and the error object like pcall does. Resuming a coroutine that isn't suspended returns false and a message such as "cannot resume dead coroutine".

Input
const doubler = coroutine.create(function(first: number)
    local value = first
    while true do
        value = coroutine.yield(value * 2)
    end
end)
const _, a = coroutine.resume(doubler, 5)
const _, b = coroutine.resume(doubler, 21)
print(`{a} {b}`)
print(coroutine.status(doubler))
Output
10 42
suspended

coroutine.yieldLua

function coroutine.yield<A..., R...>(...: A...): R...

Pauses the running coroutine and passes its arguments to the code that resumed it. The coroutine becomes "suspended". When it's resumed again, yield returns the arguments passed to that resume.

Checking state

coroutine.runningLua

function coroutine.running(): thread?

Returns the coroutine that's running or nil when the main thread is running. Some hosts run every script inside a coroutine and never use the main thread for script code. Luwu's type definitions declare the result as thread without the nil.

coroutine.statusLua

function coroutine.status(co: thread): "running" | "suspended" | "normal" | "dead"

coroutine.isyieldableLuau

function coroutine.isyieldable(): boolean

Returns whether the running code can call coroutine.yield. The main thread can't yield. Inside a coroutine, yielding also fails from a metamethod like __index and from a callback that a C function calls, like table.sort's comparison function. pcall and xpcall are the exception: code inside them can yield.