Lua runner with rich builtin stdlib
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
a/lua/demo/coroutines-demo.lua

105 lines
4.0 KiB

-- Proof-of-concept: how Lua coroutines interact with the tokio runtime, and
-- how the async os.sleep behaves in each setting.
--
-- Run with: cargo run -- lua/coroutines-demo.lua
local function banner(s)
print("\n=== " .. s .. " ===")
end
----------------------------------------------------------------------
banner("1. Pure Lua coroutines (no async involved)")
----------------------------------------------------------------------
-- Plain cooperative coroutines work exactly as in stock Lua: this is pure VM
-- machinery and never touches tokio.
local function counter(n)
for i = 1, n do
coroutine.yield(i * i)
end
return "done"
end
local co = coroutine.wrap(counter)
print("squares:", co(3), co(3), co(3)) -- 1 4 9
local raw = coroutine.create(counter)
print("status fresh:", coroutine.status(raw)) -- suspended
coroutine.resume(raw, 1)
coroutine.resume(raw, 1) -- runs to `return`
print("status after return:", coroutine.status(raw)) -- dead
----------------------------------------------------------------------
banner("2. Async os.sleep at the top level")
----------------------------------------------------------------------
-- The main chunk is itself run by mlua's async executor (exec_async), so an
-- async call here is driven correctly and really suspends on the tokio timer.
local t0 = os.microtime()
os.sleep(0.10)
print(string.format("slept ~0.10s, measured %.3fs", os.microtime() - t0))
----------------------------------------------------------------------
banner("3. GOTCHA: async os.sleep inside a *manually resumed* coroutine")
----------------------------------------------------------------------
-- mlua implements async functions by yielding a private sentinel to whatever is
-- driving the coroutine. mlua's own executor understands it; a plain
-- coroutine.resume / coroutine.wrap does NOT. So driving an async call yourself
-- does not actually wait -- the sleep is not performed by your resume loop.
local sleeper = coroutine.wrap(function()
os.sleep(0.50)
return "awoke"
end)
local t1 = os.microtime()
local first = sleeper() -- returns immediately with the sentinel
print(
string.format("first resume returned %s after only %.3fs (did NOT wait 0.5s)", tostring(first), os.microtime() - t1)
)
print("--> lesson: don't hand-drive coroutines that call async stdlib functions.")
----------------------------------------------------------------------
banner("4. Real concurrency: task.join drives coroutines on tokio")
----------------------------------------------------------------------
-- task.join runs each function as its own coroutine and lets the tokio reactor
-- interleave them. Three tasks that each sleep concurrently finish in ~the
-- longest single sleep, not the sum -- proof the sleeps overlap on one thread.
local function worker(name, secs)
return function()
print(string.format(" [%s] start", name))
os.sleep(secs)
print(string.format(" [%s] woke after %.2fs", name, secs))
return name .. ":" .. secs
end
end
local t2 = os.microtime()
local a, b, c = task.join(worker("slow", 0.30), worker("med", 0.20), worker("fast", 0.10))
local elapsed = os.microtime() - t2
print(string.format("results: %s, %s, %s", a, b, c))
print(string.format("wall time: %.3fs", elapsed))
print(
string.format(
"sequential would have been ~0.60s; concurrent ~0.30s => %s",
elapsed < 0.45 and "CONCURRENT (overlapped on tokio)" or "serialized?!"
)
)
----------------------------------------------------------------------
banner("5. Nested + return values")
----------------------------------------------------------------------
-- task.join itself yields, so it composes: a joined task can join again.
local outer = task.join(function()
local x, y = task.join(function()
os.sleep(0.05)
return 21
end, function()
os.sleep(0.05)
return 21
end)
return x + y
end, function()
os.sleep(0.10)
return "sibling"
end)
print("nested join result:", outer) -- 42
print("\nAll demos finished.")