summaryrefslogtreecommitdiff
path: root/indra/newview/scripts/lua/fiber.lua
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/scripts/lua/fiber.lua')
-rw-r--r--indra/newview/scripts/lua/fiber.lua106
1 files changed, 70 insertions, 36 deletions
diff --git a/indra/newview/scripts/lua/fiber.lua b/indra/newview/scripts/lua/fiber.lua
index 8ed99f12b7..7dc67f510c 100644
--- a/indra/newview/scripts/lua/fiber.lua
+++ b/indra/newview/scripts/lua/fiber.lua
@@ -178,36 +178,6 @@ function fiber.wake(co)
-- but don't yet resume it: that happens next time we reach yield()
end
--- Run fibers until all but main have terminated: return nil.
--- Or until configured idle() callback returns x ~= nil: return x.
-function fiber.run()
- -- A fiber calling run() is not also doing other useful work. Remove the
- -- calling fiber from the ready list. Otherwise yield() would keep seeing
- -- that our caller is ready and return to us, instead of realizing that
- -- all coroutines are waiting and call idle(). But don't say we're
- -- waiting, either, because then when all other fibers have terminated
- -- we'd call idle() forever waiting for something to make us ready again.
- local i = table.find(ready, fiber.running())
- if i then
- table.remove(ready, i)
- end
- local others, idle_done
- repeat
- debug('%s calling fiber.run() calling yield()', fiber.get_name())
- others, idle_done = fiber.yield()
- debug("%s fiber.run()'s yield() returned %s, %s", fiber.get_name(),
- tostring(others), tostring(idle_done))
- until (not others)
- debug('%s fiber.run() done', fiber.get_name())
- -- For whatever it's worth, put our own fiber back in the ready list.
- table.insert(ready, fiber.running())
- -- Once there are no more waiting fibers, and the only ready fiber is
- -- us, return to caller. All previously-launched fibers are done. Possibly
- -- the chunk is done, or the chunk may decide to launch a new batch of
- -- fibers.
- return idle_done
-end
-
-- pop and return the next not-dead fiber in the ready list, or nil if none remain
local function live_ready_iter()
-- don't write
@@ -237,16 +207,24 @@ local function prune_waiting()
end
end
--- Give other ready fibers a chance to run, leaving this one ready, returning
--- after a cycle. Returns:
--- * true, nil if there remain other live fibers, whether ready or waiting
+-- Run other ready fibers, leaving this one ready, returning after a cycle.
+-- Returns:
+-- * true, nil if there remain other live fibers, whether ready or waiting,
+-- but it's our turn to run
-- * false, nil if this is the only remaining fiber
--- * nil, x if configured idle() callback returned non-nil x
-function fiber.yield()
+-- * nil, x if configured idle() callback returns non-nil x
+local function scheduler()
+ -- scheduler() is asymmetric because Lua distinguishes the main thread
+ -- from other coroutines. The main thread can't yield; it can only resume
+ -- other coroutines. So although an arbitrary coroutine could resume still
+ -- other arbitrary coroutines, it could NOT resume the main thread because
+ -- the main thread can't yield. Therefore, scheduler() delegates its real
+ -- processing to the main thread. If called from a coroutine, pass control
+ -- back to the main thread.
if coroutine.running() then
-- seize the opportunity to make sure the viewer isn't shutting down
-- check_stop()
- -- this is a real coroutine, yield normally to main or whoever
+ -- this is a real coroutine, yield normally to main thread
coroutine.yield()
-- main certainly still exists
return true
@@ -294,4 +272,60 @@ function fiber.yield()
until false
end
+-- Let other fibers run. This is useful in either of two cases:
+-- * fiber.wait() calls this to run other fibers while this one is waiting.
+-- fiber.yield() (and therefore fiber.wait()) works from the main thread as
+-- well as from explicitly-launched fibers, without the caller having to
+-- care.
+-- * A long-running fiber that doesn't often call fiber.wait() should sprinkle
+-- in fiber.yield() calls to interleave processing on other fibers.
+function fiber.yield()
+ -- The difference between this and fiber.run() is that fiber.yield()
+ -- assumes its caller has work to do. yield() returns to its caller as
+ -- soon as scheduler() pops this fiber from the ready list. fiber.run()
+ -- continues looping until all other fibers have terminated, or the
+ -- set_idle() callback tells it to stop.
+ local others, idle_done = scheduler()
+ -- scheduler() returns either if we're ready, or if idle_done ~= nil.
+ if idle_done ~= nil then
+ -- Returning normally from yield() means the caller can carry on with
+ -- its pending work. But in this case scheduler() returned because the
+ -- configured set_idle() function interrupted it -- not because we're
+ -- actually ready. Don't return normally.
+ error('fiber.set_idle() interrupted yield() with: ' .. tostring(idle_done))
+ end
+ -- We're ready! Just return to caller. In this situation we don't care
+ -- whether there are other ready fibers.
+end
+
+-- Run fibers until all but main have terminated: return nil.
+-- Or until configured idle() callback returns x ~= nil: return x.
+function fiber.run()
+ -- A fiber calling run() is not also doing other useful work. Remove the
+ -- calling fiber from the ready list. Otherwise yield() would keep seeing
+ -- that our caller is ready and return to us, instead of realizing that
+ -- all coroutines are waiting and call idle(). But don't say we're
+ -- waiting, either, because then when all other fibers have terminated
+ -- we'd call idle() forever waiting for something to make us ready again.
+ local i = table.find(ready, fiber.running())
+ if i then
+ table.remove(ready, i)
+ end
+ local others, idle_done
+ repeat
+ debug('%s calling fiber.run() calling scheduler()', fiber.get_name())
+ others, idle_done = scheduler()
+ debug("%s fiber.run()'s scheduler() returned %s, %s", fiber.get_name(),
+ tostring(others), tostring(idle_done))
+ until (not others)
+ debug('%s fiber.run() done', fiber.get_name())
+ -- For whatever it's worth, put our own fiber back in the ready list.
+ table.insert(ready, fiber.running())
+ -- Once there are no more waiting fibers, and the only ready fiber is
+ -- us, return to caller. All previously-launched fibers are done. Possibly
+ -- the chunk is done, or the chunk may decide to launch a new batch of
+ -- fibers.
+ return idle_done
+end
+
return fiber