From ec6487f3a70a5067f6d8fc601437160bb4fa753f Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Thu, 27 Sep 2018 14:57:03 -0400
Subject: [PATCH] DRTVWR-474: Make LLEventMailDrop pass all saved events to
 listener.

Previously, LLEventMailDrop would send only the first queued event to a
newly-connected listener. If you wanted to flush all queued events, you'd have
to "pump" the queue by repeatedly disconnecting and reconnecting -- with no
good way to know when you'd caught up.

The new behavior makes LLEventMailDrop resemble a multi-valued future: a
rendezvous between producer and consumer that, once connected, pushes values
rather than requiring them to be pulled (as with a simple queue) -- regardless
of the relative order in which post() and listen() are called.
---
 indra/llcommon/llevents.cpp | 23 +++++++++++++++--------
 indra/llcommon/llevents.h   | 20 +++++++++++++-------
 2 files changed, 28 insertions(+), 15 deletions(-)

diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index dce97b5411d..eedd8c92b5a 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -545,10 +545,8 @@ bool LLEventStream::post(const LLSD& event)
  *****************************************************************************/
 bool LLEventMailDrop::post(const LLSD& event)
 {
-    bool posted = false;
-    
-    if (!mSignal->empty())
-        posted = LLEventStream::post(event);
+    // forward the call to our base class
+    bool posted = LLEventStream::post(event);
     
     if (!posted)
     {   // if the event was not handled we will save it for later so that it can 
@@ -564,16 +562,25 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name,
                                     const NameList& after,
                                     const NameList& before)
 {
-    if (!mEventHistory.empty())
+    // Before actually connecting this listener for subsequent post() calls,
+    // first feed each of the saved events, in order, to the new listener.
+    // Remove any that this listener consumes -- Effective STL, Item 9.
+    for (auto hi(mEventHistory.begin()), hend(mEventHistory.end()); hi != hend; )
     {
-        if (listener(mEventHistory.front()))
+        if (listener(*hi))
         {
-            mEventHistory.pop_front();
+            // new listener consumed this event, erase it
+            hi = mEventHistory.erase(hi);
+        }
+        else
+        {
+            // listener did not consume this event, just move along
+            ++hi;
         }
     }
 
+    // let base class perform the actual connection
     return LLEventStream::listen_impl(name, listener, after, before);
-
 }
 
 
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 1d51c660ed0..5d60c638101 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -650,15 +650,21 @@ class LL_COMMON_API LLEventStream: public LLEventPump
  *   LLEventMailDrop
  *****************************************************************************/
 /**
- * LLEventMailDrop is a specialization of LLEventStream. Events are posted normally, 
- * however if no listeners return that they have handled the event it is placed in 
- * a queue. Subsequent attaching listeners will receive stored events from the queue 
- * until a listener indicates that the event has been handled.  In order to receive 
- * multiple events from a mail drop the listener must disconnect and reconnect.
+ * LLEventMailDrop is a specialization of LLEventStream. Events are posted
+ * normally, however if no listener returns that it has handled the event
+ * (returns true), it is placed in a queue. Subsequent attaching listeners
+ * will receive stored events from the queue until some listener indicates
+ * that the event has been handled.
+ *
+ * LLEventMailDrop completely decouples the timing of post() calls from
+ * listen() calls: every event posted to an LLEventMailDrop is eventually seen
+ * 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 (or LLEventQueue) with a LLEventTimeout or
- * LLEventFilter attaching the filter downstream using Timeout's constructor will
- * cause the MailDrop to discharge any of it's stored events. The timeout should 
+ * 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.
  */
-- 
GitLab