summaryrefslogtreecommitdiff
path: root/indra/newview/scripts
diff options
context:
space:
mode:
authornat-goodspeed <nat@lindenlab.com>2024-08-21 11:52:55 -0400
committerGitHub <noreply@github.com>2024-08-21 11:52:55 -0400
commit7ee93ea34dfc41640f852279888bbee0cafcecbe (patch)
tree371a14729aa2d2ffa2c62fc55fd5d203752eb0d9 /indra/newview/scripts
parent8324ef8edf68c074f4d30322d37b091b3ea10539 (diff)
parentf2abd050bce3c4132f12d962fc6436d1f06666bd (diff)
Merge pull request #2373 from secondlife/viewer-lua-2237
Fix for #2237: intermittent Lua data stack overflow.
Diffstat (limited to 'indra/newview/scripts')
-rw-r--r--indra/newview/scripts/lua/require/timers.lua34
1 files changed, 26 insertions, 8 deletions
diff --git a/indra/newview/scripts/lua/require/timers.lua b/indra/newview/scripts/lua/require/timers.lua
index e4938078dc..ab1615ffbf 100644
--- a/indra/newview/scripts/lua/require/timers.lua
+++ b/indra/newview/scripts/lua/require/timers.lua
@@ -34,35 +34,53 @@ function timers.Timer:new(delay, callback, iterate)
callback = callback or function() obj:tick() end
- local first = true
+ local calls = 0
if iterate then
+ -- With iterative timers, beware of running a timer callback which
+ -- performs async actions lasting longer than the timer interval. The
+ -- lengthy callback suspends, allowing leap to retrieve the next
+ -- event, which is a timer tick. leap calls a new instance of the
+ -- callback, even though the previous callback call is still
+ -- suspended... etc. 'in_callback' defends against that recursive
+ -- case. Rather than re-enter the suspended callback, drop the
+ -- too-soon timer event. (We could count the too-soon timer events and
+ -- iterate calling the callback, but it's a bathtub problem: the
+ -- callback could end up getting farther and farther behind.)
+ local in_callback = false
obj.id = leap.eventstream(
'Timers',
{op='scheduleEvery', every=delay},
function (event)
local reqid = event.reqid
- if first then
- first = false
+ calls += 1
+ if calls == 1 then
dbg('timer(%s) first callback', reqid)
-- discard the first (immediate) response: don't call callback
return nil
else
- dbg('timer(%s) nth callback', reqid)
- return callback(event)
+ if in_callback then
+ dbg('dropping timer(%s) callback %d', reqid, calls)
+ else
+ dbg('timer(%s) callback %d', reqid, calls)
+ in_callback = true
+ local ret = callback(event)
+ in_callback = false
+ return ret
+ end
end
end
).reqid
- else
+ else -- (not iterate)
obj.id = leap.eventstream(
'Timers',
{op='scheduleAfter', after=delay},
function (event)
+ calls += 1
-- Arrange to return nil the first time, true the second. This
-- callback is called immediately with the response to
-- 'scheduleAfter', and if we immediately returned true, we'd
-- be done, and the subsequent timer event would be discarded.
- if first then
- first = false
+ if calls == 1 then
-- Caller doesn't expect an immediate callback.
return nil
else