PageRenderTime 62ms CodeModel.GetById 15ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/llevents.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 614 lines | 347 code | 35 blank | 232 comment | 61 complexity | 37403f6e3662bf4533cd129b9480034b MD5 | raw file
  1/**
  2 * @file   llevents.cpp
  3 * @author Nat Goodspeed
  4 * @date   2008-09-12
  5 * @brief  Implementation for llevents.
  6 * 
  7 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29// Precompiled header
 30#include "linden_common.h"
 31
 32#if LL_WINDOWS
 33#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
 34#endif
 35
 36// associated header
 37#include "llevents.h"
 38// STL headers
 39#include <set>
 40#include <sstream>
 41#include <algorithm>
 42// std headers
 43#include <typeinfo>
 44#include <cassert>
 45#include <cmath>
 46#include <cctype>
 47// external library headers
 48#include <boost/range/iterator_range.hpp>
 49#if LL_WINDOWS
 50#pragma warning (push)
 51#pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
 52#endif
 53#include <boost/lexical_cast.hpp>
 54#if LL_WINDOWS
 55#pragma warning (pop)
 56#endif
 57// other Linden headers
 58#include "stringize.h"
 59#include "llerror.h"
 60#include "llsdutil.h"
 61#if LL_MSVC
 62#pragma warning (disable : 4702)
 63#endif
 64
 65/*****************************************************************************
 66*   queue_names: specify LLEventPump names that should be instantiated as
 67*   LLEventQueue
 68*****************************************************************************/
 69/**
 70 * At present, we recognize particular requested LLEventPump names as needing
 71 * LLEventQueues. Later on we'll migrate this information to an external
 72 * configuration file.
 73 */
 74const char* queue_names[] =
 75{
 76    "placeholder - replace with first real name string"
 77};
 78
 79/*****************************************************************************
 80*   If there's a "mainloop" pump, listen on that to flush all LLEventQueues
 81*****************************************************************************/
 82struct RegisterFlush : public LLEventTrackable
 83{
 84    RegisterFlush():
 85        pumps(LLEventPumps::instance())
 86    {
 87        pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
 88    }
 89    bool flush(const LLSD&)
 90    {
 91        pumps.flush();
 92        return false;
 93    }
 94    ~RegisterFlush()
 95    {
 96        // LLEventTrackable handles stopListening for us.
 97    }
 98    LLEventPumps& pumps;
 99};
100static RegisterFlush registerFlush;
101
102/*****************************************************************************
103*   LLEventPumps
104*****************************************************************************/
105LLEventPumps::LLEventPumps():
106    // Until we migrate this information to an external config file,
107    // initialize mQueueNames from the static queue_names array.
108    mQueueNames(boost::begin(queue_names), boost::end(queue_names))
109{
110}
111
112LLEventPump& LLEventPumps::obtain(const std::string& name)
113{
114    PumpMap::iterator found = mPumpMap.find(name);
115    if (found != mPumpMap.end())
116    {
117        // Here we already have an LLEventPump instance with the requested
118        // name.
119        return *found->second;
120    }
121    // Here we must instantiate an LLEventPump subclass. 
122    LLEventPump* newInstance;
123    // Should this name be an LLEventQueue?
124    PumpNames::const_iterator nfound = mQueueNames.find(name);
125    if (nfound != mQueueNames.end())
126        newInstance = new LLEventQueue(name);
127    else
128        newInstance = new LLEventStream(name);
129    // LLEventPump's constructor implicitly registers each new instance in
130    // mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
131    // delete it later.
132    mOurPumps.insert(newInstance);
133    return *newInstance;
134}
135
136void LLEventPumps::flush()
137{
138    // Flush every known LLEventPump instance. Leave it up to each instance to
139    // decide what to do with the flush() call.
140    for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
141    {
142        pmi->second->flush();
143    }
144}
145
146void LLEventPumps::reset()
147{
148    // Reset every known LLEventPump instance. Leave it up to each instance to
149    // decide what to do with the reset() call.
150    for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
151    {
152        pmi->second->reset();
153    }
154}
155
156std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string& name, bool tweak)
157{
158    std::pair<PumpMap::iterator, bool> inserted =
159        mPumpMap.insert(PumpMap::value_type(name, const_cast<LLEventPump*>(&pump)));
160    // If the insert worked, then the name is unique; return that.
161    if (inserted.second)
162        return name;
163    // Here the new entry was NOT inserted, and therefore name isn't unique.
164    // Unless we're permitted to tweak it, that's Bad.
165    if (! tweak)
166    {
167        throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'");
168    }
169    // The passed name isn't unique, but we're permitted to tweak it. Find the
170    // first decimal-integer suffix not already taken. The insert() attempt
171    // above will have set inserted.first to the iterator of the existing
172    // entry by that name. Starting there, walk forward until we reach an
173    // entry that doesn't start with 'name'. For each entry consisting of name
174    // + integer suffix, capture the integer suffix in a set. Use a set
175    // because we're going to encounter string suffixes in the order: name1,
176    // name10, name11, name2, ... Walking those possibilities in that order
177    // isn't convenient to detect the first available "hole."
178    std::set<int> suffixes;
179    PumpMap::iterator pmi(inserted.first), pmend(mPumpMap.end());
180    // We already know inserted.first references the existing entry with
181    // 'name' as the key; skip that one and start with the next.
182    while (++pmi != pmend)
183    {
184        if (pmi->first.substr(0, name.length()) != name)
185        {
186            // Found the first entry beyond the entries starting with 'name':
187            // stop looping.
188            break;
189        }
190        // Here we're looking at an entry that starts with 'name'. Is the rest
191        // of it an integer?
192        // Dubious (?) assumption: in the local character set, decimal digits
193        // are in increasing order such that '9' is the last of them. This
194        // test deals with 'name' values such as 'a', where there might be a
195        // very large number of entries starting with 'a' whose suffixes
196        // aren't integers. A secondary assumption is that digit characters
197        // precede most common name characters (true in ASCII, false in
198        // EBCDIC). The test below is correct either way, but it's worth more
199        // if the assumption holds.
200        if (pmi->first[name.length()] > '9')
201            break;
202        // It should be cheaper to detect that we're not looking at a digit
203        // character -- and therefore the suffix can't possibly be an integer
204        // -- than to attempt the lexical_cast and catch the exception.
205        if (! std::isdigit(pmi->first[name.length()]))
206            continue;
207        // Okay, the first character of the suffix is a digit, it's worth at
208        // least attempting to convert to int.
209        try
210        {
211            suffixes.insert(boost::lexical_cast<int>(pmi->first.substr(name.length())));
212        }
213        catch (const boost::bad_lexical_cast&)
214        {
215            // If the rest of pmi->first isn't an int, just ignore it.
216        }
217    }
218    // Here we've accumulated in 'suffixes' all existing int suffixes of the
219    // entries starting with 'name'. Find the first unused one.
220    int suffix = 1;
221    for ( ; suffixes.find(suffix) != suffixes.end(); ++suffix)
222        ;
223    // Here 'suffix' is not in 'suffixes'. Construct a new name based on that
224    // suffix, insert it and return it.
225    std::ostringstream out;
226    out << name << suffix;
227    return registerNew(pump, out.str(), tweak);
228}
229
230void LLEventPumps::unregister(const LLEventPump& pump)
231{
232    // Remove this instance from mPumpMap
233    PumpMap::iterator found = mPumpMap.find(pump.getName());
234    if (found != mPumpMap.end())
235    {
236        mPumpMap.erase(found);
237    }
238    // If this instance is one we created, also remove it from mOurPumps so we
239    // won't try again to delete it later!
240    PumpSet::iterator psfound = mOurPumps.find(const_cast<LLEventPump*>(&pump));
241    if (psfound != mOurPumps.end())
242    {
243        mOurPumps.erase(psfound);
244    }
245}
246
247LLEventPumps::~LLEventPumps()
248{
249    // On destruction, delete every LLEventPump we instantiated (via
250    // obtain()). CAREFUL: deleting an LLEventPump calls its destructor, which
251    // calls unregister(), which removes that LLEventPump instance from
252    // mOurPumps. So an iterator loop over mOurPumps to delete contained
253    // LLEventPump instances is dangerous! Instead, delete them one at a time
254    // until mOurPumps is empty.
255    while (! mOurPumps.empty())
256    {
257        delete *mOurPumps.begin();
258    }
259}
260
261/*****************************************************************************
262*   LLEventPump
263*****************************************************************************/
264#if LL_WINDOWS
265#pragma warning (push)
266#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
267#endif
268
269LLEventPump::LLEventPump(const std::string& name, bool tweak):
270    // Register every new instance with LLEventPumps
271    mName(LLEventPumps::instance().registerNew(*this, name, tweak)),
272    mSignal(new LLStandardSignal()),
273    mEnabled(true)
274{}
275
276#if LL_WINDOWS
277#pragma warning (pop)
278#endif
279
280LLEventPump::~LLEventPump()
281{
282    // Unregister this doomed instance from LLEventPumps
283    LLEventPumps::instance().unregister(*this);
284}
285
286// static data member
287const LLEventPump::NameList LLEventPump::empty;
288
289std::string LLEventPump::inventName(const std::string& pfx)
290{
291    static long suffix = 0;
292    return STRINGIZE(pfx << suffix++);
293}
294
295void LLEventPump::reset()
296{
297    mSignal.reset();
298    mConnections.clear();
299    //mDeps.clear();
300}
301
302LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
303                                         const NameList& after,
304                                         const NameList& before)
305{
306    // Check for duplicate name before connecting listener to mSignal
307    ConnectionMap::const_iterator found = mConnections.find(name);
308    // In some cases the user might disconnect a connection explicitly -- or
309    // might use LLEventTrackable to disconnect implicitly. Either way, we can
310    // end up retaining in mConnections a zombie connection object that's
311    // already been disconnected. Such a connection object can't be
312    // reconnected -- nor, in the case of LLEventTrackable, would we want to
313    // try, since disconnection happens with the destruction of the listener
314    // object. That means it's safe to overwrite a disconnected connection
315    // object with the new one we're attempting. The case we want to prevent
316    // is only when the existing connection object is still connected.
317    if (found != mConnections.end() && found->second.connected())
318    {
319        throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
320                              "' on " + typeid(*this).name() + " '" + getName() + "'");
321    }
322    // Okay, name is unique, try to reconcile its dependencies. Specify a new
323    // "node" value that we never use for an mSignal placement; we'll fix it
324    // later.
325    DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
326    // What if this listener has been added, removed and re-added? In that
327    // case newNode already has a non-negative value because we never remove a
328    // listener from mDeps. But keep processing uniformly anyway in case the
329    // listener was added back with different dependencies. Then mDeps.sort()
330    // would put it in a different position, and the old newNode placement
331    // value would be wrong, so we'd have to reassign it anyway. Trust that
332    // re-adding a listener with the same dependencies is the trivial case for
333    // mDeps.sort(): it can just replay its cache.
334    DependencyMap::sorted_range sorted_range;
335    try
336    {
337        // Can we pick an order that works including this new entry?
338        sorted_range = mDeps.sort();
339    }
340    catch (const DependencyMap::Cycle& e)
341    {
342        // No: the new node's after/before dependencies have made mDeps
343        // unsortable. If we leave the new node in mDeps, it will continue
344        // to screw up all future attempts to sort()! Pull it out.
345        mDeps.remove(name);
346        throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
347                    " '" + getName() + "' would cause cycle: " + e.what());
348    }
349    // Walk the list to verify that we haven't changed the order.
350    float previous = 0.0, myprev = 0.0;
351    DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
352    for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
353         dmi != sorted_range.end(); ++dmi)
354    {
355        // Since we've added the new entry with an invalid placement,
356        // recognize it and skip it.
357        if (dmi->first == name)
358        {
359            // Remember the iterator belonging to our new node, and which
360            // placement value was 'previous' at that point.
361            mydmi = dmi;
362            myprev = previous;
363            continue;
364        }
365        // If the new node has rearranged the existing nodes, we'll find
366        // that their placement values are no longer in increasing order.
367        if (dmi->second < previous)
368        {
369            // This is another scenario in which we'd better back out the
370            // newly-added node from mDeps -- but don't do it yet, we want to
371            // traverse the existing mDeps to report on it!
372            // Describe the change to the order of our listeners. Copy
373            // everything but the newest listener to a vector we can sort to
374            // obtain the old order.
375            typedef std::vector< std::pair<float, std::string> > SortNameList;
376            SortNameList sortnames;
377            for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
378                 cdmi != cdmend; ++cdmi)
379            {
380                if (cdmi->first != name)
381                {
382                    sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
383                }
384            }
385            std::sort(sortnames.begin(), sortnames.end());
386            std::ostringstream out;
387            out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
388                << "' would move previous listener '" << dmi->first << "'\nwas: ";
389            SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
390            if (sni != snend)
391            {
392                out << sni->second;
393                while (++sni != snend)
394                {
395                    out << ", " << sni->second;
396                }
397            }
398            out << "\nnow: ";
399            DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
400            if (ddmi != ddmend)
401            {
402                out << ddmi->first;
403                while (++ddmi != ddmend)
404                {
405                    out << ", " << ddmi->first;
406                }
407            }
408            // NOW remove the offending listener node.
409            mDeps.remove(name);
410            // Having constructed a description of the order change, inform caller.
411            throw OrderChange(out.str());
412        }
413        // This node becomes the previous one.
414        previous = dmi->second;
415    }
416    // We just got done with a successful mDeps.add(name, ...) call. We'd
417    // better have found 'name' somewhere in that sorted list!
418    assert(mydmi != sorted_range.end());
419    // Four cases:
420    // 0. name is the only entry: placement 1.0
421    // 1. name is the first of several entries: placement (next placement)/2
422    // 2. name is between two other entries: placement (myprev + (next placement))/2
423    // 3. name is the last entry: placement ceil(myprev) + 1.0
424    // Since we've cleverly arranged for myprev to be 0.0 if name is the
425    // first entry, this folds down to two cases. Case 1 is subsumed by
426    // case 2, and case 0 is subsumed by case 3. So we need only handle
427    // cases 2 and 3, which means we need only detect whether name is the
428    // last entry. Increment mydmi to see if there's anything beyond.
429    if (++mydmi != sorted_range.end())
430    {
431        // The new node isn't last. Place it between the previous node and
432        // the successor.
433        newNode = (myprev + mydmi->second)/2.0;
434    }
435    else
436    {
437        // The new node is last. Bump myprev up to the next integer, add
438        // 1.0 and use that.
439        newNode = std::ceil(myprev) + 1.0;
440    }
441    // Now that newNode has a value that places it appropriately in mSignal,
442    // connect it.
443    LLBoundListener bound = mSignal->connect(newNode, listener);
444    mConnections[name] = bound;
445    return bound;
446}
447
448LLBoundListener LLEventPump::getListener(const std::string& name) const
449{
450    ConnectionMap::const_iterator found = mConnections.find(name);
451    if (found != mConnections.end())
452    {
453        return found->second;
454    }
455    // not found, return dummy LLBoundListener
456    return LLBoundListener();
457}
458
459void LLEventPump::stopListening(const std::string& name)
460{
461    ConnectionMap::iterator found = mConnections.find(name);
462    if (found != mConnections.end())
463    {
464        found->second.disconnect();
465        mConnections.erase(found);
466    }
467    // We intentionally do NOT remove this name from mDeps. It may happen that
468    // the same listener with the same name and dependencies will jump on and
469    // off this LLEventPump repeatedly. Keeping a cache of dependencies will
470    // avoid a new dependency sort in such cases.
471}
472
473/*****************************************************************************
474*   LLEventStream
475*****************************************************************************/
476bool LLEventStream::post(const LLSD& event)
477{
478    if (! mEnabled || !mSignal)
479    {
480        return false;
481    }
482    // NOTE NOTE NOTE: Any new access to member data beyond this point should
483    // cause us to move our LLStandardSignal object to a pimpl class along
484    // with said member data. Then the local shared_ptr will preserve both.
485
486    // DEV-43463: capture a local copy of mSignal. We've turned up a
487    // cross-coroutine scenario (described in the Jira) in which this post()
488    // call could end up destroying 'this', the LLEventPump subclass instance
489    // containing mSignal, during the call through *mSignal. So -- capture a
490    // *stack* instance of the shared_ptr, ensuring that our heap
491    // LLStandardSignal object will live at least until post() returns, even
492    // if 'this' gets destroyed during the call.
493    boost::shared_ptr<LLStandardSignal> signal(mSignal);
494    // Let caller know if any one listener handled the event. This is mostly
495    // useful when using LLEventStream as a listener for an upstream
496    // LLEventPump.
497    return (*signal)(event);
498}
499
500/*****************************************************************************
501*   LLEventQueue
502*****************************************************************************/
503bool LLEventQueue::post(const LLSD& event)
504{
505    if (mEnabled)
506    {
507        // Defer sending this event by queueing it until flush()
508        mEventQueue.push_back(event);
509    }
510    // Unconditionally return false. We won't know until flush() whether a
511    // listener claims to have handled the event -- meanwhile, don't block
512    // other listeners.
513    return false;
514}
515
516void LLEventQueue::flush()
517{
518	if(!mSignal) return;
519		
520    // Consider the case when a given listener on this LLEventQueue posts yet
521    // another event on the same queue. If we loop over mEventQueue directly,
522    // we'll end up processing all those events during the same flush() call
523    // -- rather like an EventStream. Instead, copy mEventQueue and clear it,
524    // so that any new events posted to this LLEventQueue during flush() will
525    // be processed in the *next* flush() call.
526    EventQueue queue(mEventQueue);
527    mEventQueue.clear();
528    // NOTE NOTE NOTE: Any new access to member data beyond this point should
529    // cause us to move our LLStandardSignal object to a pimpl class along
530    // with said member data. Then the local shared_ptr will preserve both.
531
532    // DEV-43463: capture a local copy of mSignal. See LLEventStream::post()
533    // for detailed comments.
534    boost::shared_ptr<LLStandardSignal> signal(mSignal);
535    for ( ; ! queue.empty(); queue.pop_front())
536    {
537        (*signal)(queue.front());
538    }
539}
540
541/*****************************************************************************
542*   LLListenerOrPumpName
543*****************************************************************************/
544LLListenerOrPumpName::LLListenerOrPumpName(const std::string& pumpname):
545    // Look up the specified pumpname, and bind its post() method as our listener
546    mListener(boost::bind(&LLEventPump::post,
547                          boost::ref(LLEventPumps::instance().obtain(pumpname)),
548                          _1))
549{
550}
551
552LLListenerOrPumpName::LLListenerOrPumpName(const char* pumpname):
553    // Look up the specified pumpname, and bind its post() method as our listener
554    mListener(boost::bind(&LLEventPump::post,
555                          boost::ref(LLEventPumps::instance().obtain(pumpname)),
556                          _1))
557{
558}
559
560bool LLListenerOrPumpName::operator()(const LLSD& event) const
561{
562    if (! mListener)
563    {
564        throw Empty("attempting to call uninitialized");
565    }
566    return (*mListener)(event);
567}
568
569void LLReqID::stamp(LLSD& response) const
570{
571    if (! (response.isUndefined() || response.isMap()))
572    {
573        // If 'response' was previously completely empty, it's okay to
574        // turn it into a map. If it was already a map, then it should be
575        // okay to add a key. But if it was anything else (e.g. a scalar),
576        // assigning a ["reqid"] key will DISCARD the previous value,
577        // replacing it with a map. That would be Bad.
578        LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: "
579                            << response << LL_ENDL;
580        return;
581    }
582    LLSD oldReqid(response["reqid"]);
583    if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid)))
584    {
585        LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing [\"reqid\"] value "
586                            << oldReqid << " in response: " << response << LL_ENDL;
587        return;
588    }
589    response["reqid"] = mReqid;
590}
591
592bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
593{
594    // If the original request has no value for replyKey, it's pointless to
595    // construct or send a reply event: on which LLEventPump should we send
596    // it? Allow that to be optional: if the caller wants to require replyKey,
597    // it can so specify when registering the operation method.
598    if (! request.has(replyKey))
599    {
600        return false;
601    }
602
603    // Here the request definitely contains replyKey; reasonable to proceed.
604
605    // Copy 'reply' to modify it.
606    LLSD newreply(reply);
607    // Get the ["reqid"] element from request
608    LLReqID reqID(request);
609    // and copy it to 'newreply'.
610    reqID.stamp(newreply);
611    // Send reply on LLEventPump named in request[replyKey]. Don't forget to
612    // send the modified 'newreply' instead of the original 'reply'.
613    return LLEventPumps::instance().obtain(request[replyKey]).post(newreply);
614}