From 0c32c98f25f3f7d99a244a249dc08b03cd36a5b0 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 29 Dec 2018 10:15:28 -0500 Subject: SL-793: Add LLEventPumps::clear() method to disconnect all listeners. This is like the existing reset() method, except that reset() is specifically intended for shutdown: it disables every existing LLEventPump in such a way that it cannot be subsequently reused. (The original idea was to disconnect listeners in DLLs unloaded at shutdown.) clear() forcibly disconnects all existing listeners, but leaves LLEventPumps ready for reuse. This is useful (e.g.) for test programs to reset the state of LLEventPumps between individual test functions. --- indra/llcommon/llevents.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'indra/llcommon/llevents.cpp') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index eedd8c92b5..99abb333bb 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -45,6 +45,7 @@ #include // external library headers #include +#include #if LL_WINDOWS #pragma warning (push) #pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no @@ -154,13 +155,23 @@ void LLEventPumps::flush() } } +void LLEventPumps::clear() +{ + // Clear every known LLEventPump instance. Leave it up to each instance to + // decide what to do with the clear() call. + for (PumpMap::value_type& pair : mPumpMap) + { + pair.second->clear(); + } +} + void LLEventPumps::reset() { // Reset every known LLEventPump instance. Leave it up to each instance to // decide what to do with the reset() call. - for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi) + for (PumpMap::value_type& pair : mPumpMap) { - pmi->second->reset(); + pair.second->reset(); } } @@ -283,7 +294,7 @@ LLEventPump::LLEventPump(const std::string& name, bool tweak): // Register every new instance with LLEventPumps mRegistry(LLEventPumps::instance().getHandle()), mName(mRegistry.get()->registerNew(*this, name, tweak)), - mSignal(new LLStandardSignal()), + mSignal(boost::make_shared()), mEnabled(true) {} @@ -311,6 +322,14 @@ std::string LLEventPump::inventName(const std::string& pfx) return STRINGIZE(pfx << suffix++); } +void LLEventPump::clear() +{ + // Destroy the original LLStandardSignal instance, replacing it with a + // whole new one. + mSignal = boost::make_shared(); + mConnections.clear(); +} + void LLEventPump::reset() { mSignal.reset(); @@ -553,7 +572,7 @@ bool LLEventMailDrop::post(const LLSD& event) // be posted to any future listeners when they attach. mEventHistory.push_back(event); } - + return posted; } -- cgit v1.2.3 From 6805e82acdcde9a3f18681427a33a5bc7be7a0a6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Oct 2019 12:04:02 -0400 Subject: DRTVWR-476: Introduce LLEventMailDrop::discard() (instead of flush()). Overriding virtual LLEventPump::flush() for the semantic of discarding LLEventMailDrop's queued events turns out not to be such a great idea, because LLEventPumps::flush(), which calls every registered LLEventPump's flush() method, is called every mainloop tick. The first time we hit a use case in which we expected LLEventMailDrop to hold queued events across a mainloop tick, we were baffled that they were never delivered. Moving that logic to a separate method specific to LLEventMailDrop resolves that problem. Naming it discard() clarifies its intended functionality. --- indra/llcommon/llevents.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'indra/llcommon/llevents.cpp') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 99abb333bb..186e710c43 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -602,6 +602,11 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, return LLEventStream::listen_impl(name, listener, after, before); } +void LLEventMailDrop::discard() +{ + mEventHistory.clear(); + LLEventStream::flush(); +} /***************************************************************************** * LLEventQueue @@ -621,8 +626,8 @@ bool LLEventQueue::post(const LLSD& event) void LLEventQueue::flush() { - if(!mSignal) return; - + if(!mSignal) return; + // Consider the case when a given listener on this LLEventQueue posts yet // another event on the same queue. If we loop over mEventQueue directly, // we'll end up processing all those events during the same flush() call -- cgit v1.2.3 From 79a3e391d3c992925230ff01551747e7edccb0ca Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Oct 2019 13:26:51 -0400 Subject: DRTVWR-476: Kill LLEventQueue, per-frame LLEventPump::flush() calls. No one uses LLEventQueue to defer posted events until the next mainloop tick -- and with LLCoros moving to Boost.Fiber, cross-coroutine event posting works that way anyway, making LLEventQueue pretty unnecessary. The static RegisterFlush instance in llevents.cpp was used to call LLEventPumps::flush() once per mainloop tick, which in turn called flush() on every registered LLEventPump. But the only reason for that mechanism was to support LLEventQueue. In fact, when LLEventMailDrop overrode its flush() method for something quite different, it was startling to find that the new flush() override was being called once per frame -- which caused at least one fairly mysterious bug. Remove RegisterFlush. Both LLEventPumps::flush() and LLEventPump::flush() remain for now, though intended usage is unclear. Eliminating LLEventQueue means we must at least repurpose LLEventPumps::mQueueNames, a map intended to make LLEventPumps::obtain() instantiate an LLEventQueue rather than the default LLEventPump. Replace it with mFactories, a map from desired instance name to a callable returning LLEventPump*. New map initialization syntax plus lambda support allows us to populate that map at compile time with little lambdas returning the correct subclass instance. Similarly, LLLeapListener::newpump() used to check the ["type"] entry in the LLSD request specifically for "LLEventQueue". Introduce another such map in llleaplistener.cpp for potential future extensibility. Eliminate the LLEventQueue-specific test. --- indra/llcommon/llevents.cpp | 102 ++++++-------------------------------------- 1 file changed, 12 insertions(+), 90 deletions(-) (limited to 'indra/llcommon/llevents.cpp') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 186e710c43..d31f5f2d32 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -64,51 +64,16 @@ #endif /***************************************************************************** -* queue_names: specify LLEventPump names that should be instantiated as -* LLEventQueue -*****************************************************************************/ -/** - * At present, we recognize particular requested LLEventPump names as needing - * LLEventQueues. Later on we'll migrate this information to an external - * configuration file. - */ -const char* queue_names[] = -{ - "placeholder - replace with first real name string" -}; - -/***************************************************************************** -* If there's a "mainloop" pump, listen on that to flush all LLEventQueues +* LLEventPumps *****************************************************************************/ -struct RegisterFlush : public LLEventTrackable +LLEventPumps::PumpFactories LLEventPumps::mFactories { - RegisterFlush(): - pumps(LLEventPumps::instance()) - { - pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1)); - } - bool flush(const LLSD&) - { - pumps.flush(); - return false; - } - ~RegisterFlush() - { - // LLEventTrackable handles stopListening for us. - } - LLEventPumps& pumps; + // LLEventStream is the default for obtain(), so even if somebody DOES + // call obtain("placeholder"), this sample entry won't break anything. + { "placeholder", [](const std::string& name) { return new LLEventStream(name); } } }; -static RegisterFlush registerFlush; -/***************************************************************************** -* LLEventPumps -*****************************************************************************/ -LLEventPumps::LLEventPumps(): - // Until we migrate this information to an external config file, - // initialize mQueueNames from the static queue_names array. - mQueueNames(boost::begin(queue_names), boost::end(queue_names)) -{ -} +LLEventPumps::LLEventPumps() {} LLEventPump& LLEventPumps::obtain(const std::string& name) { @@ -121,10 +86,10 @@ LLEventPump& LLEventPumps::obtain(const std::string& name) } // Here we must instantiate an LLEventPump subclass. LLEventPump* newInstance; - // Should this name be an LLEventQueue? - PumpNames::const_iterator nfound = mQueueNames.find(name); - if (nfound != mQueueNames.end()) - newInstance = new LLEventQueue(name); + // Do we have a predefined factory for this instance name? + PumpFactories::const_iterator nfound = mFactories.find(name); + if (nfound != mFactories.end()) + newInstance = (nfound->second)(name); else newInstance = new LLEventStream(name); // LLEventPump's constructor implicitly registers each new instance in @@ -144,14 +109,13 @@ bool LLEventPumps::post(const std::string&name, const LLSD&message) return (*found).second->post(message); } - void LLEventPumps::flush() { // Flush every known LLEventPump instance. Leave it up to each instance to // decide what to do with the flush() call. - for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi) + for (PumpMap::value_type& pair : mPumpMap) { - pmi->second->flush(); + pair.second->flush(); } } @@ -605,48 +569,6 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, void LLEventMailDrop::discard() { mEventHistory.clear(); - LLEventStream::flush(); -} - -/***************************************************************************** -* LLEventQueue -*****************************************************************************/ -bool LLEventQueue::post(const LLSD& event) -{ - if (mEnabled) - { - // Defer sending this event by queueing it until flush() - mEventQueue.push_back(event); - } - // Unconditionally return false. We won't know until flush() whether a - // listener claims to have handled the event -- meanwhile, don't block - // other listeners. - return false; -} - -void LLEventQueue::flush() -{ - if(!mSignal) return; - - // Consider the case when a given listener on this LLEventQueue posts yet - // another event on the same queue. If we loop over mEventQueue directly, - // we'll end up processing all those events during the same flush() call - // -- rather like an EventStream. Instead, copy mEventQueue and clear it, - // so that any new events posted to this LLEventQueue during flush() will - // be processed in the *next* flush() call. - EventQueue queue(mEventQueue); - mEventQueue.clear(); - // NOTE NOTE NOTE: Any new access to member data beyond this point should - // cause us to move our LLStandardSignal object to a pimpl class along - // with said member data. Then the local shared_ptr will preserve both. - - // DEV-43463: capture a local copy of mSignal. See LLEventStream::post() - // for detailed comments. - boost::shared_ptr signal(mSignal); - for ( ; ! queue.empty(); queue.pop_front()) - { - (*signal)(queue.front()); - } } /***************************************************************************** -- cgit v1.2.3 From 95a218fcc0d22cdf02a7c81c555b9c909e24bd40 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 18 Oct 2019 14:46:05 -0400 Subject: DRTVWR-476: Eliminate static LLEventPump factory maps. Having a map from std::string to a factory function returning LLEventPump* is a cool idea, especially since you could statically populate such a map with string literals and little lambdas. Unfortunately, static initialization of any data is a bad idea when control can reach consuming code before that module's static data are constructed. Since LLEventPumps is already an LLSingleton, it's simple enough to make its map non-static and initialize it in the constructor. But another recent static factory-function map was introduced in llleaplistener.cpp to support the LLLeapListener::newpump() operation. That involves no LLSingletons. Introduce LLEventPumps::make(name, tweak, type) to instantiate an LLEventPump subclass of the specified type with specified (name, tweak) parameters. Instances returned by make() are owned by LLEventPumps, as with obtain(). Introduce LLEventPumps::BadType exception for when the type string isn't recognized. LLEventPumps::obtain() can then simply call make() when the specified instance name doesn't already exist. The configuration data used internally by obtain() becomes { string instance name, string subclass name }. Although this too is currently initialized in the LLEventPumps constructor, migrating it to a configuration file would now be far more straightforward than before. LLLeapListener::newpump(), too, can call LLEventPumps::make() with the caller-specified type string. This eliminates that static factory map. newpump() must catch BadType and report the error back to its invoker. Given that the LLEventPump subclass instances returned by make() are owned by LLEventPumps rather than LLLeapListener, there is no further need for the LLLeapListener::mEventPumps ptr_map, which was used only to manage lifetime. Also remove LLLeapListener's "killpump" operation since LLEventPumps provides no corresponding functionality. --- indra/llcommon/llevents.cpp | 56 ++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 16 deletions(-) (limited to 'indra/llcommon/llevents.cpp') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index d31f5f2d32..281a1121bd 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -66,14 +66,21 @@ /***************************************************************************** * LLEventPumps *****************************************************************************/ -LLEventPumps::PumpFactories LLEventPumps::mFactories -{ - // LLEventStream is the default for obtain(), so even if somebody DOES - // call obtain("placeholder"), this sample entry won't break anything. - { "placeholder", [](const std::string& name) { return new LLEventStream(name); } } -}; - -LLEventPumps::LLEventPumps() {} +LLEventPumps::LLEventPumps(): + mFactories + { + { "LLEventStream", [](const std::string& name, bool tweak) + { return new LLEventStream(name, tweak); } }, + { "LLEventMailDrop", [](const std::string& name, bool tweak) + { return new LLEventMailDrop(name, tweak); } } + }, + mTypes + { + // LLEventStream is the default for obtain(), so even if somebody DOES + // call obtain("placeholder"), this sample entry won't break anything. + { "placeholder", "LLEventStream" } + } +{} LLEventPump& LLEventPumps::obtain(const std::string& name) { @@ -84,14 +91,31 @@ LLEventPump& LLEventPumps::obtain(const std::string& name) // name. return *found->second; } - // Here we must instantiate an LLEventPump subclass. - LLEventPump* newInstance; - // Do we have a predefined factory for this instance name? - PumpFactories::const_iterator nfound = mFactories.find(name); - if (nfound != mFactories.end()) - newInstance = (nfound->second)(name); - else - newInstance = new LLEventStream(name); + + // Here we must instantiate an LLEventPump subclass. Is there a + // preregistered class name override for this specific instance name? + auto nfound = mTypes.find(name); + std::string type; + if (nfound != mTypes.end()) + { + type = nfound->second; + } + // pass tweak=false: we already know there's no existing instance with + // this name + return make(name, false, type); +} + +LLEventPump& LLEventPumps::make(const std::string& name, bool tweak, + const std::string& type) +{ + // find the relevant factory for this (or default) type + auto found = mFactories.find(type.empty()? "LLEventStream" : type); + if (found == mFactories.end()) + { + // Passing an unrecognized type name is a no-no + LLTHROW(BadType(type)); + } + auto newInstance = (found->second)(name, tweak); // LLEventPump's constructor implicitly registers each new instance in // mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll // delete it later. -- cgit v1.2.3 From aea1e469820d276d0bc077fb8b3a77a5e6a35faf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 22 Oct 2019 16:27:59 -0400 Subject: DRTVWR-476: Make ~LLEventPumps() call reset() on its way out. ~LLEventPumps() deletes every LLEventPump instance it created itself. However, many classes themselves contain LLEventPump subclass instances. These are registered with LLEventPumps without it managing their lifespan. But LLEventPump::reset() frees the LLStandardSignal aka boost::signals2::signal instance owned by the LLEventPump, perforce disconnecting all current listeners and disabling the LLEventPump. Even though the instance still exists, if someone subsequently calls post(), nothing will happen -- which is better than control trying to reach a method of a deleted object. --- indra/llcommon/llevents.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/llcommon/llevents.cpp') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 281a1121bd..64fb985951 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -266,6 +266,9 @@ LLEventPumps::~LLEventPumps() { delete *mOurPumps.begin(); } + // Reset every remaining registered LLEventPump subclass instance: those + // we DIDN'T instantiate using either make() or obtain(). + reset(); } /***************************************************************************** -- cgit v1.2.3