From de1fc577666686fb0c3f8b38d8c6c90eb6dff414 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 23 Mar 2024 17:14:33 +0900 Subject: Update sendReply(): accepting LLSD by value already copies it. --- indra/llcommon/llevents.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index c1dbf4392f..1f35a6de18 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -779,7 +779,7 @@ private: * Before sending the reply event, sendReply() copies the ["reqid"] item from * the request to the reply. */ -LL_COMMON_API bool sendReply(const LLSD& reply, const LLSD& request, +LL_COMMON_API bool sendReply(LLSD reply, const LLSD& request, const std::string& replyKey="reply"); #endif /* ! defined(LL_LLEVENTS_H) */ -- cgit v1.2.3 From 93b30f960ea327fe3c36deed72a8075ce0d13f19 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sun, 24 Mar 2024 02:16:55 +0900 Subject: Introduce LLStreamListener: bundle LLEventStream+LLTempBoundListener. This is a very common pattern, especially in test code, but elsewhere in the viewer too. Use it in llluamanager_test.cpp. --- indra/llcommon/llevents.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 1f35a6de18..ebc893d1e6 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -151,6 +151,8 @@ typedef boost::signals2::signal LL /// Methods that forward listeners (e.g. constructed with /// boost::bind()) should accept (const LLEventListener&) typedef LLStandardSignal::slot_type LLEventListener; +/// Accept a void listener too +typedef std::function LLVoidListener; /// Result of registering a listener, supports connected(), /// disconnect() and blocked() typedef boost::signals2::connection LLBoundListener; @@ -158,6 +160,23 @@ typedef boost::signals2::connection LLBoundListener; /// referenced listener when the LLTempBoundListener instance is destroyed. typedef boost::signals2::scoped_connection LLTempBoundListener; +/// Accepting (const LLListener&) allows either LLEventListener or LLVoidListener +/// TODO: but compiler considers the constructor call ambiguous?? +class LLListener +{ +public: + LLListener(const LLEventListener& listener): + mListener(listener) + {} + LLListener(const LLVoidListener& listener): + mListener([listener](const LLSD& data){ listener(data); return false; }) + {} + operator LLEventListener() const { return mListener; } + +private: + LLEventListener mListener; +}; + /** * A common idiom for event-based code is to accept either a callable -- * directly called on completion -- or the string name of an LLEventPump on @@ -687,6 +706,30 @@ private: EventList mEventHistory; }; +/***************************************************************************** +* LLNamedListener +*****************************************************************************/ +/** + * LLNamedListener bundles a concrete LLEventPump subclass with a specific + * listener function, with an LLTempBoundListener to ensure that it's + * disconnected before destruction. + */ +template +class LL_COMMON_API LLNamedListener: PUMP +{ + using pump_t = PUMP; +public: + template + LLNamedListener(const std::string& name, LISTENER&& listener): + pump_t(name, false), // don't tweak the name + mConn(pump_t::listen("func", std::forward(listener))) + {} + +private: + LLTempBoundListener mConn; +}; +using LLStreamListener = LLNamedListener<>; + /***************************************************************************** * LLReqID *****************************************************************************/ -- cgit v1.2.3 From fd8c5fced1ee62e08c55adf92fb9c8d0e52d313a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 25 Mar 2024 10:03:17 -0400 Subject: Remove colliding LLListener. --- indra/llcommon/llevents.h | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index ebc893d1e6..77a405871d 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -160,23 +160,6 @@ typedef boost::signals2::connection LLBoundListener; /// referenced listener when the LLTempBoundListener instance is destroyed. typedef boost::signals2::scoped_connection LLTempBoundListener; -/// Accepting (const LLListener&) allows either LLEventListener or LLVoidListener -/// TODO: but compiler considers the constructor call ambiguous?? -class LLListener -{ -public: - LLListener(const LLEventListener& listener): - mListener(listener) - {} - LLListener(const LLVoidListener& listener): - mListener([listener](const LLSD& data){ listener(data); return false; }) - {} - operator LLEventListener() const { return mListener; } - -private: - LLEventListener mListener; -}; - /** * A common idiom for event-based code is to accept either a callable -- * directly called on completion -- or the string name of an LLEventPump on -- cgit v1.2.3 From 10af340aa35b3dcdc0c00a03743d33f33e627ad7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 Mar 2024 07:08:36 -0400 Subject: Clean up unused llevents.h #includes. --- indra/llcommon/llevents.h | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 77a405871d..c1b752a143 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -48,27 +48,13 @@ #pragma warning (pop) #endif -#include -#include // noncopyable #include -#include -#include // reference_wrapper -#include -#include #include "llsd.h" #include "llsingleton.h" #include "lldependencies.h" -#include "llstl.h" #include "llexception.h" #include "llhandle.h" -/*==========================================================================*| -// override this to allow binding free functions with more parameters -#ifndef LLEVENTS_LISTENER_ARITY -#define LLEVENTS_LISTENER_ARITY 10 -#endif -|*==========================================================================*/ - // hack for testing #ifndef testable #define testable private -- cgit v1.2.3 From 271bc05b91772e5aedd834db116734b34f0108a1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 10 May 2024 09:55:19 -0400 Subject: Fix latent access violation in ~LLEventPump() if LLEventPumps gone. Instead of making LLEventPumps an LLHandleProvider, and storing an LLHandle in each LLEventPump instance, just make ~LLEventPump() query LLEventPumps::instanceExists() before calling instance(). --- indra/llcommon/llevents.h | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 1fc0f23ecd..15d1d5035c 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -53,7 +53,6 @@ #include "llsingleton.h" #include "lldependencies.h" #include "llexception.h" -#include "llhandle.h" // hack for testing #ifndef testable @@ -213,15 +212,7 @@ class LLEventPump; * LLEventPumps is a Singleton manager through which one typically accesses * this subsystem. */ -// LLEventPumps isa LLHandleProvider only for (hopefully rare) long-lived -// class objects that must refer to this class late in their lifespan, say in -// the destructor. Specifically, the case that matters is a possible reference -// after LLEventPumps::deleteSingleton(). (Lingering LLEventPump instances are -// capable of this.) In that case, instead of calling LLEventPumps::instance() -// again -- resurrecting the deleted LLSingleton -- store an -// LLHandle and test it before use. -class LL_COMMON_API LLEventPumps: public LLSingleton, - public LLHandleProvider +class LL_COMMON_API LLEventPumps: public LLSingleton { LLSINGLETON(LLEventPumps); public: @@ -582,12 +573,7 @@ private: virtual void clear(); virtual void reset(); - - private: - // must precede mName; see LLEventPump::LLEventPump() - LLHandle mRegistry; - std::string mName; LLMutex mConnectionListMutex; -- cgit v1.2.3 From 2c687e6d687ead0f29c096271b207dbe16b31c35 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 10 May 2024 11:05:56 -0400 Subject: Fix up a few #includes --- indra/llcommon/llevents.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 15d1d5035c..2cc1f91408 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -49,10 +49,11 @@ #endif #include -#include "llsd.h" -#include "llsingleton.h" #include "lldependencies.h" #include "llexception.h" +#include "llmutex.h" +#include "llsd.h" +#include "llsingleton.h" // hack for testing #ifndef testable -- cgit v1.2.3 From 7137647e90d8c11197513f542f04fb39b483d663 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 15 May 2024 12:19:54 -0400 Subject: Manual whitespace cleanup (fix_whitespace.py). --- indra/llcommon/llevents.h | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 2cc1f91408..d0686bd8b5 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -6,25 +6,25 @@ * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Event_System, * originally introduced in llnotifications.h. It has nothing * whatsoever to do with the older system in llevent.h. - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -39,13 +39,13 @@ #include #include #if LL_WINDOWS - #pragma warning (push) - #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch - #pragma warning (disable : 4264) + #pragma warning (push) + #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch + #pragma warning (disable : 4264) #endif #include #if LL_WINDOWS - #pragma warning (pop) + #pragma warning (pop) #endif #include @@ -290,9 +290,9 @@ public: /** * Find the named LLEventPump instance. If it exists post the message to it. * If the pump does not exist, do nothing. - * + * * returns the result of the LLEventPump::post. If no pump exists returns false. - * + * * This is syntactically similar to LLEventPumps::instance().post(name, message), * however if the pump does not already exist it will not be created. */ @@ -521,10 +521,10 @@ public: * instantiate your listener, then passing the same name on each listen() * call, allows us to optimize away the second and subsequent dependency * sorts. - * - * 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 + * + * 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 * way to later find and disconnect this listener manually. */ @@ -582,7 +582,7 @@ protected: virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, const NameList& after, const NameList& before); - + /// implement the dispatching std::shared_ptr mSignal; @@ -635,21 +635,21 @@ public: * by all listeners, until some listener consumes it. The caveat is that each * event *must* eventually reach a listener that will consume it, else the * queue will grow to arbitrary length. - * + * * @NOTE: When using an LLEventMailDrop with an LLEventTimeout or * LLEventFilter attaching the filter downstream, using Timeout's constructor will - * cause the MailDrop to discharge any of its stored events. The timeout should - * instead be connected upstream using its listen() method. + * cause the MailDrop to discharge any of its stored events. The timeout should + * instead be connected upstream using its listen() method. */ class LL_COMMON_API LLEventMailDrop : public LLEventStream { public: LLEventMailDrop(const std::string& name, bool tweak = false) : LLEventStream(name, tweak) {} virtual ~LLEventMailDrop() {} - + /// Post an event to all listeners virtual bool post(const LLSD& event) override; - + /// Remove any history stored in the mail drop. void discard(); -- cgit v1.2.3 From 35ee96709ef2704a2636a11c67d61190dd6bdd50 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 18 Jul 2024 12:55:38 -0400 Subject: 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. --- indra/llcommon/llevents.h | 70 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) (limited to 'indra/llcommon/llevents.h') 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 +#include +#include #include #include +#include +#include #include -#include -#include #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 LL /// Methods that forward listeners (e.g. constructed with /// boost::bind()) 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 LLVoidListener; /// Result of registering a listener, supports connected(), @@ -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 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) + { + // 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, + "LLEventPump::listen() listener has bad parameter signature"); + return listenb(name, std::forward(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 + LLBoundListener listenb(const std::string& name, + LISTENER&& listener, + const NameList& after=NameList(), + const NameList& before=NameList()) + { + using result_t = std::decay_t; + if constexpr (std::is_same_v) + { + return listen_impl(name, std::forward(listener), after, before); + } + else + { + static_assert(std::is_same_v, + "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; -- cgit v1.2.3 From f2f0fa7fd0efc221f1358dd4e440b5d51a5fb8b4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 18 Jul 2024 13:29:34 -0400 Subject: Ditch `LLEventTrackable` aka `boost::signals2::trackable`. Remove documented `LLEventPump` support for `LLEventTrackable`. That claimed support was always a little bit magical/fragile. IF: * a class included `LLEventTrackable` as a base class AND * an instance of that class was managed by `boost::shared_ptr` AND * you passed one of that class's methods and the `boost::shared_ptr` specifically to `boost::bind()` AND * the resulting `boost::bind()` object was passed into `LLEventPump::listen()` THEN the promise was that on destruction of that object, that listener would automatically be disconnected -- instead of leaving a dangling pointer bound into the `LLEventPump`, causing a crash on the next `LLEventPump::post()` call. The only existing code in the viewer code base that exercised `LLEventTrackable` functionality was in test programs. When the viewer calls `LLEventPump::listen()`, it typically stores the resulting connection object in an `LLTempBoundListener` variable, which guarantees disconnection on destruction of that variable. The fact that `LLEventTrackable` support is specific to `boost::bind()`, that it silently fails to keep its promise with `std::bind()` or a lambda or any other form of C++ callable, makes it untrustworthy for new code. Note that the code base still uses `boost::signals2::trackable` for other `boost::signals2::signal` instances not associated with `LLEventPump`. We are not changing those at this time. --- indra/llcommon/llevents.h | 45 +-------------------------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 8ef3a5af95..abc25ba400 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -363,57 +363,14 @@ testable: InstanceTypes mTypes; }; -/***************************************************************************** -* LLEventTrackable -*****************************************************************************/ -/** - * LLEventTrackable wraps boost::signals2::trackable, which resembles - * boost::trackable. Derive your listener class from LLEventTrackable instead, - * and use something like - * LLEventPump::listen(boost::bind(&YourTrackableSubclass::method, - * instance, _1)). This will implicitly disconnect when the object - * referenced by @c instance is destroyed. - * - * @note - * LLEventTrackable doesn't address a couple of cases: - * * Object destroyed during call - * - You enter a slot call in thread A. - * - Thread B destroys the object, which of course disconnects it from any - * future slot calls. - * - Thread A's call uses 'this', which now refers to a defunct object. - * Undefined behavior results. - * * Call during destruction - * - @c MySubclass is derived from LLEventTrackable. - * - @c MySubclass registers one of its own methods using - * LLEventPump::listen(). - * - The @c MySubclass object begins destruction. ~MySubclass() - * runs, destroying state specific to the subclass. (For instance, a - * Foo* data member is deleted but not zeroed.) - * - The listening method will not be disconnected until - * ~LLEventTrackable() runs. - * - Before we get there, another thread posts data to the @c LLEventPump - * instance, calling the @c MySubclass method. - * - The method in question relies on valid @c MySubclass state. (For - * instance, it attempts to dereference the Foo* pointer that was - * deleted but not zeroed.) - * - Undefined behavior results. - */ -typedef boost::signals2::trackable LLEventTrackable; - /***************************************************************************** * LLEventPump *****************************************************************************/ /** * LLEventPump is the base class interface through which we access the * concrete subclasses such as LLEventStream. - * - * @NOTE - * LLEventPump derives from LLEventTrackable so that when you "chain" - * LLEventPump instances together, they will automatically disconnect on - * destruction. Please see LLEventTrackable documentation for situations in - * which this may be perilous across threads. */ -class LL_COMMON_API LLEventPump: public LLEventTrackable +class LL_COMMON_API LLEventPump { public: static const std::string ANONYMOUS; // constant for anonymous listeners. -- cgit v1.2.3 From ea24ac899ca28a587be0e187b77873a25d08a556 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 4 Sep 2024 14:07:26 -0400 Subject: Extract coroutine-aware synchronization primitives to new header. Changes on new main and changes on Lua project branch combined into a header circularity. Resolved by hoisting coroutine-aware synchronization primitives out to a new llcoromutex.h file in the `llcoro` namespace, rather than being literally members of the `LLCoros` class. But retained `using` declarations in `LLCoros` for backwards compatibility. --- indra/llcommon/llevents.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llcommon/llevents.h') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 368138a50b..f92257238d 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -50,7 +50,7 @@ #endif #include -#include "llcoros.h" +#include "llcoromutex.h" #include "lldependencies.h" #include "llexception.h" #include "llsd.h" @@ -557,7 +557,7 @@ private: private: std::string mName; - LLCoros::Mutex mConnectionListMutex; + llcoro::Mutex mConnectionListMutex; protected: template -- cgit v1.2.3