From 85ef003ed7836cb351ee62ed44a4837a305e9dbd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 22 Feb 2024 20:42:08 -0500 Subject: Lua listen_events(), await_event() => get_event_{pumps,next}(). Don't set up a Lua callback to receive incoming events, a la listen_events(). Don't listen on an arbitrary event pump, a la await_event(). Instead, the new get_event_pumps() entry point simply delivers the reply pump and command pump names (as listen_events() did) without storing a Lua callback. Make LuaListener capture incoming events on the reply pump in a queue. This avoids the problem of multiple events arriving too quickly for the Lua script to retrieve. If the queue gets too big, discard the excess instead of blocking the caller of post(). Then the new get_event_next() entry point retrieves the next (pump, data) pair from the queue, blocking the Lua script until a suitable event arrives. This is closer to the use of stdin for a LEAP plugin. It also addresses the question: what should the Lua script's C++ coroutine do while waiting for an incoming reply pump event? Recast llluamanager_test.cpp for this new, more straightforward API. Move LLLeap's and LuaListener's reply LLEventPump into LLLeapListener, which they both use. This simplifies LLLeapListener's API, which was a little convoluted: the caller supplied a connect callback to allow LLLeapListener to connect some listener to the caller's reply pump. Now, instead, the caller simply passes a bool(pumpname, data) callback to receive events incoming on LLLeapListener's own reply pump. Fix a latent bug in LLLeapListener: if a plugin called listen() more than once with the same listener name, the new connection would not have been saved. While at it, replace some older Boost features in LLLeapListener and LLLeap. --- indra/newview/llluamanager.cpp | 63 +++++++++++-------------------- indra/newview/tests/llluamanager_test.cpp | 35 +++++++---------- 2 files changed, 35 insertions(+), 63 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index c6a900aa81..33b996ab50 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -166,55 +166,34 @@ lua_function(post_on, "post_on(pumpname, data): post specified data to specified return 0; } -lua_function(listen_events, - "listen_events(callback): call callback(pumpname, data) with events received\n" - "on this Lua chunk's replypump.\n" - "Returns replypump, commandpump: names of LLEventPumps specific to this chunk.") +lua_function(get_event_pumps, + "get_event_pumps():\n" + "Returns replypump, commandpump: names of LLEventPumps specific to this chunk.\n" + "Events posted to replypump are queued for get_event_next().\n" + "post_on(commandpump, ...) to engage LLEventAPI operations (see helpleap()).") { - if (! lua_isfunction(L, 1)) - { - luaL_typeerror(L, 1, "function"); - return 0; - } 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, 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 }; - - auto listener{ LuaState::obtainListener(mainthread) }; - - // Now that we've found or created our LuaListener, store the passed Lua - // function as the callback. Beware: our caller passed the function on L's - // stack, but we want to store it on the mainthread registry. - if (L != mainthread) - { - // push 1 value (the Lua function) from L's stack to mainthread's - lua_xmove(L, mainthread, 1); - } - lua_setfield(mainthread, LUA_REGISTRYINDEX, "event.function"); - + 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()); lua_pushstdstring(L, listener->getCommandName()); return 2; } +lua_function(get_event_next, + "get_event_next():\n" + "Returns the next (pumpname, data) pair from the replypump whose name\n" + "is returned by get_event_pumps(). Blocks the calling chunk until an\n" + "event becomes available.") +{ + luaL_checkstack(L, 2, nullptr); + auto listener{ LuaState::obtainListener(L) }; + const auto& [pump, data]{ listener->getNext() }; + lua_pushstdstring(L, pump); + lua_pushllsd(L, data); + return 2; +} + lua_function(await_event, "await_event(pumpname [, timeout [, value to return if timeout (default nil)]]):\n" "pause the running Lua chunk until the next event on the named LLEventPump") @@ -343,7 +322,7 @@ void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_r if (shortchunk.length() > shortlen) shortchunk = stringize(shortchunk.substr(0, shortlen), "..."); - std::string desc{ stringize("runScriptLine('", shortchunk, "')") }; + std::string desc{ stringize("lua: ", shortchunk) }; LLCoros::instance().launch(desc, [&L, desc, chunk, cb]() { auto [count, result] = L.expr(desc, chunk); diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index 1f25fd5f3b..81ec186cf4 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -108,7 +108,6 @@ namespace tut replypump.listen("llluamanager_test", listener([&fromlua](const LLSD& data){ fromlua = data; }))); const std::string lua(stringize( - "-- test LLSD synthesized by Lua\n", "data = ", construct, "\n" "post_on('testpump', data)\n" )); @@ -134,7 +133,7 @@ namespace tut template<> template<> void object::test<3>() { - set_test_name("test post_on(), listen_events(), await_event()"); + set_test_name("test post_on(), get_event_pumps(), get_event_next()"); StringVec posts; LLEventStream replypump("testpump"); LLTempBoundListener conn( @@ -142,17 +141,14 @@ namespace tut listener([&posts](const LLSD& data) { posts.push_back(data.asString()); }))); const std::string lua( - "-- test post_on,listen_events,await_event\n" + "-- test post_on,get_event_pumps,get_event_next\n" "post_on('testpump', 'entry')\n" - "callback = function(pump, data)\n" - " -- just echo the data we received\n" - " post_on('testpump', data)\n" - "end\n" - "post_on('testpump', 'listen_events()')\n" - "replypump, cmdpump = listen_events(callback)\n" + "post_on('testpump', 'get_event_pumps()')\n" + "replypump, cmdpump = get_event_pumps()\n" "post_on('testpump', replypump)\n" - "post_on('testpump', 'await_event()')\n" - "await_event(replypump)\n" + "post_on('testpump', 'get_event_next()')\n" + "pump, data = get_event_next()\n" + "post_on('testpump', data)\n" "post_on('testpump', 'exit')\n" ); LuaState L; @@ -162,9 +158,9 @@ namespace tut auto future = LLLUAmanager::startScriptLine(L, lua); StringVec expected{ "entry", - "listen_events()", + "get_event_pumps()", "", - "await_event()", + "get_event_next()", "message", "exit" }; @@ -188,18 +184,15 @@ namespace tut listener([&reply](const LLSD& post){ reply = post; }))); const std::string lua( "-- test LLSD round trip\n" - "callback = function(pump, data)\n" - " -- just echo the data we received\n" - " post_on('testpump', data)\n" - "end\n" - "replypump, cmdpump = listen_events(callback)\n" + "replypump, cmdpump = get_event_pumps()\n" "post_on('testpump', replypump)\n" - "await_event(replypump)\n" + "pump, data = get_event_next()\n" + "return data\n" ); LuaState L; auto future = LLLUAmanager::startScriptLine(L, lua); // We woke up again ourselves because the coroutine running Lua has - // reached the await_event() call, which suspends the calling C++ + // reached the get_event_next() call, which suspends the calling C++ // coroutine (including the Lua code running on it) until we post // something to that reply pump. auto luapump{ reply.asString() }; @@ -208,7 +201,7 @@ namespace tut // The C++ coroutine running the Lua script is now ready to run. Run // it so it will echo the LLSD back to us. auto [count, result] = future.get(); - ensure_equals(desc, reply, expect); + ensure_equals(desc, result, expect); } // Define an RTItem to be used for round-trip LLSD testing: what it is, -- cgit v1.2.3