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:

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:

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