diff options
Diffstat (limited to 'indra/llcommon')
| -rw-r--r-- | indra/llcommon/lleventdispatcher.cpp | 118 | ||||
| -rw-r--r-- | indra/llcommon/lleventdispatcher.h | 54 | 
2 files changed, 129 insertions, 43 deletions
| diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp index caff854753..0079b9ce36 100644 --- a/indra/llcommon/lleventdispatcher.cpp +++ b/indra/llcommon/lleventdispatcher.cpp @@ -103,7 +103,8 @@ class LL_COMMON_API LLEventDispatcher::LLSDArgsMapper  public:      /// Accept description of function: function name, param names, param      /// default values -    LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults); +    LLSDArgsMapper(LLEventDispatcher* parent, const std::string& function, +                   const LLSD& names, const LLSD& defaults);      /// Given arguments map, return LLSD::Array of parameter values, or      /// trigger error. @@ -114,6 +115,9 @@ private:      template <typename... ARGS>      void callFail(ARGS&&... args) const; +    // store a plain dumb back-pointer because we don't have to manage the +    // parent LLEventDispatcher's lifespan +    LLEventDispatcher* _parent;      // The function-name string is purely descriptive. We want error messages      // to be able to indicate which function's LLSDArgsMapper has the problem.      std::string _function; @@ -132,9 +136,11 @@ private:      FilledVector _has_dft;  }; -LLEventDispatcher::LLSDArgsMapper::LLSDArgsMapper(const std::string& function, +LLEventDispatcher::LLSDArgsMapper::LLSDArgsMapper(LLEventDispatcher* parent, +                                                  const std::string& function,                                                    const LLSD& names,                                                    const LLSD& defaults): +    _parent(parent),      _function(function),      _names(names),      _has_dft(names.size()) @@ -334,7 +340,7 @@ std::string LLEventDispatcher::LLSDArgsMapper::formatlist(const LLSD& list)  template <typename... ARGS>  void LLEventDispatcher::LLSDArgsMapper::callFail(ARGS&&... args) const  { -    LLEventDispatcher::sCallFail<LLEventDispatcher::DispatchError> +    _parent->callFail<LLEventDispatcher::DispatchError>          (_function, std::forward<ARGS>(args)...);  } @@ -356,7 +362,8 @@ LLEventDispatcher::~LLEventDispatcher()  {  } -LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc): +LLEventDispatcher::DispatchEntry::DispatchEntry(LLEventDispatcher* parent, const std::string& desc): +    mParent(parent),      mDesc(desc)  {} @@ -365,8 +372,9 @@ LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):   */  struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry  { -    LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required): -        DispatchEntry(desc), +    LLSDDispatchEntry(LLEventDispatcher* parent, const std::string& desc, +                      const Callable& func, const LLSD& required): +        DispatchEntry(parent, desc),          mFunc(func),          mRequired(required)      {} @@ -380,8 +388,7 @@ struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchE          std::string mismatch(llsd_matches(mRequired, event));          if (! mismatch.empty())          { -            LLEventDispatcher::sCallFail<LLEventDispatcher::DispatchError> -                (desc, ": bad request: ", mismatch); +            return callFail(desc, ": bad request: ", mismatch);          }          // Event syntax looks good, go for it!          return mFunc(event); @@ -400,9 +407,9 @@ struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchE   */  struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry  { -    ParamsDispatchEntry(const std::string& name, const std::string& desc, -                        const invoker_function& func): -        DispatchEntry(desc), +    ParamsDispatchEntry(LLEventDispatcher* parent, const std::string& name, +                        const std::string& desc, const invoker_function& func): +        DispatchEntry(parent, desc),          mName(name),          mInvoker(func)      {} @@ -422,14 +429,6 @@ struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::Dispatc              return callFail(err.what());          }      } - -    template <typename... ARGS> -    LLSD callFail(ARGS&&... args) const -    { -        LLEventDispatcher::sCallFail<LLEventDispatcher::DispatchError>(mName, ": ", std::forward<ARGS>(args)...); -        // pacify the compiler -        return {}; -    }  };  /** @@ -438,9 +437,10 @@ struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::Dispatc   */  struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry  { -    ArrayParamsDispatchEntry(const std::string& name, const std::string& desc, -                             const invoker_function& func, LLSD::Integer arity): -        ParamsDispatchEntry(name, desc, func), +    ArrayParamsDispatchEntry(LLEventDispatcher* parent, const std::string& name, +                             const std::string& desc, const invoker_function& func, +                             LLSD::Integer arity): +        ParamsDispatchEntry(parent, name, desc, func),          mArity(arity)      {} @@ -503,11 +503,11 @@ struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::Pa   */  struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry  { -    MapParamsDispatchEntry(const std::string& name, const std::string& desc, -                           const invoker_function& func, +    MapParamsDispatchEntry(LLEventDispatcher* parent, const std::string& name, +                           const std::string& desc, const invoker_function& func,                             const LLSD& params, const LLSD& defaults): -        ParamsDispatchEntry(name, desc, func), -        mMapper(name, params, defaults), +        ParamsDispatchEntry(parent, name, desc, func), +        mMapper(parent, name, params, defaults),          mRequired(LLSD::emptyMap())      {          // Build the set of all param keys, then delete the ones that are @@ -581,11 +581,9 @@ void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,                                                      const invoker_function& invoker,                                                      LLSD::Integer arity)  { -    // The first parameter to ArrayParamsDispatchEntry is solely for error -    // messages. Identify our instance and this entry.      mDispatch.emplace(          name, -        new ArrayParamsDispatchEntry(stringize(*this, '[', name, ']'), desc, invoker, arity)); +        new ArrayParamsDispatchEntry(this, "", desc, invoker, arity));  }  void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name, @@ -597,15 +595,14 @@ void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,      // Pass instance info as well as this entry name for error messages.      mDispatch.emplace(          name, -        new MapParamsDispatchEntry(stringize(*this, '[', name, ']'), -                                   desc, invoker, params, defaults)); +        new MapParamsDispatchEntry(this, "", desc, invoker, params, defaults));  }  /// Register a callable by name  void LLEventDispatcher::addLLSD(const std::string& name, const std::string& desc,                                  const Callable& callable, const LLSD& required)  { -    mDispatch.emplace(name, new LLSDDispatchEntry(desc, callable, required)); +    mDispatch.emplace(name, new LLSDDispatchEntry(this, desc, callable, required));  }  /// Unregister a callable @@ -682,7 +679,7 @@ LLSD LLEventDispatcher::try_call(const std::string& key, const std::string& name      DispatchMap::const_iterator found = mDispatch.find(name);      if (found == mDispatch.end())      { -        // Here we were passed a valid name, but there's no registered +        // Here we were passed a non-empty name, but there's no registered          // callable with that name. This is the one case in which we throw          // DispatchMissing instead of the generic DispatchError.          // Distinguish the public method by which our caller reached here: @@ -699,8 +696,10 @@ LLSD LLEventDispatcher::try_call(const std::string& key, const std::string& name      }      // Found the name, so it's plausible to even attempt the call. -    return found->second->call(stringize(*this, " calling ", std::quoted(name)), -                               event, (! key.empty()), mArgskey); +    const char* delim = (key.empty()? "" : "="); +    // append either "[key=name]" or just "[name]" +    SetState transient(this, '[', key, delim, name, ']'); +    return found->second->call("", event, (! key.empty()), mArgskey);  }  template <typename EXCEPTION, typename... ARGS> @@ -735,7 +734,34 @@ LLSD LLEventDispatcher::getMetadata(const std::string& name) const  std::ostream& operator<<(std::ostream& out, const LLEventDispatcher& self)  {      // If we're a subclass of LLEventDispatcher, e.g. LLEventAPI, report that. -    return out << LLError::Log::classname(self) << '(' << self.mDesc << ')'; +    // Also report whatever transient state is active. +    return out << LLError::Log::classname(self) << '(' << self.mDesc << ')' +               << self.getState(); +} + +std::string LLEventDispatcher::getState() const +{ +    // default value of fiber_specific_ptr is nullptr, and ~SetState() reverts +    // to that; infer empty string +    if (! mState.get()) +        return {}; +    else +        return *mState; +} + +bool LLEventDispatcher::setState(SetState&, const std::string& state) const +{ +    // If SetState is instantiated at multiple levels of function call, ignore  +    // the lower-level call because the outer call presumably provides more +    // context. +    if (mState.get()) +        return false; + +    // Pass us empty string (a la ~SetState()) to reset to nullptr, else take +    // a heap copy of the passed state string so we can delete it on +    // subsequent reset(). +    mState.reset(state.empty()? nullptr : new std::string(state)); +    return true;  }  /***************************************************************************** @@ -802,6 +828,8 @@ void LLDispatchListener::call_map(const LLSD& reqmap, const LLSD& event) const  {      // LLSD map containing returned values      LLSD result; +    // cache dispatch key +    std::string key{ getDispatchKey() };      // collect any error messages here      std::ostringstream errors;      const char* delim = ""; @@ -812,6 +840,9 @@ void LLDispatchListener::call_map(const LLSD& reqmap, const LLSD& event) const          const LLSD& args{ pair.second };          try          { +            // in case of errors, tell user the dispatch key, the fact that +            // we're processing a request map and the current key in that map +            SetState(this, '[', key, '[', name, "]]");              // With this form, capture return value even if undefined:              // presence of the key in the response map can be used to detect              // which request keys succeeded. @@ -819,10 +850,8 @@ void LLDispatchListener::call_map(const LLSD& reqmap, const LLSD& event) const          }          catch (const DispatchError& err)          { -            // collect message in 'errors', with hint as to which request map -            // entry failed -            errors << delim << err.what() << " (" << getDispatchKey() -                   << '[' << name << "])"; +            // collect message in 'errors' +            errors << delim << err.what();              delim = "\n";          }      } @@ -850,6 +879,8 @@ void LLDispatchListener::call_array(const LLSD& reqarray, const LLSD& event) con  {      // LLSD array containing returned values      LLSD results; +    // cache the dispatch key +    std::string key{ getDispatchKey() };      // arguments array, if present -- const because, if it's shorter than      // reqarray, we don't want to grow it      const LLSD argsarray{ event[getArgsKey()] }; @@ -883,13 +914,16 @@ void LLDispatchListener::call_array(const LLSD& reqarray, const LLSD& event) con          // reqentry is one of the valid forms, got name and args          try          { +            // in case of errors, tell user the dispatch key, the fact that +            // we're processing a request array, the current entry in that +            // array and the corresponding callable name +            SetState(this, '[', key, '[', i, "]=", name, ']');              // With this form, capture return value even if undefined              results.append((*this)(name, args));          }          catch (const DispatchError& err)          { -            // append hint as to which requentry produced the error -            error = stringize(err.what(), " (", getDispatchKey(), '[', i, ']'); +            error = err.what();              break;          }      } diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h index 789a59459c..2c5c62dd50 100644 --- a/indra/llcommon/lleventdispatcher.h +++ b/indra/llcommon/lleventdispatcher.h @@ -32,6 +32,7 @@  #if ! defined(LL_LLEVENTDISPATCHER_H)  #define LL_LLEVENTDISPATCHER_H +#include <boost/fiber/fss.hpp>  #include <boost/function_types/is_member_function_pointer.hpp>  #include <boost/function_types/is_nonmember_callable_builtin.hpp>  #include <boost/hof/is_invocable.hpp> // until C++17, when we get std::is_invocable @@ -460,14 +461,26 @@ public:  private:      struct DispatchEntry      { -        DispatchEntry(const std::string& desc); +        DispatchEntry(LLEventDispatcher* parent, const std::string& desc);          virtual ~DispatchEntry() {} // suppress MSVC warning, sigh +        // store a plain dumb back-pointer because the parent +        // LLEventDispatcher manages the lifespan of each DispatchEntry +        // subclass instance -- not the other way around +        LLEventDispatcher* mParent;          std::string mDesc;          virtual LLSD call(const std::string& desc, const LLSD& event,                            bool fromMap, const std::string& argskey) const = 0;          virtual LLSD addMetadata(LLSD) const = 0; + +        template <typename... ARGS> +        LLSD callFail(ARGS&&... args) const +        { +            mParent->callFail<LLEventDispatcher::DispatchError>(std::forward<ARGS>(args)...); +            // pacify the compiler +            return {}; +        }      };      typedef std::map<std::string, std::unique_ptr<DispatchEntry> > DispatchMap; @@ -561,9 +574,48 @@ protected:      static      void sCallFail(ARGS&&... args); +    // Manage transient state, e.g. which registered callable we're attempting +    // to call, for error reporting +    class SetState +    { +    public: +        template <typename... ARGS> +        SetState(const LLEventDispatcher* self, ARGS&&... args): +            mSelf(self) +        { +            mSet = mSelf->setState(*this, stringize(std::forward<ARGS>(args)...)); +        } +        // RAII class: forbid both copy and move +        SetState(const SetState&) = delete; +        SetState(SetState&&) = delete; +        SetState& operator=(const SetState&) = delete; +        SetState& operator=(SetState&&) = delete; +        virtual ~SetState() +        { +            // if we're the ones who succeeded in setting state, clear it +            if (mSet) +            { +                mSelf->setState(*this, {}); +            } +        } + +    private: +        const LLEventDispatcher* mSelf; +        bool mSet; +    }; +  private:      std::string mDesc, mKey, mArgskey;      DispatchMap mDispatch; +    // transient state: must be fiber_specific since multiple threads and/or +    // multiple fibers may be calling concurrently. Make it mutable so we can +    // use SetState even within const methods. +    mutable boost::fibers::fiber_specific_ptr<std::string> mState; + +    std::string getState() const; +    // setState() requires SetState& because only the SetState class should +    // call it. Make it const so we can use SetState even within const methods. +    bool setState(SetState&, const std::string& state) const;      static NameDesc makeNameDesc(const DispatchMap::value_type& item)      { | 
