diff options
| author | palange <palange@lindenlab.com> | 2009-10-12 19:03:52 -0400 | 
|---|---|---|
| committer | palange <palange@lindenlab.com> | 2009-10-12 19:03:52 -0400 | 
| commit | dbe7135cc4694e906a7d95a935df70f20514c962 (patch) | |
| tree | 227ed3c9bc717171aeb009067e0f07335bcee8c3 /indra/llcommon/llevents.h | |
| parent | d4b2897700c66354413af42ab055bd1aaa47f91c (diff) | |
| parent | e3a4e3dc10a96b0822674cea262f41774e55a660 (diff) | |
merge of login-api
Diffstat (limited to 'indra/llcommon/llevents.h')
| -rw-r--r-- | indra/llcommon/llevents.h | 1765 | 
1 files changed, 934 insertions, 831 deletions
| diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 240adcdd41..b999bfafa7 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -1,831 +1,934 @@ -/** - * @file   llevents.h - * @author Kent Quirk, Nat Goodspeed - * @date   2008-09-11 - * @brief  This is an implementation of the event system described at - *         https://wiki.lindenlab.com/wiki/Viewer:Messaging/Event_System, - *         originally introduced in llnotifications.h. It has nothing - *         whatsoever to do with the older system in llevent.h. - *  - * $LicenseInfo:firstyear=2008&license=viewergpl$ - * Copyright (c) 2008, Linden Research, Inc. - * $/LicenseInfo$ - */ - -#if ! defined(LL_LLEVENTS_H) -#define LL_LLEVENTS_H - -#include <string> -#include <map> -#include <set> -#include <vector> -#include <list> -#include <deque> -#include <stdexcept> -#if LL_WINDOWS -	#pragma warning (push) -	#pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch -	#pragma warning (disable : 4264)  -#endif -#include <boost/signals2.hpp> -#if LL_WINDOWS -	#pragma warning (pop) -#endif - -#include <boost/bind.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> -#include <boost/utility.hpp>        // noncopyable -#include <boost/optional/optional.hpp> -#include <boost/ptr_container/ptr_vector.hpp> -#include <boost/visit_each.hpp> -#include <boost/ref.hpp>            // reference_wrapper -#include <boost/type_traits/is_pointer.hpp> -#include <boost/utility/addressof.hpp> -#include <boost/preprocessor/repetition/enum_params.hpp> -#include <boost/preprocessor/iteration/local.hpp> -#include <boost/function.hpp> -#include <boost/static_assert.hpp> -#include "llsd.h" -#include "llsingleton.h" -#include "lldependencies.h" - -// override this to allow binding free functions with more parameters -#ifndef LLEVENTS_LISTENER_ARITY -#define LLEVENTS_LISTENER_ARITY 10 -#endif - -// hack for testing -#ifndef testable -#define testable private -#endif - -/***************************************************************************** -*   Signal and handler declarations -*   Using a single handler signature means that we can have a common handler -*   type, rather than needing a distinct one for each different handler. -*****************************************************************************/ - -/** - * A boost::signals Combiner that stops the first time a handler returns true - * We need this because we want to have our handlers return bool, so that - * we have the option to cause a handler to stop further processing. The - * default handler fails when the signal returns a value but has no slots. - */ -struct LLStopWhenHandled -{ -    typedef bool result_type; - -    template<typename InputIterator> -    result_type operator()(InputIterator first, InputIterator last) const -    { -        for (InputIterator si = first; si != last; ++si) -		{ -            if (*si) -			{ -                return true; -			} -		} -        return false; -    } -}; - -/** - * We want to have a standard signature for all signals; this way, - * we can easily document a protocol for communicating across - * dlls and into scripting languages someday. - * - * We want to return a bool to indicate whether the signal has been - * handled and should NOT be passed on to other listeners. - * Return true to stop further handling of the signal, and false - * to continue. - * - * We take an LLSD because this way the contents of the signal - * are independent of the API used to communicate it. - * It is const ref because then there's low cost to pass it; - * if you only need to inspect it, it's very cheap. - * - * @internal - * The @c float template parameter indicates that we will internally use @c - * float to indicate relative listener order on a given LLStandardSignal. - * Don't worry, the @c float values are strictly internal! They are not part - * of the interface, for the excellent reason that requiring the caller to - * specify a numeric key to establish order means that the caller must know - * the universe of possible values. We use LLDependencies for that instead. - */ -typedef boost::signals2::signal<bool(const LLSD&), LLStopWhenHandled, float>  LLStandardSignal; -/// Methods that forward listeners (e.g. constructed with -/// <tt>boost::bind()</tt>) should accept (const LLEventListener&) -typedef LLStandardSignal::slot_type LLEventListener; -/// Result of registering a listener, supports <tt>connected()</tt>, -/// <tt>disconnect()</tt> and <tt>blocked()</tt> -typedef boost::signals2::connection LLBoundListener; - -/** - * A common idiom for event-based code is to accept either a callable -- - * directly called on completion -- or the string name of an LLEventPump on - * which to post the completion event. Specifying a parameter as <tt>const - * LLListenerOrPumpName&</tt> allows either. - * - * Calling a validly-constructed LLListenerOrPumpName, passing the LLSD - * 'event' object, either calls the callable or posts the event to the named - * LLEventPump. - * - * A default-constructed LLListenerOrPumpName is 'empty'. (This is useful as - * the default value of an optional method parameter.) Calling it throws - * LLListenerOrPumpName::Empty. Test for this condition beforehand using - * either <tt>if (param)</tt> or <tt>if (! param)</tt>. - */ -class LLListenerOrPumpName -{ -public: -    /// passing string name of LLEventPump -    LLListenerOrPumpName(const std::string& pumpname); -    /// passing string literal (overload so compiler isn't forced to infer -    /// double conversion) -    LLListenerOrPumpName(const char* pumpname); -    /// passing listener -- the "anything else" catch-all case. The type of an -    /// object constructed by boost::bind() isn't intended to be written out. -    /// Normally we'd just accept 'const LLEventListener&', but that would -    /// require double implicit conversion: boost::bind() object to -    /// LLEventListener, LLEventListener to LLListenerOrPumpName. So use a -    /// template to forward anything. -    template<typename T> -    LLListenerOrPumpName(const T& listener): mListener(listener) {} - -    /// for omitted method parameter: uninitialized mListener -    LLListenerOrPumpName() {} - -    /// test for validity -    operator bool() const { return bool(mListener); } -    bool operator! () const { return ! mListener; } - -    /// explicit accessor -    const LLEventListener& getListener() const { return *mListener; } - -    /// implicit conversion to LLEventListener -    operator LLEventListener() const { return *mListener; } - -    /// allow calling directly -    bool operator()(const LLSD& event) const; - -    /// exception if you try to call when empty -    struct Empty: public std::runtime_error -    { -        Empty(const std::string& what): -            std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {} -    }; - -private: -    boost::optional<LLEventListener> mListener; -}; - -/***************************************************************************** -*   LLEventPumps -*****************************************************************************/ -class LLEventPump; - -/** - * LLEventPumps is a Singleton manager through which one typically accesses - * this subsystem. - */ -class LLEventPumps: public LLSingleton<LLEventPumps> -{ -    friend class LLSingleton<LLEventPumps>; -public: -    /** -     * Find or create an LLEventPump instance with a specific name. We return -     * a reference so there's no question about ownership. obtain() @em finds -     * an instance without conferring @em ownership. -     */ -    LLEventPump& obtain(const std::string& name); -    /** -     * Flush all known LLEventPump instances -     */ -    void flush(); - -private: -    friend class LLEventPump; -    /** -     * Register a new LLEventPump instance (internal) -     */ -    std::string registerNew(const LLEventPump&, const std::string& name, bool tweak); -    /** -     * Unregister a doomed LLEventPump instance (internal) -     */ -    void unregister(const LLEventPump&); - -private: -    LLEventPumps(); -    ~LLEventPumps(); - -testable: -    // Map of all known LLEventPump instances, whether or not we instantiated -    // them. We store a plain old LLEventPump* because this map doesn't claim -    // ownership of the instances. Though the common usage pattern is to -    // request an instance using obtain(), it's fair to instantiate an -    // LLEventPump subclass statically, as a class member, on the stack or on -    // the heap. In such cases, the instantiating party is responsible for its -    // lifespan. -    typedef std::map<std::string, LLEventPump*> PumpMap; -    PumpMap mPumpMap; -    // Set of all LLEventPumps we instantiated. Membership in this set means -    // we claim ownership, and will delete them when this LLEventPumps is -    // destroyed. -    typedef std::set<LLEventPump*> PumpSet; -    PumpSet mOurPumps; -    // LLEventPump names that should be instantiated as LLEventQueue rather -    // than as LLEventStream -    typedef std::set<std::string> PumpNames; -    PumpNames mQueueNames; -}; - -/***************************************************************************** -*   details -*****************************************************************************/ -namespace LLEventDetail -{ -    /// Any callable capable of connecting an LLEventListener to an -    /// LLStandardSignal to produce an LLBoundListener can be mapped to this -    /// signature. -    typedef boost::function<LLBoundListener(const LLEventListener&)> ConnectFunc; - -    /** -     * Utility template function to use Visitor appropriately -     * -     * @param listener Callable to connect, typically a boost::bind() -     * expression. This will be visited by Visitor using boost::visit_each(). -     * @param connect_func Callable that will connect() @a listener to an -     * LLStandardSignal, returning LLBoundListener. -     */ -    template <typename LISTENER> -    LLBoundListener visit_and_connect(const LISTENER& listener, -                                      const ConnectFunc& connect_func); -} // namespace LLEventDetail - -/***************************************************************************** -*   LLEventPump -*****************************************************************************/ -/** - * LLEventPump is the base class interface through which we access the - * concrete subclasses LLEventStream and LLEventQueue. - */ -class LLEventPump: boost::noncopyable -{ -public: -    /** -     * Exception thrown by LLEventPump(). You are trying to instantiate an -     * LLEventPump (subclass) using the same name as some other instance, and -     * you didn't pass <tt>tweak=true</tt> to permit it to generate a unique -     * variant. -     */ -    struct DupPumpName: public std::runtime_error -    { -        DupPumpName(const std::string& what): -            std::runtime_error(std::string("DupPumpName: ") + what) {} -    }; - -    /** -     * Instantiate an LLEventPump (subclass) with the string name by which it -     * can be found using LLEventPumps::obtain(). -     * -     * If you pass (or default) @a tweak to @c false, then a duplicate name -     * will throw DupPumpName. This won't happen if LLEventPumps::obtain() -     * instantiates the LLEventPump, because obtain() uses find-or-create -     * logic. It can only happen if you instantiate an LLEventPump in your own -     * code -- and a collision with the name of some other LLEventPump is -     * likely to cause much more subtle problems! -     * -     * When you hand-instantiate an LLEventPump, consider passing @a tweak as -     * @c true. This directs LLEventPump() to append a suffix to the passed @a -     * name to make it unique. You can retrieve the adjusted name by calling -     * getName() on your new instance. -     */ -    LLEventPump(const std::string& name, bool tweak=false); -    virtual ~LLEventPump(); - -    /// group exceptions thrown by listen(). We use exceptions because these -    /// particular errors are likely to be coding errors, found and fixed by -    /// the developer even before preliminary checkin. -    struct ListenError: public std::runtime_error -    { -        ListenError(const std::string& what): std::runtime_error(what) {} -    }; -    /** -     * exception thrown by listen(). You are attempting to register a -     * listener on this LLEventPump using the same listener name as an -     * already-registered listener. -     */ -    struct DupListenerName: public ListenError -    { -        DupListenerName(const std::string& what): -            ListenError(std::string("DupListenerName: ") + what) -        {} -    }; -    /** -     * exception thrown by listen(). The order dependencies specified for your -     * listener are incompatible with existing listeners. -     * -     * Consider listener "a" which specifies before "b" and "b" which -     * specifies before "c". You are now attempting to register "c" before -     * "a". There is no order that can satisfy all constraints. -     */ -    struct Cycle: public ListenError -    { -        Cycle(const std::string& what): ListenError(std::string("Cycle: ") + what) {} -    }; -    /** -     * exception thrown by listen(). This one means that your new listener -     * would force a change to the order of previously-registered listeners, -     * and we don't have a good way to implement that. -     * -     * Consider listeners "some", "other" and "third". "some" and "other" are -     * registered earlier without specifying relative order, so "other" -     * happens to be first. Now you attempt to register "third" after "some" -     * and before "other". Whoops, that would require swapping "some" and -     * "other", which we can't do. Instead we throw this exception. -     * -     * It may not be possible to change the registration order so we already -     * know "third"s order requirement by the time we register the second of -     * "some" and "other". A solution would be to specify that "some" must -     * come before "other", or equivalently that "other" must come after -     * "some". -     */ -    struct OrderChange: public ListenError -    { -        OrderChange(const std::string& what): ListenError(std::string("OrderChange: ") + what) {} -    }; - -    /// used by listen() -    typedef std::vector<std::string> NameList; -    /// convenience placeholder for when you explicitly want to pass an empty -    /// NameList -    const static NameList empty; - -    /// Get this LLEventPump's name -    std::string getName() const { return mName; } - -    /** -     * Register a new listener with a unique name. Specify an optional list -     * of other listener names after which this one must be called, likewise -     * an optional list of other listener names before which this one must be -     * called. The other listeners mentioned need not yet be registered -     * themselves. listen() can throw any ListenError; see ListenError -     * subclasses. -     * -     * If (as is typical) you pass a <tt>boost::bind()</tt> expression, -     * listen() will inspect the components of that expression. If a bound -     * object matches any of several cases, the connection will automatically -     * be disconnected when that object is destroyed. -     * -     * * You bind a <tt>boost::weak_ptr</tt>. -     * * Binding a <tt>boost::shared_ptr</tt> that way would ensure that the -     *   referenced object would @em never be destroyed, since the @c -     *   shared_ptr stored in the LLEventPump would remain an outstanding -     *   reference. Use the weaken() function to convert your @c shared_ptr to -     *   @c weak_ptr. Because this is easy to forget, binding a @c shared_ptr -     *   will produce a compile error (@c BOOST_STATIC_ASSERT failure). -     * * You bind a simple pointer or reference to an object derived from -     *   <tt>boost::enable_shared_from_this</tt>. (UNDER CONSTRUCTION) -     * * You bind a simple pointer or reference to an object derived from -     *   LLEventTrackable. Unlike the cases described above, though, this is -     *   vulnerable to a couple of cross-thread race conditions, as described -     *   in the LLEventTrackable documentation. -     */ -    template <typename LISTENER> -    LLBoundListener listen(const std::string& name, const LISTENER& listener, -                           const NameList& after=NameList(), -                           const NameList& before=NameList()) -    { -        // Examine listener, using our listen_impl() method to make the -        // actual connection. -        // This is why listen() is a template. Conversion from boost::bind() -        // to LLEventListener performs type erasure, so it's important to look -        // at the boost::bind object itself before that happens. -        return LLEventDetail::visit_and_connect(listener, -                                                boost::bind(&LLEventPump::listen_impl, -                                                            this, -                                                            name, -                                                            _1, -                                                            after, -                                                            before)); -    } - -    /// Get the LLBoundListener associated with the passed name (dummy -    /// LLBoundListener if not found) -    virtual LLBoundListener getListener(const std::string& name) const; -    /** -     * Instantiate one of these to block an existing connection: -     * @code -     * { // in some local scope -     *     LLEventPump::Blocker block(someLLBoundListener); -     *     // code that needs the connection blocked -     * } // unblock the connection again -     * @endcode -     */ -    typedef boost::signals2::shared_connection_block Blocker; -    /// Unregister a listener by name. Prefer this to -    /// <tt>getListener(name).disconnect()</tt> because stopListening() also -    /// forgets this name. -    virtual void stopListening(const std::string& name); -    /// Post an event to all listeners. The @c bool return is only meaningful -    /// if the underlying leaf class is LLEventStream -- beware of relying on -    /// it too much! Truthfully, we return @c bool mostly to permit chaining -    /// one LLEventPump as a listener on another. -    virtual bool post(const LLSD&) = 0; -    /// Enable/disable: while disabled, silently ignore all post() calls -    virtual void enable(bool enabled=true) { mEnabled = enabled; } -    /// query -    virtual bool enabled() const { return mEnabled; } - -private: -    friend class LLEventPumps; -    /// flush queued events -    virtual void flush() {} - -private: -    virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, -                                        const NameList& after, -                                        const NameList& before); -    std::string mName; - -protected: -    /// implement the dispatching -    LLStandardSignal mSignal; -    /// valve open? -    bool mEnabled; -    /// Map of named listeners. This tracks the listeners that actually exist -    /// at this moment. When we stopListening(), we discard the entry from -    /// this map. -    typedef std::map<std::string, boost::signals2::connection> ConnectionMap; -    ConnectionMap mConnections; -    typedef LLDependencies<std::string, float> DependencyMap; -    /// Dependencies between listeners. For each listener, track the float -    /// used to establish its place in mSignal's order. This caches all the -    /// listeners that have ever registered; stopListening() does not discard -    /// the entry from this map. This is to avoid a new dependency sort if the -    /// same listener with the same dependencies keeps hopping on and off this -    /// LLEventPump. -    DependencyMap mDeps; -}; - -/***************************************************************************** -*   LLEventStream -*****************************************************************************/ -/** - * LLEventStream is a thin wrapper around LLStandardSignal. Posting an - * event immediately calls all registered listeners. - */ -class LLEventStream: public LLEventPump -{ -public: -    LLEventStream(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {} -    virtual ~LLEventStream() {} - -    /// Post an event to all listeners -    virtual bool post(const LLSD& event); -}; - -/***************************************************************************** -*   LLEventQueue -*****************************************************************************/ -/** - * LLEventQueue isa LLEventPump whose post() method defers calling registered - * listeners until flush() is called. - */ -class LLEventQueue: public LLEventPump -{ -public: -    LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {} -    virtual ~LLEventQueue() {} - -    /// Post an event to all listeners -    virtual bool post(const LLSD& event); - -private: -    /// flush queued events -    virtual void flush(); - -private: -    typedef std::deque<LLSD> EventQueue; -    EventQueue mEventQueue; -}; - -/***************************************************************************** -*   LLEventTrackable and underpinnings -*****************************************************************************/ -/** - * LLEventTrackable wraps boost::signals2::trackable, which resembles - * boost::trackable. Derive your listener class from LLEventTrackable instead, - * and use something like - * <tt>LLEventPump::listen(boost::bind(&YourTrackableSubclass::method, - * instance, _1))</tt>. This will implicitly disconnect when the object - * referenced by @c instance is destroyed. - * - * @note - * LLEventTrackable doesn't address a couple of cases: - * * Object destroyed during call - *   - You enter a slot call in thread A. - *   - Thread B destroys the object, which of course disconnects it from any - *     future slot calls. - *   - Thread A's call uses 'this', which now refers to a defunct object. - *     Undefined behavior results. - * * Call during destruction - *   - @c MySubclass is derived from LLEventTrackable. - *   - @c MySubclass registers one of its own methods using - *     <tt>LLEventPump::listen()</tt>. - *   - The @c MySubclass object begins destruction. <tt>~MySubclass()</tt> - *     runs, destroying state specific to the subclass. (For instance, a - *     <tt>Foo*</tt> data member is <tt>delete</tt>d but not zeroed.) - *   - The listening method will not be disconnected until - *     <tt>~LLEventTrackable()</tt> runs. - *   - Before we get there, another thread posts data to the @c LLEventPump - *     instance, calling the @c MySubclass method. - *   - The method in question relies on valid @c MySubclass state. (For - *     instance, it attempts to dereference the <tt>Foo*</tt> pointer that was - *     <tt>delete</tt>d but not zeroed.) - *   - Undefined behavior results. - * If you suspect you may encounter any such scenario, you're better off - * managing the lifespan of your object with <tt>boost::shared_ptr</tt>. - * Passing <tt>LLEventPump::listen()</tt> a <tt>boost::bind()</tt> expression - * involving a <tt>boost::weak_ptr<Foo></tt> is recognized specially, engaging - * thread-safe Boost.Signals2 machinery. - */ -typedef boost::signals2::trackable LLEventTrackable; - -/** - * We originally provided a suite of overloaded - * LLEventTrackable::listenTo(LLEventPump&, ...) methods that would call - * LLEventPump::listen(...) and then pass the returned LLBoundListener to - * LLEventTrackable::track(). This was workable but error-prone: the coder - * must remember to call listenTo() rather than the more straightforward - * listen() method. - * - * Now we publish only the single canonical listen() method, so there's a - * uniform mechanism. Having a single way to do this is good, in that there's - * no question in the coder's mind which of several alternatives to choose. - * - * To support automatic connection management, we use boost::visit_each - * (http://www.boost.org/doc/libs/1_37_0/doc/html/boost/visit_each.html) to - * inspect each argument of a boost::bind expression. (Although the visit_each - * mechanism was first introduced with the original Boost.Signals library, it - * was only later documented.) - * - * Cases: - * * At least one of the function's arguments is a boost::weak_ptr<T>. Pass - *   the corresponding shared_ptr to slot_type::track(). Ideally that would be - *   the object whose method we want to call, but in fact we do the same for - *   any weak_ptr we might find among the bound arguments. If we're passing - *   our bound method a weak_ptr to some object, wouldn't the destruction of - *   that object invalidate the call? So we disconnect automatically when any - *   such object is destroyed. This is the mechanism preferred by boost:: - *   signals2. - * * One of the functions's arguments is a boost::shared_ptr<T>. This produces - *   a compile error: the bound copy of the shared_ptr stored in the - *   boost_bind object stored in the signal object would make the referenced - *   T object immortal. We provide a weaken() function. Pass - *   weaken(your_shared_ptr) instead. (We can inspect, but not modify, the - *   boost::bind object. Otherwise we'd replace the shared_ptr with weak_ptr - *   implicitly and just proceed.) - * * One of the function's arguments is a plain pointer/reference to an object - *   derived from boost::enable_shared_from_this. We assume that this object - *   is managed using boost::shared_ptr, so we implicitly extract a shared_ptr - *   and track that. (UNDER CONSTRUCTION) - * * One of the function's arguments is derived from LLEventTrackable. Pass - *   the LLBoundListener to its LLEventTrackable::track(). This is vulnerable - *   to a couple different race conditions, as described in LLEventTrackable - *   documentation. (NOTE: Now that LLEventTrackable is a typedef for - *   boost::signals2::trackable, the Signals2 library handles this itself, so - *   our visitor needs no special logic for this case.) - * * Any other argument type is irrelevant to automatic connection management. - */ - -namespace LLEventDetail -{ -    template <typename F> -    const F& unwrap(const F& f) { return f; } - -    template <typename F> -    const F& unwrap(const boost::reference_wrapper<F>& f) { return f.get(); } - -    // Most of the following is lifted from the Boost.Signals use of -    // visit_each. -    template<bool Cond> struct truth {}; - -    /** -     * boost::visit_each() Visitor, used on a template argument <tt>const F& -     * f</tt> as follows (see visit_and_connect()): -     * @code -     * LLEventListener listener(f); -     * Visitor visitor(listener); // bind listener so it can track() shared_ptrs -     * using boost::visit_each;   // allow unqualified visit_each() call for ADL -     * visit_each(visitor, unwrap(f)); -     * @endcode -     */ -    class Visitor -    { -    public: -        /** -         * Visitor binds a reference to LLEventListener so we can track() any -         * shared_ptrs we find in the argument list. -         */ -        Visitor(LLEventListener& listener): -            mListener(listener) -        { -        } - -        /** -         * boost::visit_each() calls this method for each component of a -         * boost::bind() expression. -         */ -        template <typename T> -        void operator()(const T& t) const -        { -            decode(t, 0); -        } - -    private: -        // decode() decides between a reference wrapper and anything else -        // boost::ref() variant -        template<typename T> -        void decode(const boost::reference_wrapper<T>& t, int) const -        { -//          add_if_trackable(t.get_pointer()); -        } - -        // decode() anything else -        template<typename T> -        void decode(const T& t, long) const -        { -            typedef truth<(boost::is_pointer<T>::value)> is_a_pointer; -            maybe_get_pointer(t, is_a_pointer()); -        } - -        // maybe_get_pointer() decides between a pointer and a non-pointer -        // plain pointer variant -        template<typename T> -        void maybe_get_pointer(const T& t, truth<true>) const -        { -//          add_if_trackable(t); -        } - -        // shared_ptr variant -        template<typename T> -        void maybe_get_pointer(const boost::shared_ptr<T>& t, truth<false>) const -        { -            // If we have a shared_ptr to this object, it doesn't matter -            // whether the object is derived from LLEventTrackable, so no -            // further analysis of T is needed. -//          mListener.track(t); - -            // Make this case illegal. Passing a bound shared_ptr to -            // slot_type::track() is useless, since the bound shared_ptr will -            // keep the object alive anyway! Force the coder to cast to weak_ptr. - -            // Trivial as it is, make the BOOST_STATIC_ASSERT() condition -            // dependent on template param so the macro is only evaluated if -            // this method is in fact instantiated, as described here: -            // http://www.boost.org/doc/libs/1_34_1/doc/html/boost_staticassert.html - -            // ATTENTION: Don't bind a shared_ptr<anything> using -            // LLEventPump::listen(boost::bind()). Doing so captures a copy of -            // the shared_ptr, making the referenced object effectively -            // immortal. Use the weaken() function, e.g.: -            // somepump.listen(boost::bind(...weaken(my_shared_ptr)...)); -            // This lets us automatically disconnect when the referenced -            // object is destroyed. -            BOOST_STATIC_ASSERT(sizeof(T) == 0); -        } - -        // weak_ptr variant -        template<typename T> -        void maybe_get_pointer(const boost::weak_ptr<T>& t, truth<false>) const -        { -            // If we have a weak_ptr to this object, it doesn't matter -            // whether the object is derived from LLEventTrackable, so no -            // further analysis of T is needed. -            mListener.track(t); -//          std::cout << "Found weak_ptr<" << typeid(T).name() << ">!\n"; -        } - -#if 0 -        // reference to anything derived from boost::enable_shared_from_this -        template <typename T> -        inline void maybe_get_pointer(const boost::enable_shared_from_this<T>& ct, -                                      truth<false>) const -        { -            // Use the slot_type::track(shared_ptr) mechanism. Cast away -            // const-ness because (in our code base anyway) it's unusual -            // to find shared_ptr<const T>. -            boost::enable_shared_from_this<T>& -                t(const_cast<boost::enable_shared_from_this<T>&>(ct)); -            std::cout << "Capturing shared_from_this()" << std::endl; -            boost::shared_ptr<T> sp(t.shared_from_this()); -/*==========================================================================*| -            std::cout << "Capturing weak_ptr" << std::endl; -            boost::weak_ptr<T> wp(sp); -|*==========================================================================*/ -            std::cout << "Tracking shared__ptr" << std::endl; -            mListener.track(sp); -        } -#endif - -        // non-pointer variant -        template<typename T> -        void maybe_get_pointer(const T& t, truth<false>) const -        { -            // Take the address of this object, because the object itself may be -            // trackable -//          add_if_trackable(boost::addressof(t)); -        } - -/*==========================================================================*| -        // add_if_trackable() adds LLEventTrackable objects to mTrackables -        inline void add_if_trackable(const LLEventTrackable* t) const -        { -            if (t) -            { -            } -        } - -        // pointer to anything not an LLEventTrackable subclass -        inline void add_if_trackable(const void*) const -        { -        } - -        // pointer to free function -        // The following construct uses the preprocessor to generate -        // add_if_trackable() overloads accepting pointer-to-function taking -        // 0, 1, ..., LLEVENTS_LISTENER_ARITY parameters of arbitrary type. -#define BOOST_PP_LOCAL_MACRO(n)                                     \ -        template <typename R                                        \ -                  BOOST_PP_COMMA_IF(n)                              \ -                  BOOST_PP_ENUM_PARAMS(n, typename T)>              \ -        inline void                                                 \ -        add_if_trackable(R (*)(BOOST_PP_ENUM_PARAMS(n, T))) const   \ -        {                                                           \ -        } -#define BOOST_PP_LOCAL_LIMITS (0, LLEVENTS_LISTENER_ARITY) -#include BOOST_PP_LOCAL_ITERATE() -#undef  BOOST_PP_LOCAL_MACRO -#undef  BOOST_PP_LOCAL_LIMITS -|*==========================================================================*/ - -        /// Bind a reference to the LLEventListener to call its track() method. -        LLEventListener& mListener; -    }; - -    /** -     * Utility template function to use Visitor appropriately -     * -     * @param raw_listener Callable to connect, typically a boost::bind() -     * expression. This will be visited by Visitor using boost::visit_each(). -     * @param connect_funct Callable that will connect() @a raw_listener to an -     * LLStandardSignal, returning LLBoundListener. -     */ -    template <typename LISTENER> -    LLBoundListener visit_and_connect(const LISTENER& raw_listener, -                                      const ConnectFunc& connect_func) -    { -        // Capture the listener -        LLEventListener listener(raw_listener); -        // Define our Visitor, binding the listener so we can call -        // listener.track() if we discover any shared_ptr<Foo>. -        LLEventDetail::Visitor visitor(listener); -        // Allow unqualified visit_each() call for ADL -        using boost::visit_each; -        // Visit each component of a boost::bind() expression. Pass -        // 'raw_listener', our template argument, rather than 'listener' from -        // which type details have been erased. unwrap() comes from -        // Boost.Signals, in case we were passed a boost::ref(). -        visit_each(visitor, LLEventDetail::unwrap(raw_listener)); -        // Make the connection using passed function. At present, wrapping -        // this functionality into this function is a bit silly: we don't -        // really need a visit_and_connect() function any more, just a visit() -        // function. The definition of this function dates from when, after -        // visit_each(), after establishing the connection, we had to -        // postprocess the new connection with the visitor object. That's no -        // longer necessary. -        return connect_func(listener); -    } -} // namespace LLEventDetail - -// Somewhat to my surprise, passing boost::bind(...boost::weak_ptr<T>...) to -// listen() fails in Boost code trying to instantiate LLEventListener (i.e. -// LLStandardSignal::slot_type) because the boost::get_pointer() utility function isn't -// specialized for boost::weak_ptr. This remedies that omission. -namespace boost -{ -    template <typename T> -    T* get_pointer(const weak_ptr<T>& ptr) { return shared_ptr<T>(ptr).get(); } -} - -/// Since we forbid use of listen(boost::bind(...shared_ptr<T>...)), provide an -/// easy way to cast to the corresponding weak_ptr. -template <typename T> -boost::weak_ptr<T> weaken(const boost::shared_ptr<T>& ptr) -{ -    return boost::weak_ptr<T>(ptr); -} - -#endif /* ! defined(LL_LLEVENTS_H) */ +/**
 + * @file   llevents.h
 + * @author Kent Quirk, Nat Goodspeed
 + * @date   2008-09-11
 + * @brief  This is an implementation of the event system described at
 + *         https://wiki.lindenlab.com/wiki/Viewer:Messaging/Event_System,
 + *         originally introduced in llnotifications.h. It has nothing
 + *         whatsoever to do with the older system in llevent.h.
 + * 
 + * $LicenseInfo:firstyear=2008&license=viewergpl$
 + * Copyright (c) 2008, Linden Research, Inc.
 + * $/LicenseInfo$
 + */
 +
 +#if ! defined(LL_LLEVENTS_H)
 +#define LL_LLEVENTS_H
 +
 +#include <string>
 +#include <map>
 +#include <set>
 +#include <vector>
 +#include <deque>
 +#include <stdexcept>
 +#if LL_WINDOWS
 +	#pragma warning (push)
 +	#pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
 +	#pragma warning (disable : 4264) 
 +#endif
 +#include <boost/signals2.hpp>
 +#if LL_WINDOWS
 +	#pragma warning (pop)
 +#endif
 +
 +#include <boost/bind.hpp>
 +#include <boost/shared_ptr.hpp>
 +#include <boost/enable_shared_from_this.hpp>
 +#include <boost/utility.hpp>        // noncopyable
 +#include <boost/optional/optional.hpp>
 +#include <boost/visit_each.hpp>
 +#include <boost/ref.hpp>            // reference_wrapper
 +#include <boost/type_traits/is_pointer.hpp>
 +#include <boost/function.hpp>
 +#include <boost/static_assert.hpp>
 +#include "llsd.h"
 +#include "llsingleton.h"
 +#include "lldependencies.h"
 +
 +// override this to allow binding free functions with more parameters
 +#ifndef LLEVENTS_LISTENER_ARITY
 +#define LLEVENTS_LISTENER_ARITY 10
 +#endif
 +
 +// hack for testing
 +#ifndef testable
 +#define testable private
 +#endif
 +
 +/*****************************************************************************
 +*   Signal and handler declarations
 +*   Using a single handler signature means that we can have a common handler
 +*   type, rather than needing a distinct one for each different handler.
 +*****************************************************************************/
 +
 +/**
 + * A boost::signals Combiner that stops the first time a handler returns true
 + * We need this because we want to have our handlers return bool, so that
 + * we have the option to cause a handler to stop further processing. The
 + * default handler fails when the signal returns a value but has no slots.
 + */
 +struct LLStopWhenHandled
 +{
 +    typedef bool result_type;
 +
 +    template<typename InputIterator>
 +    result_type operator()(InputIterator first, InputIterator last) const
 +    {
 +        for (InputIterator si = first; si != last; ++si)
 +		{
 +            if (*si)
 +			{
 +                return true;
 +			}
 +		}
 +        return false;
 +    }
 +};
 +
 +/**
 + * We want to have a standard signature for all signals; this way,
 + * we can easily document a protocol for communicating across
 + * dlls and into scripting languages someday.
 + *
 + * We want to return a bool to indicate whether the signal has been
 + * handled and should NOT be passed on to other listeners.
 + * Return true to stop further handling of the signal, and false
 + * to continue.
 + *
 + * We take an LLSD because this way the contents of the signal
 + * are independent of the API used to communicate it.
 + * It is const ref because then there's low cost to pass it;
 + * if you only need to inspect it, it's very cheap.
 + *
 + * @internal
 + * The @c float template parameter indicates that we will internally use @c
 + * float to indicate relative listener order on a given LLStandardSignal.
 + * Don't worry, the @c float values are strictly internal! They are not part
 + * of the interface, for the excellent reason that requiring the caller to
 + * specify a numeric key to establish order means that the caller must know
 + * the universe of possible values. We use LLDependencies for that instead.
 + */
 +typedef boost::signals2::signal<bool(const LLSD&), LLStopWhenHandled, float>  LLStandardSignal;
 +/// Methods that forward listeners (e.g. constructed with
 +/// <tt>boost::bind()</tt>) should accept (const LLEventListener&)
 +typedef LLStandardSignal::slot_type LLEventListener;
 +/// Result of registering a listener, supports <tt>connected()</tt>,
 +/// <tt>disconnect()</tt> and <tt>blocked()</tt>
 +typedef boost::signals2::connection LLBoundListener;
 +/// Storing an LLBoundListener in LLTempBoundListener will disconnect the
 +/// referenced listener when the LLTempBoundListener instance is destroyed.
 +typedef boost::signals2::scoped_connection LLTempBoundListener;
 +
 +/**
 + * A common idiom for event-based code is to accept either a callable --
 + * directly called on completion -- or the string name of an LLEventPump on
 + * which to post the completion event. Specifying a parameter as <tt>const
 + * LLListenerOrPumpName&</tt> allows either.
 + *
 + * Calling a validly-constructed LLListenerOrPumpName, passing the LLSD
 + * 'event' object, either calls the callable or posts the event to the named
 + * LLEventPump.
 + *
 + * A default-constructed LLListenerOrPumpName is 'empty'. (This is useful as
 + * the default value of an optional method parameter.) Calling it throws
 + * LLListenerOrPumpName::Empty. Test for this condition beforehand using
 + * either <tt>if (param)</tt> or <tt>if (! param)</tt>.
 + */
 +class LL_COMMON_API LLListenerOrPumpName
 +{
 +public:
 +    /// passing string name of LLEventPump
 +    LLListenerOrPumpName(const std::string& pumpname);
 +    /// passing string literal (overload so compiler isn't forced to infer
 +    /// double conversion)
 +    LLListenerOrPumpName(const char* pumpname);
 +    /// passing listener -- the "anything else" catch-all case. The type of an
 +    /// object constructed by boost::bind() isn't intended to be written out.
 +    /// Normally we'd just accept 'const LLEventListener&', but that would
 +    /// require double implicit conversion: boost::bind() object to
 +    /// LLEventListener, LLEventListener to LLListenerOrPumpName. So use a
 +    /// template to forward anything.
 +    template<typename T>
 +    LLListenerOrPumpName(const T& listener): mListener(listener) {}
 +
 +    /// for omitted method parameter: uninitialized mListener
 +    LLListenerOrPumpName() {}
 +
 +    /// test for validity
 +    operator bool() const { return bool(mListener); }
 +    bool operator! () const { return ! mListener; }
 +
 +    /// explicit accessor
 +    const LLEventListener& getListener() const { return *mListener; }
 +
 +    /// implicit conversion to LLEventListener
 +    operator LLEventListener() const { return *mListener; }
 +
 +    /// allow calling directly
 +    bool operator()(const LLSD& event) const;
 +
 +    /// exception if you try to call when empty
 +    struct Empty: public std::runtime_error
 +    {
 +        Empty(const std::string& what):
 +            std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {}
 +    };
 +
 +private:
 +    boost::optional<LLEventListener> mListener;
 +};
 +
 +/*****************************************************************************
 +*   LLEventPumps
 +*****************************************************************************/
 +class LLEventPump;
 +
 +/**
 + * LLEventPumps is a Singleton manager through which one typically accesses
 + * this subsystem.
 + */
 +class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps>
 +{
 +    friend class LLSingleton<LLEventPumps>;
 +public:
 +    /**
 +     * Find or create an LLEventPump instance with a specific name. We return
 +     * a reference so there's no question about ownership. obtain() @em finds
 +     * an instance without conferring @em ownership.
 +     */
 +    LLEventPump& obtain(const std::string& name);
 +    /**
 +     * Flush all known LLEventPump instances
 +     */
 +    void flush();
 +
 +private:
 +    friend class LLEventPump;
 +    /**
 +     * Register a new LLEventPump instance (internal)
 +     */
 +    std::string registerNew(const LLEventPump&, const std::string& name, bool tweak);
 +    /**
 +     * Unregister a doomed LLEventPump instance (internal)
 +     */
 +    void unregister(const LLEventPump&);
 +
 +private:
 +    LLEventPumps();
 +    ~LLEventPumps();
 +
 +testable:
 +    // Map of all known LLEventPump instances, whether or not we instantiated
 +    // them. We store a plain old LLEventPump* because this map doesn't claim
 +    // ownership of the instances. Though the common usage pattern is to
 +    // request an instance using obtain(), it's fair to instantiate an
 +    // LLEventPump subclass statically, as a class member, on the stack or on
 +    // the heap. In such cases, the instantiating party is responsible for its
 +    // lifespan.
 +    typedef std::map<std::string, LLEventPump*> PumpMap;
 +    PumpMap mPumpMap;
 +    // Set of all LLEventPumps we instantiated. Membership in this set means
 +    // we claim ownership, and will delete them when this LLEventPumps is
 +    // destroyed.
 +    typedef std::set<LLEventPump*> PumpSet;
 +    PumpSet mOurPumps;
 +    // LLEventPump names that should be instantiated as LLEventQueue rather
 +    // than as LLEventStream
 +    typedef std::set<std::string> PumpNames;
 +    PumpNames mQueueNames;
 +};
 +
 +/*****************************************************************************
 +*   details
 +*****************************************************************************/
 +namespace LLEventDetail
 +{
 +    /// Any callable capable of connecting an LLEventListener to an
 +    /// LLStandardSignal to produce an LLBoundListener can be mapped to this
 +    /// signature.
 +    typedef boost::function<LLBoundListener(const LLEventListener&)> ConnectFunc;
 +
 +    /**
 +     * Utility template function to use Visitor appropriately
 +     *
 +     * @param listener Callable to connect, typically a boost::bind()
 +     * expression. This will be visited by Visitor using boost::visit_each().
 +     * @param connect_func Callable that will connect() @a listener to an
 +     * LLStandardSignal, returning LLBoundListener.
 +     */
 +    template <typename LISTENER>
 +    LLBoundListener visit_and_connect(const LISTENER& listener,
 +                                      const ConnectFunc& connect_func);
 +} // namespace LLEventDetail
 +
 +/*****************************************************************************
 +*   LLEventTrackable
 +*****************************************************************************/
 +/**
 + * LLEventTrackable wraps boost::signals2::trackable, which resembles
 + * boost::trackable. Derive your listener class from LLEventTrackable instead,
 + * and use something like
 + * <tt>LLEventPump::listen(boost::bind(&YourTrackableSubclass::method,
 + * instance, _1))</tt>. This will implicitly disconnect when the object
 + * referenced by @c instance is destroyed.
 + *
 + * @note
 + * LLEventTrackable doesn't address a couple of cases:
 + * * Object destroyed during call
 + *   - You enter a slot call in thread A.
 + *   - Thread B destroys the object, which of course disconnects it from any
 + *     future slot calls.
 + *   - Thread A's call uses 'this', which now refers to a defunct object.
 + *     Undefined behavior results.
 + * * Call during destruction
 + *   - @c MySubclass is derived from LLEventTrackable.
 + *   - @c MySubclass registers one of its own methods using
 + *     <tt>LLEventPump::listen()</tt>.
 + *   - The @c MySubclass object begins destruction. <tt>~MySubclass()</tt>
 + *     runs, destroying state specific to the subclass. (For instance, a
 + *     <tt>Foo*</tt> data member is <tt>delete</tt>d but not zeroed.)
 + *   - The listening method will not be disconnected until
 + *     <tt>~LLEventTrackable()</tt> runs.
 + *   - Before we get there, another thread posts data to the @c LLEventPump
 + *     instance, calling the @c MySubclass method.
 + *   - The method in question relies on valid @c MySubclass state. (For
 + *     instance, it attempts to dereference the <tt>Foo*</tt> pointer that was
 + *     <tt>delete</tt>d but not zeroed.)
 + *   - Undefined behavior results.
 + * If you suspect you may encounter any such scenario, you're better off
 + * managing the lifespan of your object with <tt>boost::shared_ptr</tt>.
 + * Passing <tt>LLEventPump::listen()</tt> a <tt>boost::bind()</tt> expression
 + * involving a <tt>boost::weak_ptr<Foo></tt> is recognized specially, engaging
 + * thread-safe Boost.Signals2 machinery.
 + */
 +typedef boost::signals2::trackable LLEventTrackable;
 +
 +/*****************************************************************************
 +*   LLEventPump
 +*****************************************************************************/
 +/**
 + * LLEventPump is the base class interface through which we access the
 + * concrete subclasses LLEventStream and LLEventQueue.
 + *
 + * @NOTE
 + * LLEventPump derives from LLEventTrackable so that when you "chain"
 + * LLEventPump instances together, they will automatically disconnect on
 + * destruction. Please see LLEventTrackable documentation for situations in
 + * which this may be perilous across threads.
 + */
 +class LL_COMMON_API LLEventPump: public LLEventTrackable
 +{
 +public:
 +    /**
 +     * Exception thrown by LLEventPump(). You are trying to instantiate an
 +     * LLEventPump (subclass) using the same name as some other instance, and
 +     * you didn't pass <tt>tweak=true</tt> to permit it to generate a unique
 +     * variant.
 +     */
 +    struct DupPumpName: public std::runtime_error
 +    {
 +        DupPumpName(const std::string& what):
 +            std::runtime_error(std::string("DupPumpName: ") + what) {}
 +    };
 +
 +    /**
 +     * Instantiate an LLEventPump (subclass) with the string name by which it
 +     * can be found using LLEventPumps::obtain().
 +     *
 +     * If you pass (or default) @a tweak to @c false, then a duplicate name
 +     * will throw DupPumpName. This won't happen if LLEventPumps::obtain()
 +     * instantiates the LLEventPump, because obtain() uses find-or-create
 +     * logic. It can only happen if you instantiate an LLEventPump in your own
 +     * code -- and a collision with the name of some other LLEventPump is
 +     * likely to cause much more subtle problems!
 +     *
 +     * When you hand-instantiate an LLEventPump, consider passing @a tweak as
 +     * @c true. This directs LLEventPump() to append a suffix to the passed @a
 +     * name to make it unique. You can retrieve the adjusted name by calling
 +     * getName() on your new instance.
 +     */
 +    LLEventPump(const std::string& name, bool tweak=false);
 +    virtual ~LLEventPump();
 +
 +    /// group exceptions thrown by listen(). We use exceptions because these
 +    /// particular errors are likely to be coding errors, found and fixed by
 +    /// the developer even before preliminary checkin.
 +    struct ListenError: public std::runtime_error
 +    {
 +        ListenError(const std::string& what): std::runtime_error(what) {}
 +    };
 +    /**
 +     * exception thrown by listen(). You are attempting to register a
 +     * listener on this LLEventPump using the same listener name as an
 +     * already-registered listener.
 +     */
 +    struct DupListenerName: public ListenError
 +    {
 +        DupListenerName(const std::string& what):
 +            ListenError(std::string("DupListenerName: ") + what)
 +        {}
 +    };
 +    /**
 +     * exception thrown by listen(). The order dependencies specified for your
 +     * listener are incompatible with existing listeners.
 +     *
 +     * Consider listener "a" which specifies before "b" and "b" which
 +     * specifies before "c". You are now attempting to register "c" before
 +     * "a". There is no order that can satisfy all constraints.
 +     */
 +    struct Cycle: public ListenError
 +    {
 +        Cycle(const std::string& what): ListenError(std::string("Cycle: ") + what) {}
 +    };
 +    /**
 +     * exception thrown by listen(). This one means that your new listener
 +     * would force a change to the order of previously-registered listeners,
 +     * and we don't have a good way to implement that.
 +     *
 +     * Consider listeners "some", "other" and "third". "some" and "other" are
 +     * registered earlier without specifying relative order, so "other"
 +     * happens to be first. Now you attempt to register "third" after "some"
 +     * and before "other". Whoops, that would require swapping "some" and
 +     * "other", which we can't do. Instead we throw this exception.
 +     *
 +     * It may not be possible to change the registration order so we already
 +     * know "third"s order requirement by the time we register the second of
 +     * "some" and "other". A solution would be to specify that "some" must
 +     * come before "other", or equivalently that "other" must come after
 +     * "some".
 +     */
 +    struct OrderChange: public ListenError
 +    {
 +        OrderChange(const std::string& what): ListenError(std::string("OrderChange: ") + what) {}
 +    };
 +
 +    /// used by listen()
 +    typedef std::vector<std::string> NameList;
 +    /// convenience placeholder for when you explicitly want to pass an empty
 +    /// NameList
 +    const static NameList empty;
 +
 +    /// Get this LLEventPump's name
 +    std::string getName() const { return mName; }
 +
 +    /**
 +     * Register a new listener with a unique name. Specify an optional list
 +     * of other listener names after which this one must be called, likewise
 +     * an optional list of other listener names before which this one must be
 +     * called. The other listeners mentioned need not yet be registered
 +     * themselves. listen() can throw any ListenError; see ListenError
 +     * subclasses.
 +     *
 +     * The listener name must be unique among active listeners for this
 +     * LLEventPump, else you get DupListenerName. If you don't care to invent
 +     * a name yourself, use inventName(). (I was tempted to recognize e.g. ""
 +     * and internally generate a distinct name for that case. But that would
 +     * handle badly the scenario in which you want to add, remove, re-add,
 +     * etc. the same listener: each new listen() call would necessarily
 +     * perform a new dependency sort. Assuming you specify the same
 +     * after/before lists each time, using inventName() when you first
 +     * instantiate your listener, then passing the same name on each listen()
 +     * call, allows us to optimize away the second and subsequent dependency
 +     * sorts.
 +     *
 +     * If (as is typical) you pass a <tt>boost::bind()</tt> expression as @a
 +     * listener, listen() will inspect the components of that expression. If a
 +     * bound object matches any of several cases, the connection will
 +     * automatically be disconnected when that object is destroyed.
 +     *
 +     * * You bind a <tt>boost::weak_ptr</tt>.
 +     * * Binding a <tt>boost::shared_ptr</tt> that way would ensure that the
 +     *   referenced object would @em never be destroyed, since the @c
 +     *   shared_ptr stored in the LLEventPump would remain an outstanding
 +     *   reference. Use the weaken() function to convert your @c shared_ptr to
 +     *   @c weak_ptr. Because this is easy to forget, binding a @c shared_ptr
 +     *   will produce a compile error (@c BOOST_STATIC_ASSERT failure).
 +     * * You bind a simple pointer or reference to an object derived from
 +     *   <tt>boost::enable_shared_from_this</tt>. (UNDER CONSTRUCTION)
 +     * * You bind a simple pointer or reference to an object derived from
 +     *   LLEventTrackable. Unlike the cases described above, though, this is
 +     *   vulnerable to a couple of cross-thread race conditions, as described
 +     *   in the LLEventTrackable documentation.
 +     */
 +    template <typename LISTENER>
 +    LLBoundListener listen(const std::string& name, const LISTENER& listener,
 +                           const NameList& after=NameList(),
 +                           const NameList& before=NameList())
 +    {
 +        // Examine listener, using our listen_impl() method to make the
 +        // actual connection.
 +        // This is why listen() is a template. Conversion from boost::bind()
 +        // to LLEventListener performs type erasure, so it's important to look
 +        // at the boost::bind object itself before that happens.
 +        return LLEventDetail::visit_and_connect(listener,
 +                                                boost::bind(&LLEventPump::listen_impl,
 +                                                            this,
 +                                                            name,
 +                                                            _1,
 +                                                            after,
 +                                                            before));
 +    }
 +
 +    /// Get the LLBoundListener associated with the passed name (dummy
 +    /// LLBoundListener if not found)
 +    virtual LLBoundListener getListener(const std::string& name) const;
 +    /**
 +     * Instantiate one of these to block an existing connection:
 +     * @code
 +     * { // in some local scope
 +     *     LLEventPump::Blocker block(someLLBoundListener);
 +     *     // code that needs the connection blocked
 +     * } // unblock the connection again
 +     * @endcode
 +     */
 +    typedef boost::signals2::shared_connection_block Blocker;
 +    /// Unregister a listener by name. Prefer this to
 +    /// <tt>getListener(name).disconnect()</tt> because stopListening() also
 +    /// forgets this name.
 +    virtual void stopListening(const std::string& name);
 +    /// Post an event to all listeners. The @c bool return is only meaningful
 +    /// if the underlying leaf class is LLEventStream -- beware of relying on
 +    /// it too much! Truthfully, we return @c bool mostly to permit chaining
 +    /// one LLEventPump as a listener on another.
 +    virtual bool post(const LLSD&) = 0;
 +    /// Enable/disable: while disabled, silently ignore all post() calls
 +    virtual void enable(bool enabled=true) { mEnabled = enabled; }
 +    /// query
 +    virtual bool enabled() const { return mEnabled; }
 +
 +    /// Generate a distinct name for a listener -- see listen()
 +    static std::string inventName(const std::string& pfx="listener");
 +
 +private:
 +    friend class LLEventPumps;
 +    /// flush queued events
 +    virtual void flush() {}
 +
 +private:
 +    virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
 +                                        const NameList& after,
 +                                        const NameList& before);
 +    std::string mName;
 +
 +protected:
 +    /// implement the dispatching
 +    LLStandardSignal mSignal;
 +    /// valve open?
 +    bool mEnabled;
 +    /// Map of named listeners. This tracks the listeners that actually exist
 +    /// at this moment. When we stopListening(), we discard the entry from
 +    /// this map.
 +    typedef std::map<std::string, boost::signals2::connection> ConnectionMap;
 +    ConnectionMap mConnections;
 +    typedef LLDependencies<std::string, float> DependencyMap;
 +    /// Dependencies between listeners. For each listener, track the float
 +    /// used to establish its place in mSignal's order. This caches all the
 +    /// listeners that have ever registered; stopListening() does not discard
 +    /// the entry from this map. This is to avoid a new dependency sort if the
 +    /// same listener with the same dependencies keeps hopping on and off this
 +    /// LLEventPump.
 +    DependencyMap mDeps;
 +};
 +
 +/*****************************************************************************
 +*   LLEventStream
 +*****************************************************************************/
 +/**
 + * LLEventStream is a thin wrapper around LLStandardSignal. Posting an
 + * event immediately calls all registered listeners.
 + */
 +class LL_COMMON_API LLEventStream: public LLEventPump
 +{
 +public:
 +    LLEventStream(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
 +    virtual ~LLEventStream() {}
 +
 +    /// Post an event to all listeners
 +    virtual bool post(const LLSD& event);
 +};
 +
 +/*****************************************************************************
 +*   LLEventQueue
 +*****************************************************************************/
 +/**
 + * LLEventQueue isa LLEventPump whose post() method defers calling registered
 + * listeners until flush() is called.
 + */
 +class LL_COMMON_API LLEventQueue: public LLEventPump
 +{
 +public:
 +    LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
 +    virtual ~LLEventQueue() {}
 +
 +    /// Post an event to all listeners
 +    virtual bool post(const LLSD& event);
 +
 +private:
 +    /// flush queued events
 +    virtual void flush();
 +
 +private:
 +    typedef std::deque<LLSD> EventQueue;
 +    EventQueue mEventQueue;
 +};
 +
 +/*****************************************************************************
 +*   LLReqID
 +*****************************************************************************/
 +/**
 + * This class helps the implementer of a given event API to honor the
 + * ["reqid"] convention. By this convention, each event API stamps into its
 + * response LLSD a ["reqid"] key whose value echoes the ["reqid"] value, if
 + * any, from the corresponding request.
 + *
 + * This supports an (atypical, but occasionally necessary) use case in which
 + * two or more asynchronous requests are multiplexed onto the same ["reply"]
 + * LLEventPump. Since the response events could arrive in arbitrary order, the
 + * caller must be able to demux them. It does so by matching the ["reqid"]
 + * value in each response with the ["reqid"] value in the corresponding
 + * request.
 + *
 + * It is the caller's responsibility to ensure distinct ["reqid"] values for
 + * that case. Though LLSD::UUID is guaranteed to work, it might be overkill:
 + * the "namespace" of unique ["reqid"] values is simply the set of requests
 + * specifying the same ["reply"] LLEventPump name.
 + *
 + * Making a given event API echo the request's ["reqid"] into the response is
 + * nearly trivial. This helper is mostly for mnemonic purposes, to serve as a
 + * place to put these comments. We hope that each time a coder implements a
 + * new event API based on some existing one, s/he will say, "Huh, what's an
 + * LLReqID?" and look up this material.
 + *
 + * The hardest part about the convention is deciding where to store the
 + * ["reqid"] value. Ironically, LLReqID can't help with that: you must store
 + * an LLReqID instance in whatever storage will persist until the reply is
 + * sent. For example, if the request ultimately ends up using a Responder
 + * subclass, storing an LLReqID instance in the Responder works.
 + *
 + * @note
 + * The @em implementer of an event API must honor the ["reqid"] convention.
 + * However, the @em caller of an event API need only use it if s/he is sharing
 + * the same ["reply"] LLEventPump for two or more asynchronous event API
 + * requests.
 + *
 + * In most cases, it's far easier for the caller to instantiate a local
 + * LLEventStream and pass its name to the event API in question. Then it's
 + * perfectly reasonable not to set a ["reqid"] key in the request, ignoring
 + * the @c isUndefined() ["reqid"] value in the response.
 + */
 +class LL_COMMON_API LLReqID
 +{
 +public:
 +    /**
 +     * If you have the request in hand at the time you instantiate the
 +     * LLReqID, pass that request to extract its ["reqid"].
 + */
 +    LLReqID(const LLSD& request):
 +        mReqid(request["reqid"])
 +    {}
 +    /// If you don't yet have the request, use setFrom() later.
 +    LLReqID() {}
 +
 +    /// Extract and store the ["reqid"] value from an incoming request.
 +    void setFrom(const LLSD& request)
 +    {
 +        mReqid = request["reqid"];
 +    }
 +
 +    /// Set ["reqid"] key into a pending response LLSD object.
 +    void stamp(LLSD& response) const;
 +
 +    /// Make a whole new response LLSD object with our ["reqid"].
 +    LLSD makeResponse() const
 +    {
 +        LLSD response;
 +        stamp(response);
 +        return response;
 +    }
 +
 +    /// Not really sure of a use case for this accessor...
 +    LLSD getReqID() const { return mReqid; }
 +
 +private:
 +    LLSD mReqid;
 +};
 +
 +/*****************************************************************************
 +*   Underpinnings
 +*****************************************************************************/
 +/**
 + * We originally provided a suite of overloaded
 + * LLEventTrackable::listenTo(LLEventPump&, ...) methods that would call
 + * LLEventPump::listen(...) and then pass the returned LLBoundListener to
 + * LLEventTrackable::track(). This was workable but error-prone: the coder
 + * must remember to call listenTo() rather than the more straightforward
 + * listen() method.
 + *
 + * Now we publish only the single canonical listen() method, so there's a
 + * uniform mechanism. Having a single way to do this is good, in that there's
 + * no question in the coder's mind which of several alternatives to choose.
 + *
 + * To support automatic connection management, we use boost::visit_each
 + * (http://www.boost.org/doc/libs/1_37_0/doc/html/boost/visit_each.html) to
 + * inspect each argument of a boost::bind expression. (Although the visit_each
 + * mechanism was first introduced with the original Boost.Signals library, it
 + * was only later documented.)
 + *
 + * Cases:
 + * * At least one of the function's arguments is a boost::weak_ptr<T>. Pass
 + *   the corresponding shared_ptr to slot_type::track(). Ideally that would be
 + *   the object whose method we want to call, but in fact we do the same for
 + *   any weak_ptr we might find among the bound arguments. If we're passing
 + *   our bound method a weak_ptr to some object, wouldn't the destruction of
 + *   that object invalidate the call? So we disconnect automatically when any
 + *   such object is destroyed. This is the mechanism preferred by boost::
 + *   signals2.
 + * * One of the functions's arguments is a boost::shared_ptr<T>. This produces
 + *   a compile error: the bound copy of the shared_ptr stored in the
 + *   boost_bind object stored in the signal object would make the referenced
 + *   T object immortal. We provide a weaken() function. Pass
 + *   weaken(your_shared_ptr) instead. (We can inspect, but not modify, the
 + *   boost::bind object. Otherwise we'd replace the shared_ptr with weak_ptr
 + *   implicitly and just proceed.)
 + * * One of the function's arguments is a plain pointer/reference to an object
 + *   derived from boost::enable_shared_from_this. We assume that this object
 + *   is managed using boost::shared_ptr, so we implicitly extract a shared_ptr
 + *   and track that. (UNDER CONSTRUCTION)
 + * * One of the function's arguments is derived from LLEventTrackable. Pass
 + *   the LLBoundListener to its LLEventTrackable::track(). This is vulnerable
 + *   to a couple different race conditions, as described in LLEventTrackable
 + *   documentation. (NOTE: Now that LLEventTrackable is a typedef for
 + *   boost::signals2::trackable, the Signals2 library handles this itself, so
 + *   our visitor needs no special logic for this case.)
 + * * Any other argument type is irrelevant to automatic connection management.
 + */
 +
 +namespace LLEventDetail
 +{
 +    template <typename F>
 +    const F& unwrap(const F& f) { return f; }
 +
 +    template <typename F>
 +    const F& unwrap(const boost::reference_wrapper<F>& f) { return f.get(); }
 +
 +    // Most of the following is lifted from the Boost.Signals use of
 +    // visit_each.
 +    template<bool Cond> struct truth {};
 +
 +    /**
 +     * boost::visit_each() Visitor, used on a template argument <tt>const F&
 +     * f</tt> as follows (see visit_and_connect()):
 +     * @code
 +     * LLEventListener listener(f);
 +     * Visitor visitor(listener); // bind listener so it can track() shared_ptrs
 +     * using boost::visit_each;   // allow unqualified visit_each() call for ADL
 +     * visit_each(visitor, unwrap(f));
 +     * @endcode
 +     */
 +    class Visitor
 +    {
 +    public:
 +        /**
 +         * Visitor binds a reference to LLEventListener so we can track() any
 +         * shared_ptrs we find in the argument list.
 +         */
 +        Visitor(LLEventListener& listener):
 +            mListener(listener)
 +        {
 +        }
 +
 +        /**
 +         * boost::visit_each() calls this method for each component of a
 +         * boost::bind() expression.
 +         */
 +        template <typename T>
 +        void operator()(const T& t) const
 +        {
 +            decode(t, 0);
 +        }
 +
 +    private:
 +        // decode() decides between a reference wrapper and anything else
 +        // boost::ref() variant
 +        template<typename T>
 +        void decode(const boost::reference_wrapper<T>& t, int) const
 +        {
 +//          add_if_trackable(t.get_pointer());
 +        }
 +
 +        // decode() anything else
 +        template<typename T>
 +        void decode(const T& t, long) const
 +        {
 +            typedef truth<(boost::is_pointer<T>::value)> is_a_pointer;
 +            maybe_get_pointer(t, is_a_pointer());
 +        }
 +
 +        // maybe_get_pointer() decides between a pointer and a non-pointer
 +        // plain pointer variant
 +        template<typename T>
 +        void maybe_get_pointer(const T& t, truth<true>) const
 +        {
 +//          add_if_trackable(t);
 +        }
 +
 +        // shared_ptr variant
 +        template<typename T>
 +        void maybe_get_pointer(const boost::shared_ptr<T>& t, truth<false>) const
 +        {
 +            // If we have a shared_ptr to this object, it doesn't matter
 +            // whether the object is derived from LLEventTrackable, so no
 +            // further analysis of T is needed.
 +//          mListener.track(t);
 +
 +            // Make this case illegal. Passing a bound shared_ptr to
 +            // slot_type::track() is useless, since the bound shared_ptr will
 +            // keep the object alive anyway! Force the coder to cast to weak_ptr.
 +
 +            // Trivial as it is, make the BOOST_STATIC_ASSERT() condition
 +            // dependent on template param so the macro is only evaluated if
 +            // this method is in fact instantiated, as described here:
 +            // http://www.boost.org/doc/libs/1_34_1/doc/html/boost_staticassert.html
 +
 +            // ATTENTION: Don't bind a shared_ptr<anything> using
 +            // LLEventPump::listen(boost::bind()). Doing so captures a copy of
 +            // the shared_ptr, making the referenced object effectively
 +            // immortal. Use the weaken() function, e.g.:
 +            // somepump.listen(boost::bind(...weaken(my_shared_ptr)...));
 +            // This lets us automatically disconnect when the referenced
 +            // object is destroyed.
 +            BOOST_STATIC_ASSERT(sizeof(T) == 0);
 +        }
 +
 +        // weak_ptr variant
 +        template<typename T>
 +        void maybe_get_pointer(const boost::weak_ptr<T>& t, truth<false>) const
 +        {
 +            // If we have a weak_ptr to this object, it doesn't matter
 +            // whether the object is derived from LLEventTrackable, so no
 +            // further analysis of T is needed.
 +            mListener.track(t);
 +//          std::cout << "Found weak_ptr<" << typeid(T).name() << ">!\n";
 +        }
 +
 +#if 0
 +        // reference to anything derived from boost::enable_shared_from_this
 +        template <typename T>
 +        inline void maybe_get_pointer(const boost::enable_shared_from_this<T>& ct,
 +                                      truth<false>) const
 +        {
 +            // Use the slot_type::track(shared_ptr) mechanism. Cast away
 +            // const-ness because (in our code base anyway) it's unusual
 +            // to find shared_ptr<const T>.
 +            boost::enable_shared_from_this<T>&
 +                t(const_cast<boost::enable_shared_from_this<T>&>(ct));
 +            std::cout << "Capturing shared_from_this()" << std::endl;
 +            boost::shared_ptr<T> sp(t.shared_from_this());
 +/*==========================================================================*|
 +            std::cout << "Capturing weak_ptr" << std::endl;
 +            boost::weak_ptr<T> wp(sp);
 +|*==========================================================================*/
 +            std::cout << "Tracking shared__ptr" << std::endl;
 +            mListener.track(sp);
 +        }
 +#endif
 +
 +        // non-pointer variant
 +        template<typename T>
 +        void maybe_get_pointer(const T& t, truth<false>) const
 +        {
 +            // Take the address of this object, because the object itself may be
 +            // trackable
 +//          add_if_trackable(boost::addressof(t));
 +        }
 +
 +/*==========================================================================*|
 +        // add_if_trackable() adds LLEventTrackable objects to mTrackables
 +        inline void add_if_trackable(const LLEventTrackable* t) const
 +        {
 +            if (t)
 +            {
 +            }
 +        }
 +
 +        // pointer to anything not an LLEventTrackable subclass
 +        inline void add_if_trackable(const void*) const
 +        {
 +        }
 +
 +        // pointer to free function
 +        // The following construct uses the preprocessor to generate
 +        // add_if_trackable() overloads accepting pointer-to-function taking
 +        // 0, 1, ..., LLEVENTS_LISTENER_ARITY parameters of arbitrary type.
 +#define BOOST_PP_LOCAL_MACRO(n)                                     \
 +        template <typename R                                        \
 +                  BOOST_PP_COMMA_IF(n)                              \
 +                  BOOST_PP_ENUM_PARAMS(n, typename T)>              \
 +        inline void                                                 \
 +        add_if_trackable(R (*)(BOOST_PP_ENUM_PARAMS(n, T))) const   \
 +        {                                                           \
 +        }
 +#define BOOST_PP_LOCAL_LIMITS (0, LLEVENTS_LISTENER_ARITY)
 +#include BOOST_PP_LOCAL_ITERATE()
 +#undef  BOOST_PP_LOCAL_MACRO
 +#undef  BOOST_PP_LOCAL_LIMITS
 +|*==========================================================================*/
 +
 +        /// Bind a reference to the LLEventListener to call its track() method.
 +        LLEventListener& mListener;
 +    };
 +
 +    /**
 +     * Utility template function to use Visitor appropriately
 +     *
 +     * @param raw_listener Callable to connect, typically a boost::bind()
 +     * expression. This will be visited by Visitor using boost::visit_each().
 +     * @param connect_funct Callable that will connect() @a raw_listener to an
 +     * LLStandardSignal, returning LLBoundListener.
 +     */
 +    template <typename LISTENER>
 +    LLBoundListener visit_and_connect(const LISTENER& raw_listener,
 +                                      const ConnectFunc& connect_func)
 +    {
 +        // Capture the listener
 +        LLEventListener listener(raw_listener);
 +        // Define our Visitor, binding the listener so we can call
 +        // listener.track() if we discover any shared_ptr<Foo>.
 +        LLEventDetail::Visitor visitor(listener);
 +        // Allow unqualified visit_each() call for ADL
 +        using boost::visit_each;
 +        // Visit each component of a boost::bind() expression. Pass
 +        // 'raw_listener', our template argument, rather than 'listener' from
 +        // which type details have been erased. unwrap() comes from
 +        // Boost.Signals, in case we were passed a boost::ref().
 +        visit_each(visitor, LLEventDetail::unwrap(raw_listener));
 +        // Make the connection using passed function. At present, wrapping
 +        // this functionality into this function is a bit silly: we don't
 +        // really need a visit_and_connect() function any more, just a visit()
 +        // function. The definition of this function dates from when, after
 +        // visit_each(), after establishing the connection, we had to
 +        // postprocess the new connection with the visitor object. That's no
 +        // longer necessary.
 +        return connect_func(listener);
 +    }
 +} // namespace LLEventDetail
 +
 +// Somewhat to my surprise, passing boost::bind(...boost::weak_ptr<T>...) to
 +// listen() fails in Boost code trying to instantiate LLEventListener (i.e.
 +// LLStandardSignal::slot_type) because the boost::get_pointer() utility function isn't
 +// specialized for boost::weak_ptr. This remedies that omission.
 +namespace boost
 +{
 +    template <typename T>
 +    T* get_pointer(const weak_ptr<T>& ptr) { return shared_ptr<T>(ptr).get(); }
 +}
 +
 +/// Since we forbid use of listen(boost::bind(...shared_ptr<T>...)), provide an
 +/// easy way to cast to the corresponding weak_ptr.
 +template <typename T>
 +boost::weak_ptr<T> weaken(const boost::shared_ptr<T>& ptr)
 +{
 +    return boost::weak_ptr<T>(ptr);
 +}
 +
 +#endif /* ! defined(LL_LLEVENTS_H) */
 | 
