diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-03-28 07:11:31 -0400 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-03-28 07:11:31 -0400 |
commit | ce73e5c5ab0c33673067d9322c98ae8800fa9224 (patch) | |
tree | 5efe142b37454a04bedea3319e455ea4c6a2a8e5 /indra | |
parent | 9fbfd3d0ad1ed2cb721129a59421f06fedd071bf (diff) |
Terminate Lua scripts hanging in LL.get_event_next().
Make LuaListener listen for "LLApp" viewer shutdown events. On receiving such,
it closes its queue. Then the C++ coroutine calling getNext() wakes up with an
LLThreadSafeQueue exception, and calls LLCoros::checkStop() to throw one of
the exceptions recognized by LLCoros::toplevel().
Add an llluamanager_test.cpp test to verify this behavior.
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llcommon/lualistener.cpp | 31 | ||||
-rw-r--r-- | indra/llcommon/lualistener.h | 2 | ||||
-rw-r--r-- | indra/newview/tests/llluamanager_test.cpp | 21 |
3 files changed, 52 insertions, 2 deletions
diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp index ed34133924..37ce27a2a4 100644 --- a/indra/llcommon/lualistener.cpp +++ b/indra/llcommon/lualistener.cpp @@ -36,7 +36,24 @@ LuaListener::LuaListener(lua_State* L): mListener(new LLLeapListener( "LuaListener", [this](const std::string& pump, const LLSD& data) - { return queueEvent(pump, data); })) + { return queueEvent(pump, data); })), + // Listen for shutdown events on the "LLApp" LLEventPump. + mShutdownConnection( + LLEventPumps::instance().obtain("LLApp").listen( + LLEventPump::inventName("LuaState"), + [this](const LLSD& status) + { + LL_DEBUGS("LuaListener") << "caught " << status << LL_ENDL; + const auto& statsd = status["status"]; + if (statsd.asString() != "running") + { + // If a Lua script is still blocked in getNext() during + // viewer shutdown, close the queue to wake up getNext(). + LL_DEBUGS("LuaListener") << "closing queue" << LL_ENDL; + mQueue.close(); + } + return false; + })) {} LuaListener::~LuaListener() @@ -87,5 +104,15 @@ bool LuaListener::queueEvent(const std::string& pump, const LLSD& data) LuaListener::PumpData LuaListener::getNext() { - return mQueue.pop(); + try + { + return mQueue.pop(); + } + catch (const LLThreadSafeQueueInterrupt& exc) + { + // mQueue has been closed. The only way that happens is when we detect + // viewer shutdown. Terminate the calling coroutine. + LLCoros::checkStop(); + return {}; + } } diff --git a/indra/llcommon/lualistener.h b/indra/llcommon/lualistener.h index c13b7bbd5f..40ccfba8fe 100644 --- a/indra/llcommon/lualistener.h +++ b/indra/llcommon/lualistener.h @@ -12,6 +12,7 @@ #if ! defined(LL_LUALISTENER_H) #define LL_LUALISTENER_H +#include "llevents.h" #include "llinstancetracker.h" #include "llsd.h" #include "llthreadsafequeue.h" @@ -73,6 +74,7 @@ private: LLThreadSafeQueue<PumpData> mQueue; std::unique_ptr<LLLeapListener> mListener; + LLTempBoundListener mShutdownConnection; }; #endif /* ! defined(LL_LUALISTENER_H) */ diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index e10dedcf53..682289bd93 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -418,4 +418,25 @@ namespace tut auto [count, result] = future.get(); ensure_equals("leap.lua: " + result.asString(), count, 0); } + + template<> template<> + void object::test<7>() + { + set_test_name("stop hanging Lua script"); + const std::string lua( + "-- hanging Lua script should terminate\n" + "\n" + "LL.get_event_next()\n" + ); + LuaState L; + auto future = LLLUAmanager::startScriptLine(L, lua); + // The problem with this test is that the LuaState is destroyed + // (disconnecting the listener) before the LLTestApp instance mApp is + // destroyed (sending the shutdown event). Explicitly simulate LLApp's + // event. + LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", "quitting")); + // but now we have to give the startScriptLine() coroutine a chance to run + auto [count, result] = future.get(); + ensure_equals(result.asString(), count, 0); + } } // namespace tut |