From a4ff9caf69cb8fbbf3e5d40a258ec99a070b0f94 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Wed, 15 Jun 2022 19:43:53 -0400
Subject: [PATCH] DRTVWR-564: WIP: add LLEventPumps::registerPumpFactory()

and registerTypeFactory().

Untested.

This will support registering just-in-time LLEventAPI instances, instantiated
on demand.
---
 indra/llcommon/llevents.cpp | 44 ++++++++++++++++++++++++++++++++-----
 indra/llcommon/llevents.h   | 39 +++++++++++++++++++++++++++++++-
 2 files changed, 76 insertions(+), 7 deletions(-)

diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 0a213bddef3..5725dad9cc2 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -68,19 +68,51 @@
 LLEventPumps::LLEventPumps():
     mFactories
     {
-        { "LLEventStream",   [](const std::string& name, bool tweak)
+        { "LLEventStream",   [](const std::string& name, bool tweak, const std::string& /*type*/)
                              { return new LLEventStream(name, tweak); } },
-        { "LLEventMailDrop", [](const std::string& name, bool tweak)
+        { "LLEventMailDrop", [](const std::string& name, bool tweak, const std::string& /*type*/)
                              { 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" }
+//      { "placeholder", "LLEventStream" }
     }
 {}
 
+bool LLEventPumps::registerTypeFactory(const std::string& type, const TypeFactory& factory)
+{
+    auto found = mFactories.find(type);
+    // can't re-register a TypeFactory for a type name that's already registered
+    if (found != mFactories.end())
+        return false;
+    // doesn't already exist, go ahead and register
+    mFactories[type] = factory;
+    return true;
+}
+
+bool LLEventPumps::registerPumpFactory(const std::string& name, const PumpFactory& factory)
+{
+    // Do we already have a pump by this name?
+    if (mPumpMap.find(name) != mPumpMap.end())
+        return false;
+    // Do we already have an override for this pump name?
+    if (mTypes.find(name) != mTypes.end())
+        return false;
+    // Leverage the two-level lookup implemented by mTypes (pump name -> type
+    // name) and mFactories (type name -> factory). We could instead create a
+    // whole separate (pump name -> factory) map, and look in both; or we
+    // could change mTypes to (pump name -> factory) and, for typical type-
+    // based lookups, use a "factory" that looks up the real factory in
+    // mFactories. But this works, and we don't expect many calls to make() -
+    // either explicit or implicit via obtain().
+    // Create a bogus type name extremely unlikely to collide with an actual type.
+    static std::string nul(1, '\0');
+    std::string type_name{ nul + name };
+    mTypes[name] = type_name;
+    mFactories[type_name] = factory;
+    return true;
+}
+
 LLEventPump& LLEventPumps::obtain(const std::string& name)
 {
     PumpMap::iterator found = mPumpMap.find(name);
@@ -114,7 +146,7 @@ LLEventPump& LLEventPumps::make(const std::string& name, bool tweak,
         // Passing an unrecognized type name is a no-no
         LLTHROW(BadType(type));
     }
-    auto newInstance = (found->second)(name, tweak);
+    auto newInstance = (found->second)(name, tweak, type);
     // LLEventPump's constructor implicitly registers each new instance in
     // mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
     // delete it later.
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index ae6e5aabc92..38adc31121c 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -268,6 +268,43 @@ class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps>,
     LLEventPump& make(const std::string& name, bool tweak=false,
                       const std::string& type=std::string());
 
+    /// function passed to registerTypeFactory()
+    typedef std::function<LLEventPump*(const std::string& name, bool tweak, const std::string& type)> TypeFactory;
+
+    /**
+     * Register a TypeFactory for use with make(). When make() is called with
+     * the specified @a type string, call @a factory(name, tweak, type) to
+     * instantiate it.
+     *
+     * Returns true if successfully registered, false if there already exists
+     * a TypeFactory for the specified @a type name.
+     */
+    bool registerTypeFactory(const std::string& type, const TypeFactory& factory);
+
+    /// function passed to registerPumpFactory()
+    typedef std::function<LLEventPump*(const std::string&)> PumpFactory;
+
+    /**
+     * Register a PumpFactory for use with obtain(). When obtain() is called
+     * with the specified @a name string, if an LLEventPump with the specified
+     * @a name doesn't already exist, call @a factory(name) to instantiate it.
+     *
+     * Returns true if successfully registered, false if there already exists
+     * a factory override for the specified @a name.
+     *
+     * PumpFactory does not support @a tweak because it's only called when
+     * <i>that particular</i> @a name is passed to obtain(). Bear in mind that
+     * <tt>obtain(name)</tt> might still bypass the caller's PumpFactory for a
+     * couple different reasons:
+     *
+     * * registerPumpFactory() returns false because there's already a factory
+     *   override for the specified @name
+     * * between a successful <tt>registerPumpFactory(name)</tt> call (returns
+     *   true) and a call to <tt>obtain(name)</tt>, someone explicitly
+     *   instantiated an LLEventPump(name), so obtain(name) returned that.
+     */
+    bool registerPumpFactory(const std::string& name, const PumpFactory& factory);
+
     /**
      * Find the named LLEventPump instance. If it exists post the message to it.
      * If the pump does not exist, do nothing.
@@ -325,7 +362,7 @@ class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps>,
     typedef std::set<LLEventPump*> PumpSet;
     PumpSet mOurPumps;
     // for make(), map string type name to LLEventPump subclass factory function
-    typedef std::map<std::string, std::function<LLEventPump*(const std::string&, bool)>> PumpFactories;
+    typedef std::map<std::string, PumpFactory> PumpFactories;
     // Data used by make().
     // One might think mFactories and mTypes could reasonably be static. So
     // they could -- if not for the fact that make() or obtain() might be
-- 
GitLab