diff options
author | nat-goodspeed <nat@lindenlab.com> | 2024-08-21 11:52:55 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-21 11:52:55 -0400 |
commit | 7ee93ea34dfc41640f852279888bbee0cafcecbe (patch) | |
tree | 371a14729aa2d2ffa2c62fc55fd5d203752eb0d9 /indra/newview/scripts | |
parent | 8324ef8edf68c074f4d30322d37b091b3ea10539 (diff) | |
parent | f2abd050bce3c4132f12d962fc6436d1f06666bd (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.lua | 34 |
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 |