1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
-- Manage Lua coroutines
local coro = {}
coro._coros = {}
-- Launch a Lua coroutine: create and resume.
-- Returns: new coroutine, values yielded or returned from initial resume()
-- If initial resume() encountered an error, propagates the error.
function coro.launch(func, ...)
local co = coroutine.create(func)
table.insert(coro._coros, co)
return co, coro.resume(co, ...)
end
-- resume() wrapper to propagate errors
function coro.resume(co, ...)
-- if there's an idiom other than table.pack() to assign an arbitrary
-- number of return values, I don't yet know it
local ok_result = table.pack(coroutine.resume(co, ...))
if not ok_result[1] then
-- if [1] is false, then [2] is the error message
error(ok_result[2])
end
-- ok is true, whew, just return the rest of the values
return table.unpack(ok_result, 2)
end
-- yield to other coroutines even if you don't know whether you're in a
-- created coroutine or the main coroutine
function coro.yield(...)
if coroutine.running() then
-- this is a real coroutine, yield normally
return coroutine.yield(...)
else
-- This is the main coroutine: coroutine.yield() doesn't work.
-- But we can take a spin through previously-launched coroutines.
-- Walk a copy of coro._coros in case any of these coroutines launches
-- another: next() forbids creating new entries during traversal.
for co in coro._live_coros_iter, table.clone(coro._coros) do
coro.resume(co)
end
end
end
-- Walk coro._coros table, returning running or suspended coroutines.
-- Once a coroutine becomes dead, remove it from _coros and don't return it.
function coro._live_coros()
return coro._live_coros_iter, coro._coros
end
-- iterator function for _live_coros()
function coro._live_coros_iter(t, idx)
local k, co = next(t, idx)
while k and coroutine.status(co) == 'dead' do
-- t[k] = nil
-- See coro.yield(): sometimes we traverse a copy of _coros, but if we
-- discover a dead coroutine in that copy, delete it from _coros
-- anyway. Deleting it from a temporary copy does nothing.
coro._coros[k] = nil
coroutine.close(co)
k, co = next(t, k)
end
return co
end
return coro
|