diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-02-07 22:24:52 -0500 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-02-07 22:24:52 -0500 |
commit | 4ecfd706bce5c45556eaf2833f89fd9f2f5b07a9 (patch) | |
tree | db24f93e18019b41fbab5fe7f76cb0009c212933 | |
parent | 382ec7b2269546605da7ba0a44654deaadf3ff3c (diff) |
Add LLLUAmanager::startScriptFile(), startScriptLine() functions.
Break out for LLLUAmanager consumers the promise/future semantics of
waitScriptFile() and waitScriptLine(). startScriptMumble() uses
runScriptMumble() to launch a coroutine to run the specified Lua script or
chunk, providing an internal adapter callback to set a promise on completion.
It then returns to its caller the future obtained from that promise. This
allows a caller to call startScriptMumble(), run in parallel with the Lua
coroutine for a while and then call get() on the returned future to wait for
results.
waitScriptMumble() is then trivially implemented using startScriptMumble().
Fix runScriptLine()'s logic to abbreviate the passed Lua chunk for use as the
description. We were erroneously assigning back through a string_view of the
parameter, which overwrote a temporary string in the argument list.
With Lua 5.4, listen_events() tried to discover the main "thread" (Lua
coroutine) associated with the current lua_State so we could call async
callbacks on that thread. Luau doesn't seem to provide that feature, so run
callbacks on whichever thread calls listen_events().
Reinstate original multi-argument lua_print_msg(), tweaked to avoid the Lua
5.4 lua_rotate() function, which is missing from Luau.
-rw-r--r-- | indra/newview/llluamanager.cpp | 107 | ||||
-rw-r--r-- | indra/newview/llluamanager.h | 25 |
2 files changed, 76 insertions, 56 deletions
diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 44c7a6ba3b..fac66003a1 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -64,13 +64,13 @@ lua_function(sleep) return 0; }; -/* // This function consumes ALL Lua stack arguments and returns concatenated // message string -std::string lua_print_msg_args(lua_State* L, const std::string_view& level) +std::string lua_print_msg(lua_State* L, const std::string_view& level) { - // On top of existing Lua arguments, push 'where' info - luaL_checkstack(L, 1, nullptr); + // 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); luaL_where(L, 1); // start with the 'where' info at the top of the stack std::ostringstream out; @@ -79,7 +79,7 @@ std::string lua_print_msg_args(lua_State* L, const std::string_view& level) const char* sep = ""; // 'where' info ends with ": " // now iterate over arbitrary args, calling Lua tostring() on each and // concatenating with separators - for (int p = 1; p <= lua_gettop(L); ++p) + for (int p = 1, top = lua_gettop(L); p <= top; ++p) { out << sep; sep = " "; @@ -87,15 +87,15 @@ std::string lua_print_msg_args(lua_State* L, const std::string_view& level) // lua_tostring()! lua_getglobal(L, "tostring"); // Now the stack is arguments 1 .. N, plus tostring(). - // Rotate downwards, producing stack args 2 .. N, tostring(), arg1. - lua_rotate(L, 1, -1); - // pop tostring() and arg1, pushing tostring(arg1) + // Push a copy of the argument at index p. + lua_pushvalue(L, p); + // pop tostring() and arg-p, pushing tostring(arg-p) // (ignore potential error code from lua_pcall() because, if there was // an error, we expect the stack top to be an error message -- which // we'll print) lua_pcall(L, 1, 1, 0); - // stack now holds args 2 .. N, tostring(arg1) out << lua_tostring(L, -1); + lua_pop(L, 1); } // pop everything lua_settop(L, 0); @@ -105,20 +105,6 @@ std::string lua_print_msg_args(lua_State* L, const std::string_view& level) LLEventPumps::instance().obtain("lua output").post(stringize(level, ": ", msg)); return msg; } -*/ - -std::string lua_print_msg(lua_State *L, const std::string_view &level) -{ - lua_getglobal(L, "tostring"); - - lua_pushvalue(L, -1); /* function to be called */ - lua_pushvalue(L, 1); /* value to print */ - lua_call(L, 1, 1); - std::string msg = lua_tostring(L, -1); - - LLEventPumps::instance().obtain("lua output").post(stringize(level, ": ", msg)); - return msg; -} lua_function(print_debug) { @@ -187,19 +173,24 @@ lua_function(listen_events) } luaL_checkstack(L, 2, nullptr); +/*==========================================================================*| // Get the lua_State* for the main thread of this state, in case we were // called from a coroutine thread. We're going to make callbacks into Lua // code, and we want to do it on the main thread rather than a (possibly // suspended) coroutine thread. // Registry table is at pseudo-index LUA_REGISTRYINDEX // Main thread is at registry key LUA_RIDX_MAINTHREAD - auto regtype {lua_rawgeti(L, LUA_REGISTRYINDEX, 1 /*LUA_RIDX_MAINTHREAD*/)}; + auto regtype {lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD)}; // Not finding the main thread at the documented place isn't a user error, // it's a Problem llassert_always(regtype == LUA_TTHREAD); lua_State* mainthread{ lua_tothread(L, -1) }; // pop the main thread lua_pop(L, 1); +|*==========================================================================*/ + // Luau is based on Lua 5.1, and Lua 5.1 apparently provides no way to get + // back to the main thread from a coroutine thread? + lua_State* mainthread{ L }; luaL_checkstack(mainthread, 1, nullptr); LuaListener::ptr_t listener; @@ -284,14 +275,21 @@ void LLLUAmanager::runScriptFile(const std::string& filename, script_result_fn c runScriptFile(L, filename, cb); } -std::pair<int, LLSD> LLLUAmanager::waitScriptFile(LuaState& L, const std::string& filename) +LLCoros::Future<std::pair<int, LLSD>> +LLLUAmanager::startScriptFile(LuaState& L, const std::string& filename) { - LLCoros::Promise<std::pair<int, LLSD>> promise; - auto future{ LLCoros::getFuture(promise) }; + // Despite returning from startScriptFile(), we need this Promise to + // remain alive until the callback has fired. + auto promise{ std::make_shared<LLCoros::Promise<std::pair<int, LLSD>>>() }; runScriptFile(L, filename, - [&promise](int count, LLSD result) - { promise.set_value({ count, result }); }); - return future.get(); + [promise](int count, LLSD result) + { promise->set_value({ count, result }); }); + return LLCoros::getFuture(*promise); +} + +std::pair<int, LLSD> LLLUAmanager::waitScriptFile(LuaState& L, const std::string& filename) +{ + return startScriptFile(L, filename).get(); } void LLLUAmanager::runScriptFile(LuaState& L, const std::string& filename, script_result_fn cb) @@ -324,46 +322,53 @@ void LLLUAmanager::runScriptFile(LuaState& L, const std::string& filename, scrip }); } -void LLLUAmanager::runScriptLine(const std::string& cmd, script_finished_fn cb) +void LLLUAmanager::runScriptLine(const std::string& chunk, script_finished_fn cb) { // A script_finished_fn is used to initialize the LuaState. // It will be called when the LuaState is destroyed. LuaState L(cb); - runScriptLine(L, cmd); + runScriptLine(L, chunk); } -void LLLUAmanager::runScriptLine(const std::string& cmd, script_result_fn cb) +void LLLUAmanager::runScriptLine(const std::string& chunk, script_result_fn cb) { LuaState L; // A script_result_fn will be called when LuaState::expr() completes. - runScriptLine(L, cmd, cb); + runScriptLine(L, chunk, cb); +} + +LLCoros::Future<std::pair<int, LLSD>> +LLLUAmanager::startScriptLine(LuaState& L, const std::string& chunk) +{ + // Despite returning from startScriptLine(), we need this Promise to + // remain alive until the callback has fired. + auto promise{ std::make_shared<LLCoros::Promise<std::pair<int, LLSD>>>() }; + runScriptLine(L, chunk, + [promise](int count, LLSD result) + { promise->set_value({ count, result }); }); + return LLCoros::getFuture(*promise); } -std::pair<int, LLSD> LLLUAmanager::waitScriptLine(LuaState& L, const std::string& cmd) +std::pair<int, LLSD> LLLUAmanager::waitScriptLine(LuaState& L, const std::string& chunk) { - LLCoros::Promise<std::pair<int, LLSD>> promise; - auto future{ LLCoros::getFuture(promise) }; - runScriptLine(L, cmd, - [&promise](int count, LLSD result) - { promise.set_value({ count, result }); }); - return future.get(); + return startScriptLine(L, chunk).get(); } -void LLLUAmanager::runScriptLine(LuaState& L, const std::string& cmd, script_result_fn cb) +void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_result_fn cb) { - // find a suitable abbreviation for the cmd string - std::string_view shortcmd{ cmd }; + // find a suitable abbreviation for the chunk string + std::string shortchunk{ chunk }; const size_t shortlen = 40; - std::string::size_type eol = shortcmd.find_first_of("\r\n"); + std::string::size_type eol = shortchunk.find_first_of("\r\n"); if (eol != std::string::npos) - shortcmd = shortcmd.substr(0, eol); - if (shortcmd.length() > shortlen) - shortcmd = stringize(shortcmd.substr(0, shortlen), "..."); + shortchunk = shortchunk.substr(0, eol); + if (shortchunk.length() > shortlen) + shortchunk = stringize(shortchunk.substr(0, shortlen), "..."); - std::string desc{ stringize("runScriptLine('", shortcmd, "')") }; - LLCoros::instance().launch(desc, [&L, desc, cmd, cb]() + std::string desc{ stringize("runScriptLine('", shortchunk, "')") }; + LLCoros::instance().launch(desc, [&L, desc, chunk, cb]() { - auto [count, result] = L.expr(desc, cmd); + auto [count, result] = L.expr(desc, chunk); if (cb) { cb(count, result); diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index bed12e358a..12284cd0e4 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -27,6 +27,7 @@ #ifndef LL_LLLUAMANAGER_H #define LL_LLLUAMANAGER_H +#include "llcoros.h" #include "llsd.h" #include <functional> #include <string> @@ -52,16 +53,30 @@ public: static void runScriptFile(const std::string &filename, script_finished_fn cb = {}); static void runScriptFile(const std::string &filename, script_result_fn cb); static void runScriptFile(LuaState& L, const std::string &filename, script_result_fn cb = {}); + // Start running a Lua script file, returning an LLCoros::Future whose + // get() method will pause the calling coroutine until it can deliver the + // (count, result) pair described above. Between startScriptFile() and + // Future::get(), the caller and the Lua script coroutine will run + // concurrently. + static LLCoros::Future<std::pair<int, LLSD>> + startScriptFile(LuaState& L, const std::string& filename); // Run a Lua script file, and pause the calling coroutine until it completes. // The return value is the (count, result) pair described above. static std::pair<int, LLSD> waitScriptFile(LuaState& L, const std::string& filename); - static void runScriptLine(const std::string &cmd, script_finished_fn cb = {}); - static void runScriptLine(const std::string &cmd, script_result_fn cb); - static void runScriptLine(LuaState& L, const std::string &cmd, script_result_fn cb = {}); - // Run a Lua expression string, and pause the calling coroutine until it completes. + static void runScriptLine(const std::string &chunk, script_finished_fn cb = {}); + static void runScriptLine(const std::string &chunk, script_result_fn cb); + static void runScriptLine(LuaState& L, const std::string &chunk, script_result_fn cb = {}); + // Start running a Lua chunk, returning an LLCoros::Future whose + // get() method will pause the calling coroutine until it can deliver the + // (count, result) pair described above. Between startScriptLine() and + // Future::get(), the caller and the Lua script coroutine will run + // concurrently. + static LLCoros::Future<std::pair<int, LLSD>> + startScriptLine(LuaState& L, const std::string& chunk); + // Run a Lua chunk, and pause the calling coroutine until it completes. // The return value is the (count, result) pair described above. - static std::pair<int, LLSD> waitScriptLine(LuaState& L, const std::string& cmd); + static std::pair<int, LLSD> waitScriptLine(LuaState& L, const std::string& chunk); static void runScriptOnLogin(); }; |