diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 186e710c43c336a9e49876296cbfb34fab81c91e..d31f5f2d321e24cf7e4d8b7dedcdd80db632d588 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 ce2aa2f3c9781a64e3235c42f934e9d5af7faec1..c55351919e792e1c3a8e5301050628832180b408 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 @@ class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps>,
     // 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 @@ class LL_COMMON_API LLEventStream: public LLEventPump
  * 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
 {
@@ -622,30 +621,6 @@ class LL_COMMON_API LLEventMailDrop : public LLEventStream
     EventList mEventHistory;
 };
 
-/*****************************************************************************
-*   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 f50bacb1e8f30b7f519d79b5107d1a0c1e6a93f6..0d18e5fff95c547db21643ff18c0d143b95e7ea5 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 a8a31882499460661d0857a1ebf395955e518d23..17f64a49531850c38e438119f37af7ecc9860191 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);
@@ -209,43 +207,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
@@ -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 @@ class TempListener: public Listener
 };
 
 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 @@ class TempTrackableListener: public TempListener, public LLEventTrackable
 };
 
 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;