Waitgroups in Lua

Our example demonstrates how to wait for multiple threads to finish using a simple counter mechanism in Lua. While Lua doesn’t have built-in concurrency primitives like WaitGroups, we can simulate similar behavior using a combination of coroutines and a counter.

local threads = require("llthreads2")

-- This is the function we'll run in every thread
local function worker(id)
    print(string.format("Worker %d starting", id))
    -- Sleep to simulate an expensive task
    os.execute("sleep 1")
    print(string.format("Worker %d done", id))
end

-- Main function
local function main()
    local counter = 0
    local threads_list = {}

    -- Launch several threads and increment the counter for each
    for i = 1, 5 do
        counter = counter + 1
        local thread = threads.new(function()
            worker(i)
            return "done"
        end)
        thread:start()
        table.insert(threads_list, thread)
    end

    -- Wait for all threads to finish
    for _, thread in ipairs(threads_list) do
        local result = thread:join()
        if result == "done" then
            counter = counter - 1
        end
    end

    -- Ensure all threads have completed
    assert(counter == 0, "Not all threads completed")
end

main()

To run this program, you’ll need to install the luarocks package manager and the llthreads2 library:

$ luarocks install llthreads2
$ lua waitgroups.lua
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done

The order of workers starting up and finishing is likely to be different for each invocation.

In this Lua implementation:

  1. We use the llthreads2 library to create and manage threads, which is similar to goroutines in concept.

  2. The worker function simulates an expensive task by sleeping for 1 second.

  3. In the main function, we create a counter to keep track of active threads.

  4. We launch 5 threads, incrementing the counter for each.

  5. Each thread runs the worker function and returns “done” when completed.

  6. We wait for all threads to finish by joining them and decrementing the counter when each thread completes.

  7. Finally, we assert that the counter is zero, ensuring all threads have completed.

Note that this approach doesn’t provide a straightforward way to propagate errors from workers. For more advanced use cases in Lua, you might need to implement a more sophisticated error handling mechanism or use additional libraries for concurrent programming.