From 3800c0df910c83e987184d541b868168fc2b5bec Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Fri, 8 May 2009 21:08:08 +0000
Subject: svn merge -r114679:114681
 svn+ssh://svn.lindenlab.com/svn/linden/branches/event-system/event-system-7
 svn+ssh://svn.lindenlab.com/svn/linden/branches/event-system/event-system-8

---
 indra/llcommon/llevents.cpp | 501 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 501 insertions(+)
 create mode 100644 indra/llcommon/llevents.cpp

(limited to 'indra/llcommon/llevents.cpp')

diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
new file mode 100644
index 0000000000..eb380ba7c8
--- /dev/null
+++ b/indra/llcommon/llevents.cpp
@@ -0,0 +1,501 @@
+/**
+ * @file   llevents.cpp
+ * @author Nat Goodspeed
+ * @date   2008-09-12
+ * @brief  Implementation for llevents.
+ * 
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * Copyright (c) 2008, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+
+#if LL_WINDOWS
+#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
+#endif
+
+// associated header
+#include "llevents.h"
+// STL headers
+#include <set>
+#include <sstream>
+#include <algorithm>
+// std headers
+#include <typeinfo>
+#include <cassert>
+#include <cmath>
+#include <cctype>
+// external library headers
+#include <boost/range/iterator_range.hpp>
+#if LL_WINDOWS
+#pragma warning (push)
+#pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
+#endif
+#include <boost/lexical_cast.hpp>
+#if LL_WINDOWS
+#pragma warning (pop)
+#endif
+// other Linden headers
+
+/*****************************************************************************
+*   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
+*****************************************************************************/
+struct RegisterFlush
+{
+    RegisterFlush():
+        pumps(LLEventPumps::instance()),
+        mainloop(pumps.obtain("mainloop")),
+        name("flushLLEventQueues")
+    {
+        mainloop.listen(name, boost::bind(&RegisterFlush::flush, this, _1));
+    }
+    bool flush(const LLSD&)
+    {
+        pumps.flush();
+        return false;
+    }
+    ~RegisterFlush()
+    {
+        mainloop.stopListening(name);
+    }
+    LLEventPumps& pumps;
+    LLEventPump& mainloop;
+    const std::string 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))
+{
+}
+
+LLEventPump& LLEventPumps::obtain(const std::string& name)
+{
+    PumpMap::iterator found = mPumpMap.find(name);
+    if (found != mPumpMap.end())
+    {
+        // Here we already have an LLEventPump instance with the requested
+        // name.
+        return *found->second;
+    }
+    // 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);
+    else
+        newInstance = new LLEventStream(name);
+    // LLEventPump's constructor implicitly registers each new instance in
+    // mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
+    // delete it later.
+    mOurPumps.insert(newInstance);
+    return *newInstance;
+}
+
+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)
+    {
+        pmi->second->flush();
+    }
+}
+
+std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string& name, bool tweak)
+{
+    std::pair<PumpMap::iterator, bool> inserted =
+        mPumpMap.insert(PumpMap::value_type(name, const_cast<LLEventPump*>(&pump)));
+    // If the insert worked, then the name is unique; return that.
+    if (inserted.second)
+        return name;
+    // Here the new entry was NOT inserted, and therefore name isn't unique.
+    // Unless we're permitted to tweak it, that's Bad.
+    if (! tweak)
+    {
+        throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'");
+    }
+    // The passed name isn't unique, but we're permitted to tweak it. Find the
+    // first decimal-integer suffix not already taken. The insert() attempt
+    // above will have set inserted.first to the iterator of the existing
+    // entry by that name. Starting there, walk forward until we reach an
+    // entry that doesn't start with 'name'. For each entry consisting of name
+    // + integer suffix, capture the integer suffix in a set. Use a set
+    // because we're going to encounter string suffixes in the order: name1,
+    // name10, name11, name2, ... Walking those possibilities in that order
+    // isn't convenient to detect the first available "hole."
+    std::set<int> suffixes;
+    PumpMap::iterator pmi(inserted.first), pmend(mPumpMap.end());
+    // We already know inserted.first references the existing entry with
+    // 'name' as the key; skip that one and start with the next.
+    while (++pmi != pmend)
+    {
+        if (pmi->first.substr(0, name.length()) != name)
+        {
+            // Found the first entry beyond the entries starting with 'name':
+            // stop looping.
+            break;
+        }
+        // Here we're looking at an entry that starts with 'name'. Is the rest
+        // of it an integer?
+        // Dubious (?) assumption: in the local character set, decimal digits
+        // are in increasing order such that '9' is the last of them. This
+        // test deals with 'name' values such as 'a', where there might be a
+        // very large number of entries starting with 'a' whose suffixes
+        // aren't integers. A secondary assumption is that digit characters
+        // precede most common name characters (true in ASCII, false in
+        // EBCDIC). The test below is correct either way, but it's worth more
+        // if the assumption holds.
+        if (pmi->first[name.length()] > '9')
+            break;
+        // It should be cheaper to detect that we're not looking at a digit
+        // character -- and therefore the suffix can't possibly be an integer
+        // -- than to attempt the lexical_cast and catch the exception.
+        if (! std::isdigit(pmi->first[name.length()]))
+            continue;
+        // Okay, the first character of the suffix is a digit, it's worth at
+        // least attempting to convert to int.
+        try
+        {
+            suffixes.insert(boost::lexical_cast<int>(pmi->first.substr(name.length())));
+        }
+        catch (const boost::bad_lexical_cast&)
+        {
+            // If the rest of pmi->first isn't an int, just ignore it.
+        }
+    }
+    // Here we've accumulated in 'suffixes' all existing int suffixes of the
+    // entries starting with 'name'. Find the first unused one.
+    int suffix = 1;
+    for ( ; suffixes.find(suffix) != suffixes.end(); ++suffix)
+        ;
+    // Here 'suffix' is not in 'suffixes'. Construct a new name based on that
+    // suffix, insert it and return it.
+    std::ostringstream out;
+    out << name << suffix;
+    return registerNew(pump, out.str(), tweak);
+}
+
+void LLEventPumps::unregister(const LLEventPump& pump)
+{
+    // Remove this instance from mPumpMap
+    PumpMap::iterator found = mPumpMap.find(pump.getName());
+    if (found != mPumpMap.end())
+    {
+        mPumpMap.erase(found);
+    }
+    // If this instance is one we created, also remove it from mOurPumps so we
+    // won't try again to delete it later!
+    PumpSet::iterator psfound = mOurPumps.find(const_cast<LLEventPump*>(&pump));
+    if (psfound != mOurPumps.end())
+    {
+        mOurPumps.erase(psfound);
+    }
+}
+
+LLEventPumps::~LLEventPumps()
+{
+    // On destruction, delete every LLEventPump we instantiated (via
+    // obtain()). CAREFUL: deleting an LLEventPump calls its destructor, which
+    // calls unregister(), which removes that LLEventPump instance from
+    // mOurPumps. So an iterator loop over mOurPumps to delete contained
+    // LLEventPump instances is dangerous! Instead, delete them one at a time
+    // until mOurPumps is empty.
+    while (! mOurPumps.empty())
+    {
+        delete *mOurPumps.begin();
+    }
+}
+
+/*****************************************************************************
+*   LLEventPump
+*****************************************************************************/
+#if LL_WINDOWS
+#pragma warning (push)
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+LLEventPump::LLEventPump(const std::string& name, bool tweak):
+    // Register every new instance with LLEventPumps
+    mName(LLEventPumps::instance().registerNew(*this, name, tweak)),
+    mEnabled(true)
+{}
+
+#if LL_WINDOWS
+#pragma warning (pop)
+#endif
+
+LLEventPump::~LLEventPump()
+{
+    // Unregister this doomed instance from LLEventPumps
+    LLEventPumps::instance().unregister(*this);
+}
+
+// static data member
+const LLEventPump::NameList LLEventPump::empty;
+
+LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
+                                         const NameList& after,
+                                         const NameList& before)
+{
+    // Check for duplicate name before connecting listener to mSignal
+    ConnectionMap::const_iterator found = mConnections.find(name);
+    // In some cases the user might disconnect a connection explicitly -- or
+    // might use LLEventTrackable to disconnect implicitly. Either way, we can
+    // end up retaining in mConnections a zombie connection object that's
+    // already been disconnected. Such a connection object can't be
+    // reconnected -- nor, in the case of LLEventTrackable, would we want to
+    // try, since disconnection happens with the destruction of the listener
+    // object. That means it's safe to overwrite a disconnected connection
+    // object with the new one we're attempting. The case we want to prevent
+    // is only when the existing connection object is still connected.
+    if (found != mConnections.end() && found->second.connected())
+    {
+        throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
+                              "' on " + typeid(*this).name() + " '" + getName() + "'");
+    }
+    // Okay, name is unique, try to reconcile its dependencies. Specify a new
+    // "node" value that we never use for an mSignal placement; we'll fix it
+    // later.
+    DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
+    // What if this listener has been added, removed and re-added? In that
+    // case newNode already has a non-negative value because we never remove a
+    // listener from mDeps. But keep processing uniformly anyway in case the
+    // listener was added back with different dependencies. Then mDeps.sort()
+    // would put it in a different position, and the old newNode placement
+    // value would be wrong, so we'd have to reassign it anyway. Trust that
+    // re-adding a listener with the same dependencies is the trivial case for
+    // mDeps.sort(): it can just replay its cache.
+    DependencyMap::sorted_range sorted_range;
+    try
+    {
+        // Can we pick an order that works including this new entry?
+        sorted_range = mDeps.sort();
+    }
+    catch (const DependencyMap::Cycle& e)
+    {
+        // No: the new node's after/before dependencies have made mDeps
+        // unsortable. If we leave the new node in mDeps, it will continue
+        // to screw up all future attempts to sort()! Pull it out.
+        mDeps.remove(name);
+        throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
+                    " '" + getName() + "' would cause cycle: " + e.what());
+    }
+    // Walk the list to verify that we haven't changed the order.
+    float previous = 0.0, myprev = 0.0;
+    DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
+    for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
+         dmi != sorted_range.end(); ++dmi)
+    {
+        // Since we've added the new entry with an invalid placement,
+        // recognize it and skip it.
+        if (dmi->first == name)
+        {
+            // Remember the iterator belonging to our new node, and which
+            // placement value was 'previous' at that point.
+            mydmi = dmi;
+            myprev = previous;
+            continue;
+        }
+        // If the new node has rearranged the existing nodes, we'll find
+        // that their placement values are no longer in increasing order.
+        if (dmi->second < previous)
+        {
+            // This is another scenario in which we'd better back out the
+            // newly-added node from mDeps -- but don't do it yet, we want to
+            // traverse the existing mDeps to report on it!
+            // Describe the change to the order of our listeners. Copy
+            // everything but the newest listener to a vector we can sort to
+            // obtain the old order.
+            typedef std::vector< std::pair<float, std::string> > SortNameList;
+            SortNameList sortnames;
+            for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
+                 cdmi != cdmend; ++cdmi)
+            {
+                if (cdmi->first != name)
+                {
+                    sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
+                }
+            }
+            std::sort(sortnames.begin(), sortnames.end());
+            std::ostringstream out;
+            out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
+                << "' would move previous listener '" << dmi->first << "'\nwas: ";
+            SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
+            if (sni != snend)
+            {
+                out << sni->second;
+                while (++sni != snend)
+                {
+                    out << ", " << sni->second;
+                }
+            }
+            out << "\nnow: ";
+            DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
+            if (ddmi != ddmend)
+            {
+                out << ddmi->first;
+                while (++ddmi != ddmend)
+                {
+                    out << ", " << ddmi->first;
+                }
+            }
+            // NOW remove the offending listener node.
+            mDeps.remove(name);
+            // Having constructed a description of the order change, inform caller.
+            throw OrderChange(out.str());
+        }
+        // This node becomes the previous one.
+        previous = dmi->second;
+    }
+    // We just got done with a successful mDeps.add(name, ...) call. We'd
+    // better have found 'name' somewhere in that sorted list!
+    assert(mydmi != sorted_range.end());
+    // Four cases:
+    // 0. name is the only entry: placement 1.0
+    // 1. name is the first of several entries: placement (next placement)/2
+    // 2. name is between two other entries: placement (myprev + (next placement))/2
+    // 3. name is the last entry: placement ceil(myprev) + 1.0
+    // Since we've cleverly arranged for myprev to be 0.0 if name is the
+    // first entry, this folds down to two cases. Case 1 is subsumed by
+    // case 2, and case 0 is subsumed by case 3. So we need only handle
+    // cases 2 and 3, which means we need only detect whether name is the
+    // last entry. Increment mydmi to see if there's anything beyond.
+    if (++mydmi != sorted_range.end())
+    {
+        // The new node isn't last. Place it between the previous node and
+        // the successor.
+        newNode = (myprev + mydmi->second)/2.0;
+    }
+    else
+    {
+        // The new node is last. Bump myprev up to the next integer, add
+        // 1.0 and use that.
+        newNode = std::ceil(myprev) + 1.0;
+    }
+    // Now that newNode has a value that places it appropriately in mSignal,
+    // connect it.
+    LLBoundListener bound = mSignal.connect(newNode, listener);
+    mConnections[name] = bound;
+    return bound;
+}
+
+LLBoundListener LLEventPump::getListener(const std::string& name) const
+{
+    ConnectionMap::const_iterator found = mConnections.find(name);
+    if (found != mConnections.end())
+    {
+        return found->second;
+    }
+    // not found, return dummy LLBoundListener
+    return LLBoundListener();
+}
+
+void LLEventPump::stopListening(const std::string& name)
+{
+    ConnectionMap::iterator found = mConnections.find(name);
+    if (found != mConnections.end())
+    {
+        found->second.disconnect();
+        mConnections.erase(found);
+    }
+    // We intentionally do NOT remove this name from mDeps. It may happen that
+    // the same listener with the same name and dependencies will jump on and
+    // off this LLEventPump repeatedly. Keeping a cache of dependencies will
+    // avoid a new dependency sort in such cases.
+}
+
+/*****************************************************************************
+*   LLEventStream
+*****************************************************************************/
+bool LLEventStream::post(const LLSD& event)
+{
+    if (! mEnabled)
+        return false;
+    // Let caller know if any one listener handled the event. This is mostly
+    // useful when using LLEventStream as a listener for an upstream
+    // LLEventPump.
+    return mSignal(event);
+}
+
+/*****************************************************************************
+*   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()
+{
+    // 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();
+    for ( ; ! queue.empty(); queue.pop_front())
+    {
+        mSignal(queue.front());
+    }
+}
+
+/*****************************************************************************
+*   LLListenerOrPumpName
+*****************************************************************************/
+LLListenerOrPumpName::LLListenerOrPumpName(const std::string& pumpname):
+    // Look up the specified pumpname, and bind its post() method as our listener
+    mListener(boost::bind(&LLEventPump::post,
+                          boost::ref(LLEventPumps::instance().obtain(pumpname)),
+                          _1))
+{
+}
+
+LLListenerOrPumpName::LLListenerOrPumpName(const char* pumpname):
+    // Look up the specified pumpname, and bind its post() method as our listener
+    mListener(boost::bind(&LLEventPump::post,
+                          boost::ref(LLEventPumps::instance().obtain(pumpname)),
+                          _1))
+{
+}
+
+bool LLListenerOrPumpName::operator()(const LLSD& event) const
+{
+    if (! mListener)
+    {
+        throw Empty("attempting to call uninitialized");
+    }
+    return (*mListener)(event);
+}
-- 
cgit v1.2.3


From dc934629919bdcaea72c78e5291263914fb958ec Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Mon, 11 May 2009 20:05:46 +0000
Subject: svn merge -r113003:119136
 svn+ssh://svn.lindenlab.com/svn/linden/branches/login-api/login-api-2
 svn+ssh://svn.lindenlab.com/svn/linden/branches/login-api/login-api-3

---
 indra/llcommon/llevents.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

(limited to 'indra/llcommon/llevents.cpp')

diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index eb380ba7c8..7e3c6964dc 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -38,6 +38,7 @@
 #pragma warning (pop)
 #endif
 // other Linden headers
+#include "stringize.h"
 
 /*****************************************************************************
 *   queue_names: specify LLEventPump names that should be instantiated as
@@ -256,6 +257,12 @@ LLEventPump::~LLEventPump()
 // static data member
 const LLEventPump::NameList LLEventPump::empty;
 
+std::string LLEventPump::inventName(const std::string& pfx)
+{
+    static long suffix = 0;
+    return STRINGIZE(pfx << suffix++);
+}
+
 LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
                                          const NameList& after,
                                          const NameList& before)
-- 
cgit v1.2.3


From f910157c1662dedb9791efc1439ff09f1f3efbf8 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Wed, 27 May 2009 21:17:22 +0000
Subject: DEV-31979: Introduce LLReqID, a class to help individual event API
 listeners implement the ["reqid"] convention. This convention dictates that a
 response LLSD from each such API should contain a ["reqid"] key whose value
 echoes the ["reqid"] value, if any, in the request LLSD. Add LLReqID support
 to LLAresListener's "rewriteURI" service, LLSDMessage, LLCapabilityListener
 and LLXMLRPCListener.

---
 indra/llcommon/llevents.cpp | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

(limited to 'indra/llcommon/llevents.cpp')

diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 7e3c6964dc..c2fa79a524 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -39,6 +39,8 @@
 #endif
 // other Linden headers
 #include "stringize.h"
+#include "llerror.h"
+#include "llsdutil.h"
 
 /*****************************************************************************
 *   queue_names: specify LLEventPump names that should be instantiated as
@@ -506,3 +508,26 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const
     }
     return (*mListener)(event);
 }
+
+void LLReqID::stamp(LLSD& response) const
+{
+    if (! (response.isUndefined() || response.isMap()))
+    {
+        // If 'response' was previously completely empty, it's okay to
+        // turn it into a map. If it was already a map, then it should be
+        // okay to add a key. But if it was anything else (e.g. a scalar),
+        // assigning a ["reqid"] key will DISCARD the previous value,
+        // replacing it with a map. That would be Bad.
+        LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: "
+                            << response << LL_ENDL;
+        return;
+    }
+    LLSD oldReqid(response["reqid"]);
+    if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid)))
+    {
+        LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing [\"reqid\"] value "
+                            << oldReqid << " in response: " << response << LL_ENDL;
+        return;
+    }
+    response["reqid"] = mReqid;
+}
-- 
cgit v1.2.3


From 843036a7d27dbffa4aafeae70b3e46f9afe84c1b Mon Sep 17 00:00:00 2001
From: brad kittenbrink <brad@lindenlab.com>
Date: Thu, 6 Aug 2009 15:01:26 -0700
Subject: Fix for lllogin_tests crash on shutdown. RegisterFlush destructor was
 dereferencing a dangling reference to the mainloop EventPump which had
 already been destructed.

---
 indra/llcommon/llevents.cpp | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

(limited to 'indra/llcommon/llevents.cpp')

diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index c2fa79a524..aec9acc7ef 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -59,14 +59,12 @@ const char* queue_names[] =
 /*****************************************************************************
 *   If there's a "mainloop" pump, listen on that to flush all LLEventQueues
 *****************************************************************************/
-struct RegisterFlush
+struct RegisterFlush : public LLEventTrackable
 {
     RegisterFlush():
-        pumps(LLEventPumps::instance()),
-        mainloop(pumps.obtain("mainloop")),
-        name("flushLLEventQueues")
+        pumps(LLEventPumps::instance())
     {
-        mainloop.listen(name, boost::bind(&RegisterFlush::flush, this, _1));
+        pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
     }
     bool flush(const LLSD&)
     {
@@ -75,11 +73,9 @@ struct RegisterFlush
     }
     ~RegisterFlush()
     {
-        mainloop.stopListening(name);
+        // LLEventTrackable handles stopListening for us.
     }
     LLEventPumps& pumps;
-    LLEventPump& mainloop;
-    const std::string name;
 };
 static RegisterFlush registerFlush;
 
-- 
cgit v1.2.3


From c3e8c1f738b14de74b23b3a7276ef4dc083c0887 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Thu, 24 Sep 2009 13:46:02 -0400
Subject: Disable MSVC "fatal warning" 4702 for boost::lexical_cast in Release
 build

---
 indra/llcommon/llevents.cpp | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'indra/llcommon/llevents.cpp')

diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index aec9acc7ef..a6421ac696 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -41,6 +41,9 @@
 #include "stringize.h"
 #include "llerror.h"
 #include "llsdutil.h"
+#if LL_MSVC
+#pragma warning (disable : 4702)
+#endif
 
 /*****************************************************************************
 *   queue_names: specify LLEventPump names that should be instantiated as
-- 
cgit v1.2.3