summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-03-11 16:49:03 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-03-11 16:49:03 -0400
commitc371096fc3320f580fd271dad09c852f2e3d5f78 (patch)
tree44d38d4a4a175e09eb438b63b11c06dbb98fa7d2 /indra
parent42de5594cd4ebde605e6f93a48a7e7c9ab60ace4 (diff)
Add coro.lua to aggregate created coroutines.
Diffstat (limited to 'indra')
-rw-r--r--indra/newview/scripts/lua/coro.lua57
1 files changed, 57 insertions, 0 deletions
diff --git a/indra/newview/scripts/lua/coro.lua b/indra/newview/scripts/lua/coro.lua
new file mode 100644
index 0000000000..8f868f5a48
--- /dev/null
+++ b/indra/newview/scripts/lua/coro.lua
@@ -0,0 +1,57 @@
+-- Manage Lua coroutines
+
+local coro = {}
+
+coro._coros = {}
+
+-- Launch a Lua coroutine: create and resume.
+-- Returns:
+-- new coroutine
+-- bool ok
+-- if not ok: error message
+-- if ok: values yielded or returned
+function coro.launch(func, ...)
+ local co = coroutine.create(func)
+ table.insert(coro._coros, co)
+ return co, coroutine.resume(co, ...)
+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
+ co.resume()
+ 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