llevents.h
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:43k
- /**
- * @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-2010, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/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"
- #include "ll_template_cast.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();
- /**
- * Reset all known LLEventPump instances
- * workaround for DEV-35406 crash on shutdown
- */
- void reset();
- 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;
- /// overload of visit_and_connect() when we have a string identifier available
- template <typename LISTENER>
- LLBoundListener visit_and_connect(const std::string& name,
- const LISTENER& listener,
- const ConnectFunc& connect_func);
- /**
- * 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)
- {
- return visit_and_connect("", listener, 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(name,
- 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() {}
- virtual void reset();
- private:
- virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
- const NameList& after,
- const NameList& before);
- std::string mName;
- protected:
- /// implement the dispatching
- boost::shared_ptr<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;
- };
- /**
- * Base class for LLListenerWrapper. See visit_and_connect() and llwrap(). We
- * provide virtual @c accept_xxx() methods, customization points allowing a
- * subclass access to certain data visible at LLEventPump::listen() time.
- * Example subclass usage:
- *
- * @code
- * myEventPump.listen("somename",
- * llwrap<MyListenerWrapper>(boost::bind(&MyClass::method, instance, _1)));
- * @endcode
- *
- * Because of the anticipated usage (note the anonymous temporary
- * MyListenerWrapper instance in the example above), the @c accept_xxx()
- * methods must be @c const.
- */
- class LL_COMMON_API LLListenerWrapperBase
- {
- public:
- /// New instance. The accept_xxx() machinery makes it important to use
- /// shared_ptrs for our data. Many copies of this object are made before
- /// the instance that actually ends up in the signal, yet accept_xxx()
- /// will later be called on the @em original instance. All copies of the
- /// same original instance must share the same data.
- LLListenerWrapperBase():
- mName(new std::string),
- mConnection(new LLBoundListener)
- {
- }
-
- /// Copy constructor. Copy shared_ptrs to original instance data.
- LLListenerWrapperBase(const LLListenerWrapperBase& that):
- mName(that.mName),
- mConnection(that.mConnection)
- {
- }
- virtual ~LLListenerWrapperBase() {}
- /// Ask LLEventPump::listen() for the listener name
- virtual void accept_name(const std::string& name) const
- {
- *mName = name;
- }
- /// Ask LLEventPump::listen() for the new connection
- virtual void accept_connection(const LLBoundListener& connection) const
- {
- *mConnection = connection;
- }
- protected:
- /// Listener name.
- boost::shared_ptr<std::string> mName;
- /// Connection.
- boost::shared_ptr<LLBoundListener> mConnection;
- };
- /*****************************************************************************
- * 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 std::string& name,
- 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.
- LLBoundListener connection(connect_func(listener));
- // If the LISTENER is an LLListenerWrapperBase subclass, pass it the
- // desired information. It's important that we pass the raw_listener
- // so the compiler can make decisions based on its original type.
- const LLListenerWrapperBase* lwb =
- ll_template_cast<const LLListenerWrapperBase*>(&raw_listener);
- if (lwb)
- {
- lwb->accept_name(name);
- lwb->accept_connection(connection);
- }
- // In any case, show new connection to caller.
- return connection;
- }
- } // 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) */