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.
master
Nat Goodspeed 2018-09-27 14:57:03 -04:00
parent e1826d508c
commit ec6487f3a7
2 changed files with 28 additions and 15 deletions

View File

@ -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);
}

View File

@ -650,15 +650,21 @@ public:
* 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.
*/