summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/llluamanager.cpp15
-rw-r--r--indra/newview/scripts/lua/require/timers.lua34
2 files changed, 36 insertions, 13 deletions
diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp
index 7014c59e4e..22b51d7b72 100644
--- a/indra/newview/llluamanager.cpp
+++ b/indra/newview/llluamanager.cpp
@@ -53,10 +53,11 @@ std::map<std::string, std::string> LLLUAmanager::sScriptNames;
lua_function(sleep, "sleep(seconds): pause the running coroutine")
{
+ lua_checkdelta(L, -1);
F32 seconds = lua_tonumber(L, -1);
lua_pop(L, 1);
llcoro::suspendUntilTimeout(seconds);
- lluau::set_interrupts_counter(L, 0);
+ LuaState::getParent(L).set_interrupts_counter(0);
return 0;
};
@@ -66,7 +67,7 @@ std::string lua_print_msg(lua_State* L, const std::string_view& level)
{
// On top of existing Lua arguments, we're going to push tostring() and
// duplicate each existing stack entry so we can stringize each one.
- luaL_checkstack(L, 2, nullptr);
+ lluau_checkstack(L, 2);
luaL_where(L, 1);
// start with the 'where' info at the top of the stack
std::ostringstream out;
@@ -125,6 +126,7 @@ lua_function(print_warning, "print_warning(args...): WARNING level logging")
lua_function(post_on, "post_on(pumpname, data): post specified data to specified LLEventPump")
{
+ lua_checkdelta(L, -2);
std::string pumpname{ lua_tostdstring(L, 1) };
LLSD data{ lua_tollsd(L, 2) };
lua_pop(L, 2);
@@ -139,7 +141,8 @@ lua_function(get_event_pumps,
"Events posted to replypump are queued for get_event_next().\n"
"post_on(commandpump, ...) to engage LLEventAPI operations (see helpleap()).")
{
- luaL_checkstack(L, 2, nullptr);
+ lua_checkdelta(L, 2);
+ lluau_checkstack(L, 2);
auto& listener{ LuaState::obtainListener(L) };
// return the reply pump name and the command pump name on caller's lua_State
lua_pushstdstring(L, listener.getReplyName());
@@ -153,12 +156,13 @@ lua_function(get_event_next,
"is returned by get_event_pumps(). Blocks the calling chunk until an\n"
"event becomes available.")
{
- luaL_checkstack(L, 2, nullptr);
+ lua_checkdelta(L, 2);
+ lluau_checkstack(L, 2);
auto& listener{ LuaState::obtainListener(L) };
const auto& [pump, data]{ listener.getNext() };
lua_pushstdstring(L, pump);
lua_pushllsd(L, data);
- lluau::set_interrupts_counter(L, 0);
+ LuaState::getParent(L).set_interrupts_counter(0);
return 2;
}
@@ -271,6 +275,7 @@ std::string read_file(const std::string &name)
lua_function(require, "require(module_name) : load module_name.lua from known places")
{
+ lua_checkdelta(L);
std::string name = lua_tostdstring(L, 1);
lua_pop(L, 1);
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