Select in Lua

Lua doesn’t have built-in concurrency primitives like Go’s goroutines and channels. However, we can simulate similar behavior using Lua’s coroutines and a custom implementation of channels. We’ll use the luaposix library for sleep functionality.

local posix = require "posix"

-- Simulate a channel
local function makeChannel()
    local channel = {}
    local queue = {}
    local waitingThreads = {}

    function channel.send(value)
        if #waitingThreads > 0 then
            coroutine.resume(table.remove(waitingThreads), value)
        else
            table.insert(queue, value)
        end
    end

    function channel.receive()
        if #queue > 0 then
            return table.remove(queue, 1)
        else
            table.insert(waitingThreads, coroutine.running())
            return coroutine.yield()
        end
    end

    return channel
end

-- Create two channels
local c1 = makeChannel()
local c2 = makeChannel()

-- Simulate goroutines with coroutines
local function asyncSend(channel, value, delay)
    return coroutine.create(function()
        posix.sleep(delay)
        channel.send(value)
    end)
end

-- Start our "goroutines"
local t1 = asyncSend(c1, "one", 1)
local t2 = asyncSend(c2, "two", 2)

-- Simulate select
local function select(cases)
    while true do
        for _, case in ipairs(cases) do
            local status, value = coroutine.resume(case.thread)
            if status and value ~= nil then
                return case.handler(value)
            end
        end
    end
end

-- Main loop
for i = 1, 2 do
    select({
        {thread = coroutine.create(function() return c1.receive() end),
         handler = function(msg) print("received", msg) end},
        {thread = coroutine.create(function() return c2.receive() end),
         handler = function(msg) print("received", msg) end}
    })
end

-- Resume our "goroutines"
coroutine.resume(t1)
coroutine.resume(t2)

This Lua code simulates the behavior of the original example. Here’s how it works:

  1. We define a makeChannel function to simulate Go’s channels. It uses a queue to store values and a list of waiting threads.

  2. We create two channels, c1 and c2.

  3. The asyncSend function simulates a goroutine that sends a value after a delay. It returns a coroutine.

  4. We start two “goroutines” that will send “one” after 1 second and “two” after 2 seconds.

  5. The select function simulates Go’s select statement. It tries to receive from each channel and calls the appropriate handler when a value is available.

  6. In the main loop, we use our select function to await values from both channels simultaneously, printing each one as it arrives.

  7. Finally, we resume our “goroutines” to start the sending process.

To run this program, save it as select.lua and execute it with Lua:

$ lua select.lua
received one
received two

Note that the total execution time will be about 2 seconds, as both the 1 and 2 second delays execute concurrently.

This example demonstrates how to simulate Go’s select and channel behavior in Lua using coroutines. While it’s not as elegant or efficient as Go’s built-in concurrency features, it provides a similar functionality.