summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2019-10-17 13:26:51 -0400
committerNat Goodspeed <nat@lindenlab.com>2020-03-25 18:58:16 -0400
commit79a3e391d3c992925230ff01551747e7edccb0ca (patch)
tree77b60d2f8870e74b21760d44b3b32ec8223f8f24
parent6805e82acdcde9a3f18681427a33a5bc7be7a0a6 (diff)
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.
-rw-r--r--indra/llcommon/llevents.cpp102
-rw-r--r--indra/llcommon/llevents.h39
-rw-r--r--indra/llcommon/llleaplistener.cpp21
-rw-r--r--indra/test/llevents_tut.cpp57
4 files changed, 46 insertions, 173 deletions
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<LLStandardSignal> signal(mSignal);
- for ( ; ! queue.empty(); queue.pop_front())
- {
- (*signal)(queue.front());
- }
}
/*****************************************************************************
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index ce2aa2f3c9..c55351919e 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -37,6 +37,7 @@
#include <set>
#include <vector>
#include <deque>
+#include <functional>
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
@@ -55,7 +56,6 @@
#include <boost/visit_each.hpp>
#include <boost/ref.hpp> // reference_wrapper
#include <boost/type_traits/is_pointer.hpp>
-#include <boost/function.hpp>
#include <boost/static_assert.hpp>
#include "llsd.h"
#include "llsingleton.h"
@@ -303,10 +303,10 @@ testable:
// destroyed.
typedef std::set<LLEventPump*> PumpSet;
PumpSet mOurPumps;
- // LLEventPump names that should be instantiated as LLEventQueue rather
- // than as LLEventStream
- typedef std::set<std::string> PumpNames;
- PumpNames mQueueNames;
+ // LLEventPump subclasses that should be instantiated for particular
+ // instance names
+ typedef std::map<std::string, std::function<LLEventPump*(const std::string&)>> PumpFactories;
+ static PumpFactories mFactories;
};
/*****************************************************************************
@@ -351,7 +351,7 @@ typedef boost::signals2::trackable LLEventTrackable;
*****************************************************************************/
/**
* LLEventPump is the base class interface through which we access the
- * concrete subclasses LLEventStream and LLEventQueue.
+ * concrete subclasses such as LLEventStream.
*
* @NOTE
* LLEventPump derives from LLEventTrackable so that when you "chain"
@@ -594,11 +594,10 @@ public:
* event *must* eventually reach a listener that will consume it, else the
* queue will grow to arbitrary length.
*
- * @NOTE: When using an LLEventMailDrop (or LLEventQueue) with a LLEventTimeout or
+ * @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.
- * See llcoro::suspendUntilEventOnWithTimeout() for an example.
*/
class LL_COMMON_API LLEventMailDrop : public LLEventStream
{
@@ -623,30 +622,6 @@ private:
};
/*****************************************************************************
-* LLEventQueue
-*****************************************************************************/
-/**
- * LLEventQueue is a LLEventPump whose post() method defers calling registered
- * listeners until flush() is called.
- */
-class LL_COMMON_API LLEventQueue: public LLEventPump
-{
-public:
- LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
- virtual ~LLEventQueue() {}
-
- /// Post an event to all listeners
- virtual bool post(const LLSD& event);
-
- /// flush queued events
- virtual void flush();
-
-private:
- typedef std::deque<LLSD> EventQueue;
- EventQueue mEventQueue;
-};
-
-/*****************************************************************************
* LLReqID
*****************************************************************************/
/**
diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp
index f50bacb1e8..0d18e5fff9 100644
--- a/indra/llcommon/llleaplistener.cpp
+++ b/indra/llcommon/llleaplistener.cpp
@@ -14,6 +14,8 @@
// associated header
#include "llleaplistener.h"
// STL headers
+#include <map>
+#include <functional>
// std headers
// external library headers
#include <boost/foreach.hpp>
@@ -119,6 +121,18 @@ LLLeapListener::~LLLeapListener()
}
}
+namespace
+{
+
+static std::map<std::string, std::function<LLEventPump*(const std::string&)>> factories
+{
+ // tweak name for uniqueness
+ { "LLEventStream", [](const std::string& name){ return new LLEventStream(name, true); } },
+ { "LLEventMailDrop", [](const std::string& name){ return new LLEventMailDrop(name, true); } }
+};
+
+} // anonymous namespace
+
void LLLeapListener::newpump(const LLSD& request)
{
Response reply(LLSD(), request);
@@ -127,13 +141,14 @@ void LLLeapListener::newpump(const LLSD& request)
LLSD const & type = request["type"];
LLEventPump * new_pump = NULL;
- if (type.asString() == "LLEventQueue")
+ auto found = factories.find(type.asString());
+ if (found != factories.end())
{
- new_pump = new LLEventQueue(name, true); // tweak name for uniqueness
+ new_pump = (found->second)(name);
}
else
{
- if (! (type.isUndefined() || type.asString() == "LLEventStream"))
+ if (! type.isUndefined())
{
reply.warn(STRINGIZE("unknown 'type' " << type << ", using LLEventStream"));
}
diff --git a/indra/test/llevents_tut.cpp b/indra/test/llevents_tut.cpp
index a8a3188249..17f64a4953 100644
--- a/indra/test/llevents_tut.cpp
+++ b/indra/test/llevents_tut.cpp
@@ -91,9 +91,7 @@ template<> template<>
void events_object::test<1>()
{
set_test_name("basic operations");
- // Now there's a static constructor in llevents.cpp that registers on
- // the "mainloop" pump to call LLEventPumps::flush().
- // Actually -- having to modify this to track the statically-
+ // Having to modify this to track the statically-
// constructed pumps in other TUT modules in this giant monolithic test
// executable isn't such a hot idea.
// ensure_equals("initial pump", pumps.mPumpMap.size(), 1);
@@ -210,43 +208,6 @@ bool chainEvents(Listener& someListener, const LLSD& event)
template<> template<>
void events_object::test<3>()
{
- set_test_name("LLEventQueue delayed action");
- // This access is NOT legal usage: we can do it only because we're
- // hacking private for test purposes. Normally we'd either compile in
- // a particular name, or (later) edit a config file.
- pumps.mQueueNames.insert("login");
- LLEventPump& login(pumps.obtain("login"));
- // The "mainloop" pump is special: posting on that implicitly calls
- // LLEventPumps::flush(), which in turn should flush our "login"
- // LLEventQueue.
- LLEventPump& mainloop(pumps.obtain("mainloop"));
- ensure("LLEventQueue leaf class", dynamic_cast<LLEventQueue*> (&login));
- listener0.listenTo(login);
- listener0.reset(0);
- login.post(1);
- check_listener("waiting for queued event", listener0, 0);
- mainloop.post(LLSD());
- check_listener("got queued event", listener0, 1);
- login.stopListening(listener0.getName());
- // Verify that when an event handler posts a new event on the same
- // LLEventQueue, it doesn't get processed in the same flush() call --
- // it waits until the next flush() call.
- listener0.reset(17);
- login.listen("chainEvents", boost::bind(chainEvents, boost::ref(listener0), _1));
- login.post(1);
- check_listener("chainEvents(1) not yet called", listener0, 17);
- mainloop.post(LLSD());
- check_listener("chainEvents(1) called", listener0, 1);
- mainloop.post(LLSD());
- check_listener("chainEvents(0) called", listener0, 0);
- mainloop.post(LLSD());
- check_listener("chainEvents(-1) not called", listener0, 0);
- login.stopListening("chainEvents");
-}
-
-template<> template<>
-void events_object::test<4>()
-{
set_test_name("explicitly-instantiated LLEventStream");
// Explicitly instantiate an LLEventStream, and verify that it
// self-registers with LLEventPumps
@@ -270,7 +231,7 @@ void events_object::test<4>()
}
template<> template<>
-void events_object::test<5>()
+void events_object::test<4>()
{
set_test_name("stopListening()");
LLEventPump& login(pumps.obtain("login"));
@@ -284,7 +245,7 @@ void events_object::test<5>()
}
template<> template<>
-void events_object::test<6>()
+void events_object::test<5>()
{
set_test_name("chaining LLEventPump instances");
LLEventPump& upstream(pumps.obtain("upstream"));
@@ -309,7 +270,7 @@ void events_object::test<6>()
}
template<> template<>
-void events_object::test<7>()
+void events_object::test<6>()
{
set_test_name("listener dependency order");
typedef LLEventPump::NameList NameList;
@@ -391,7 +352,7 @@ void events_object::test<7>()
}
template<> template<>
-void events_object::test<8>()
+void events_object::test<7>()
{
set_test_name("tweaked and untweaked LLEventPump instance names");
{ // nested scope
@@ -423,7 +384,7 @@ void eventSource(const LLListenerOrPumpName& listener)
}
template<> template<>
-void events_object::test<9>()
+void events_object::test<8>()
{
set_test_name("LLListenerOrPumpName");
// Passing a boost::bind() expression to LLListenerOrPumpName
@@ -464,7 +425,7 @@ private:
};
template<> template<>
-void events_object::test<10>()
+void events_object::test<9>()
{
set_test_name("listen(boost::bind(...TempListener...))");
// listen() can't do anything about a plain TempListener instance:
@@ -501,7 +462,7 @@ public:
};
template<> template<>
-void events_object::test<11>()
+void events_object::test<10>()
{
set_test_name("listen(boost::bind(...TempTrackableListener ref...))");
bool live = false;
@@ -524,7 +485,7 @@ void events_object::test<11>()
}
template<> template<>
-void events_object::test<12>()
+void events_object::test<11>()
{
set_test_name("listen(boost::bind(...TempTrackableListener pointer...))");
bool live = false;