summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-07-18 12:55:38 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-07-18 12:55:38 -0400
commit35ee96709ef2704a2636a11c67d61190dd6bdd50 (patch)
tree6ebe53856900f0c6bef75e0de920cd0851fa81f0 /indra
parentdbfab7c12c47a2300854eb4e53b0ee68e2ff6e5e (diff)
Make `LLEventPump::listen()` also accept new `LLAwareListener`.
`listen()` still takes `LLEventListener`, a `callable(const LLSD&)`, but now also accepts `LLAwareListener`, a `callable(const LLBoundListener&, const LLSD&)`. This uses `boost::signals2::signal::connect_extended()`, which, when the signal is called, passes to a connected listener the `LLBoundListener` (aka `boost::signals2::connection`) representing its own connection. This allows a listener to disconnect itself when done. Internally, `listen_impl()` now always uses `connect_extended()`. When passed a classic `LLEventListener`, `listen()` wraps it in a lambda that ignores the passed `LLBoundListener`. `listen()` also now accepts `LLVoidListener`, and internally wraps it in a lambda that returns `false` on its behalf.
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/lleventfilter.cpp13
-rw-r--r--indra/llcommon/lleventfilter.h7
-rw-r--r--indra/llcommon/llevents.cpp11
-rw-r--r--indra/llcommon/llevents.h70
-rw-r--r--indra/newview/tests/llluamanager_test.cpp25
5 files changed, 86 insertions, 40 deletions
diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp
index da19946e3b..2b5401e9f7 100644
--- a/indra/llcommon/lleventfilter.cpp
+++ b/indra/llcommon/lleventfilter.cpp
@@ -365,21 +365,22 @@ bool LLEventLogProxy::post(const LLSD& event) /* override */
}
LLBoundListener LLEventLogProxy::listen_impl(const std::string& name,
- const LLEventListener& target,
+ const LLAwareListener& target,
const NameList& after,
const NameList& before)
{
LL_DEBUGS("LogProxy") << "LLEventLogProxy('" << getName() << "').listen('"
<< name << "')" << LL_ENDL;
return mPump.listen(name,
- [this, name, target](const LLSD& event)->bool
- { return listener(name, target, event); },
+ [this, name, target](const LLBoundListener& conn, const LLSD& event)
+ { return listener(conn, name, target, event); },
after,
before);
}
-bool LLEventLogProxy::listener(const std::string& name,
- const LLEventListener& target,
+bool LLEventLogProxy::listener(const LLBoundListener& conn,
+ const std::string& name,
+ const LLAwareListener& target,
const LLSD& event) const
{
auto eventminus = event;
@@ -391,7 +392,7 @@ bool LLEventLogProxy::listener(const std::string& name,
}
std::string hdr{STRINGIZE(getName() << " to " << name << " " << counter)};
LL_INFOS("LogProxy") << hdr << ": " << eventminus << LL_ENDL;
- bool result = target(eventminus);
+ bool result = target(conn, eventminus);
LL_INFOS("LogProxy") << hdr << " => " << result << LL_ENDL;
return result;
}
diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h
index 9988459aae..de706d72eb 100644
--- a/indra/llcommon/lleventfilter.h
+++ b/indra/llcommon/lleventfilter.h
@@ -459,7 +459,7 @@ public:
LLEventLogProxy(LLEventPump& source, const std::string& name, bool tweak=false);
/// register a new listener
- LLBoundListener listen_impl(const std::string& name, const LLEventListener& target,
+ LLBoundListener listen_impl(const std::string& name, const LLAwareListener& target,
const NameList& after, const NameList& before);
/// Post an event to all listeners
@@ -469,8 +469,9 @@ private:
/// This method intercepts each call to any target listener. We pass it
/// the listener name and the caller's intended target listener plus the
/// posted LLSD event.
- bool listener(const std::string& name,
- const LLEventListener& target,
+ bool listener(const LLBoundListener& conn,
+ const std::string& name,
+ const LLAwareListener& target,
const LLSD& event) const;
LLEventPump& mPump;
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 5a6e13cb7d..98bd990f31 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -415,7 +415,7 @@ void LLEventPump::reset()
//mDeps.clear();
}
-LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
+LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLAwareListener& listener,
const NameList& after,
const NameList& before)
{
@@ -575,7 +575,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
}
// Now that newNode has a value that places it appropriately in mSignal,
// connect it.
- LLBoundListener bound = mSignal->connect(nodePosition, listener);
+ LLBoundListener bound = mSignal->connect_extended(nodePosition, listener);
if (!name.empty())
{ // note that we are not tracking anonymous listeners here either.
@@ -659,7 +659,7 @@ bool LLEventMailDrop::post(const LLSD& event)
}
LLBoundListener LLEventMailDrop::listen_impl(const std::string& name,
- const LLEventListener& listener,
+ const LLAwareListener& listener,
const NameList& after,
const NameList& before)
{
@@ -668,7 +668,10 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name,
// Remove any that this listener consumes -- Effective STL, Item 9.
for (auto hi(mEventHistory.begin()), hend(mEventHistory.end()); hi != hend; )
{
- if (listener(*hi))
+ // We don't actually have an LLBoundListener in hand, and we won't
+ // until the base-class listen_impl() call below. Pass an empty
+ // instance.
+ if (listener({}, *hi))
{
// new listener consumed this event, erase it
hi = mEventHistory.erase(hi);
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index d0686bd8b5..8ef3a5af95 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -32,12 +32,13 @@
#if ! defined(LL_LLEVENTS_H)
#define LL_LLEVENTS_H
-#include <string>
+#include <deque>
+#include <functional>
#include <map>
#include <set>
+#include <string>
+#include <type_traits>
#include <vector>
-#include <deque>
-#include <functional>
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
@@ -137,6 +138,10 @@ typedef boost::signals2::signal<bool(const LLSD&), LLStopWhenHandled, float> LL
/// Methods that forward listeners (e.g. constructed with
/// <tt>boost::bind()</tt>) should accept (const LLEventListener&)
typedef LLStandardSignal::slot_type LLEventListener;
+/// Support a listener accepting (const LLBoundListener&, const LLSD&).
+/// Note that LLBoundListener::disconnect() is a const method: this feature is
+/// specifically intended to allow a listener to disconnect itself when done.
+typedef LLStandardSignal::extended_slot_type LLAwareListener;
/// Accept a void listener too
typedef std::function<void(const LLSD&)> LLVoidListener;
/// Result of registering a listener, supports <tt>connected()</tt>,
@@ -522,18 +527,37 @@ public:
* call, allows us to optimize away the second and subsequent dependency
* sorts.
*
- * If name is set to LLEventPump::ANONYMOUS listen will bypass the entire
+ * If name is set to LLEventPump::ANONYMOUS, listen() will bypass the entire
* dependency and ordering calculation. In this case, it is critical that
* the result be assigned to a LLTempBoundListener or the listener is
- * manually disconnected when no longer needed since there will be no
+ * manually disconnected when no longer needed, since there will be no
* way to later find and disconnect this listener manually.
*/
+ template <typename LISTENER>
LLBoundListener listen(const std::string& name,
- const LLEventListener& listener,
+ LISTENER&& listener,
const NameList& after=NameList(),
const NameList& before=NameList())
{
- return listen_impl(name, listener, after, before);
+ if constexpr (std::is_invocable_v<LISTENER, const LLSD&>)
+ {
+ // wrap classic LLEventListener in LLAwareListener lambda
+ return listenb(
+ name,
+ [listener=std::move(listener)]
+ (const LLBoundListener&, const LLSD& event)
+ {
+ return listener(event);
+ },
+ after,
+ before);
+ }
+ else
+ {
+ static_assert(std::is_invocable_v<LISTENER, LLBoundListener, const LLSD&>,
+ "LLEventPump::listen() listener has bad parameter signature");
+ return listenb(name, std::forward<LISTENER>(listener), after, before);
+ }
}
/// Get the LLBoundListener associated with the passed name (dummy
@@ -579,7 +603,35 @@ private:
LLMutex mConnectionListMutex;
protected:
- virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
+ template <typename LISTENER>
+ LLBoundListener listenb(const std::string& name,
+ LISTENER&& listener,
+ const NameList& after=NameList(),
+ const NameList& before=NameList())
+ {
+ using result_t = std::decay_t<decltype(listener(LLBoundListener(), LLSD()))>;
+ if constexpr (std::is_same_v<bool, result_t>)
+ {
+ return listen_impl(name, std::forward<LISTENER>(listener), after, before);
+ }
+ else
+ {
+ static_assert(std::is_same_v<void, result_t>,
+ "LLEventPump::listen() listener has bad return type");
+ // wrap void listener in one that returns bool
+ return listen_impl(
+ name,
+ [listener=std::move(listener)]
+ (const LLBoundListener& conn, const LLSD& event)
+ {
+ listener(conn, event);
+ return false;
+ },
+ after,
+ before);
+ }
+ }
+ virtual LLBoundListener listen_impl(const std::string& name, const LLAwareListener&,
const NameList& after,
const NameList& before);
@@ -654,7 +706,7 @@ public:
void discard();
protected:
- virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
+ virtual LLBoundListener listen_impl(const std::string& name, const LLAwareListener&,
const NameList& after,
const NameList& before) override;
diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp
index 26a4ac95e3..3209d93d39 100644
--- a/indra/newview/tests/llluamanager_test.cpp
+++ b/indra/newview/tests/llluamanager_test.cpp
@@ -42,17 +42,6 @@ public:
LLControlGroup gSavedSettings("Global");
-template <typename CALLABLE>
-auto listener(CALLABLE&& callable)
-{
- return [callable=std::forward<CALLABLE>(callable)]
- (const LLSD& data)
- {
- callable(data);
- return false;
- };
-}
-
/*****************************************************************************
* TUT
*****************************************************************************/
@@ -138,7 +127,7 @@ namespace tut
{
LLSD fromlua;
LLStreamListener pump("testpump",
- listener([&fromlua](const LLSD& data){ fromlua = data; }));
+ [&fromlua](const LLSD& data){ fromlua = data; });
const std::string lua(stringize(
"data = ", construct, "\n"
"LL.post_on('testpump', data)\n"
@@ -167,8 +156,8 @@ namespace tut
set_test_name("test post_on(), get_event_pumps(), get_event_next()");
StringVec posts;
LLStreamListener pump("testpump",
- listener([&posts](const LLSD& data)
- { posts.push_back(data.asString()); }));
+ [&posts](const LLSD& data)
+ { posts.push_back(data.asString()); });
const std::string lua(
"-- test post_on,get_event_pumps,get_event_next\n"
"LL.post_on('testpump', 'entry')\n"
@@ -346,11 +335,11 @@ namespace tut
LLStreamListener pump(
"echo",
- listener([](const LLSD& data)
+ [](const LLSD& data)
{
LL_DEBUGS("Lua") << "echo pump got: " << data << LL_ENDL;
sendReply(data, data);
- }));
+ });
auto [count, result] = LLLUAmanager::waitScriptLine(lua);
ensure_equals("Lua script didn't return item", count, 1);
@@ -424,11 +413,11 @@ namespace tut
LLSD requests;
LLStreamListener pump(
"testpump",
- listener([&requests](const LLSD& data)
+ [&requests](const LLSD& data)
{
LL_DEBUGS("Lua") << "testpump got: " << data << LL_ENDL;
requests.append(data);
- }));
+ });
auto future = LLLUAmanager::startScriptLine(lua);
// LuaState::expr() periodically interrupts a running chunk to ensure