diff options
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 15 | ||||
| -rw-r--r-- | indra/llcommon/lleventfilter.cpp | 96 | ||||
| -rw-r--r-- | indra/llcommon/lleventfilter.h | 49 | ||||
| -rw-r--r-- | indra/llcommon/tests/listener.h | 11 | ||||
| -rw-r--r-- | indra/llcommon/tests/lleventfilter_test.cpp | 124 | 
5 files changed, 262 insertions, 33 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 3493f80556..aa76a57f1d 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -324,26 +324,27 @@ if (LL_TESTS)    LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(lleventfilter "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}") -  LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}") -  LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}") -  LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}") -  LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}") -  LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}") -  LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}") -  LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")  ## llexception_test.cpp isn't a regression test, and doesn't need to be run  ## every build. It's to help a developer make implementation choices about diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp index 87eb583cb6..ddd7d5547a 100644 --- a/indra/llcommon/lleventfilter.cpp +++ b/indra/llcommon/lleventfilter.cpp @@ -38,12 +38,18 @@  #include "llerror.h"                // LL_ERRS  #include "llsdutil.h"               // llsd_matches() +/***************************************************************************** +*   LLEventFilter +*****************************************************************************/  LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):      LLEventStream(name, tweak),      mSource(source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1)))  {  } +/***************************************************************************** +*   LLEventMatching +*****************************************************************************/  LLEventMatching::LLEventMatching(const LLSD& pattern):      LLEventFilter("matching"),      mPattern(pattern) @@ -64,6 +70,9 @@ bool LLEventMatching::post(const LLSD& event)      return LLEventStream::post(event);  } +/***************************************************************************** +*   LLEventTimeoutBase +*****************************************************************************/  LLEventTimeoutBase::LLEventTimeoutBase():      LLEventFilter("timeout")  { @@ -153,6 +162,9 @@ bool LLEventTimeoutBase::running() const      return mMainloop.connected();  } +/***************************************************************************** +*   LLEventTimeout +*****************************************************************************/  LLEventTimeout::LLEventTimeout() {}  LLEventTimeout::LLEventTimeout(LLEventPump& source): @@ -170,6 +182,9 @@ bool LLEventTimeout::countdownElapsed() const      return mTimer.hasExpired();  } +/***************************************************************************** +*   LLEventBatch +*****************************************************************************/  LLEventBatch::LLEventBatch(std::size_t size):      LLEventFilter("batch"),      mBatchSize(size) @@ -208,19 +223,22 @@ void LLEventBatch::setSize(std::size_t size)      }  } -LLEventThrottle::LLEventThrottle(F32 interval): +/***************************************************************************** +*   LLEventThrottleBase +*****************************************************************************/ +LLEventThrottleBase::LLEventThrottleBase(F32 interval):      LLEventFilter("throttle"),      mInterval(interval),      mPosts(0)  {} -LLEventThrottle::LLEventThrottle(LLEventPump& source, F32 interval): +LLEventThrottleBase::LLEventThrottleBase(LLEventPump& source, F32 interval):      LLEventFilter(source, "throttle"),      mInterval(interval),      mPosts(0)  {} -void LLEventThrottle::flush() +void LLEventThrottleBase::flush()  {      // flush() is a no-op unless there's something pending.      // Don't test mPending because there's no requirement that the consumer @@ -228,11 +246,11 @@ void LLEventThrottle::flush()      if (mPosts)      {          mPosts = 0; -        mAlarm.cancel(); +        alarmCancel();          // This is not to set our alarm; we are not yet requesting          // any notification. This is just to track whether subsequent post()          // calls fall within this mInterval or not. -        mTimer.setTimerExpirySec(mInterval); +        timerSet(mInterval);          // copy and clear mPending BEFORE posting to avoid weird circularity          // effects          LLSD pending = mPending; @@ -241,12 +259,12 @@ void LLEventThrottle::flush()      }  } -LLSD LLEventThrottle::pending() const +LLSD LLEventThrottleBase::pending() const  {      return mPending;  } -bool LLEventThrottle::post(const LLSD& event) +bool LLEventThrottleBase::post(const LLSD& event)  {      // Always capture most recent post() event data. If caller wants to      // aggregate multiple events, let them retrieve pending() and modify @@ -257,7 +275,7 @@ bool LLEventThrottle::post(const LLSD& event)      ++mPosts;      // We reset mTimer on every flush() call to let us know if we're still      // within the same mInterval. So -- are we? -    F32 timeRemaining = mTimer.getRemainingTimeF32(); +    F32 timeRemaining = timerGetRemaining();      if (! timeRemaining)      {          // more than enough time has elapsed, immediately flush() @@ -266,24 +284,24 @@ bool LLEventThrottle::post(const LLSD& event)      else      {          // still within mInterval of the last flush() call: have to defer -        if (! mAlarm.running()) +        if (! alarmRunning())          {              // timeRemaining tells us how much longer it will be until              // mInterval seconds since the last flush() call. At that time,              // flush() deferred events. -            mAlarm.actionAfter(timeRemaining, boost::bind(&LLEventThrottle::flush, this)); +            alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this));          }      }      return false;  } -void LLEventThrottle::setInterval(F32 interval) +void LLEventThrottleBase::setInterval(F32 interval)  {      F32 oldInterval = mInterval;      mInterval = interval;      // If we are not now within oldInterval of the last flush(), we're done:      // this will only affect behavior starting with the next flush(). -    F32 timeRemaining = mTimer.getRemainingTimeF32(); +    F32 timeRemaining = timerGetRemaining();      if (timeRemaining)      {          // We are currently within oldInterval of the last flush(). Figure out @@ -305,16 +323,60 @@ void LLEventThrottle::setInterval(F32 interval)          else          {              // immediately reset mTimer -            mTimer.setTimerExpirySec(timeRemaining); +            timerSet(timeRemaining);              // and if mAlarm is running, reset that too -            if (mAlarm.running()) +            if (alarmRunning())              { -                mAlarm.actionAfter(timeRemaining, boost::bind(&LLEventThrottle::flush, this)); +                alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this));              }          }      }  } +F32 LLEventThrottleBase::getDelay() const +{ +    return timerGetRemaining(); +} + +/***************************************************************************** +*   LLEventThrottle implementation +*****************************************************************************/ +LLEventThrottle::LLEventThrottle(F32 interval): +    LLEventThrottleBase(interval) +{} + +LLEventThrottle::LLEventThrottle(LLEventPump& source, F32 interval): +    LLEventThrottleBase(source, interval) +{} + +void LLEventThrottle::alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) +{ +    mAlarm.actionAfter(interval, action); +} + +bool LLEventThrottle::alarmRunning() const +{ +    return mAlarm.running(); +} + +void LLEventThrottle::alarmCancel() +{ +    return mAlarm.cancel(); +} + +void LLEventThrottle::timerSet(F32 interval) +{ +    mTimer.setTimerExpirySec(interval); +} + +F32  LLEventThrottle::timerGetRemaining() const +{ +    return mTimer.getRemainingTimeF32(); +} + +/***************************************************************************** +*   LLEventBatchThrottle +*****************************************************************************/  LLEventBatchThrottle::LLEventBatchThrottle(F32 interval):      LLEventThrottle(interval)  {} @@ -326,5 +388,7 @@ LLEventBatchThrottle::LLEventBatchThrottle(LLEventPump& source, F32 interval):  bool LLEventBatchThrottle::post(const LLSD& event)  {      // simply retrieve pending value and append the new event to it -    return LLEventThrottle::post(pending().append(event)); +    LLSD partial = pending(); +    partial.append(event); +    return LLEventThrottle::post(partial);  } diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h index 68890846a7..cae18bfd86 100644 --- a/indra/llcommon/lleventfilter.h +++ b/indra/llcommon/lleventfilter.h @@ -247,7 +247,7 @@ private:  };  /** - * LLEventThrottle: construct with a time interval. Regardless of how + * LLEventThrottleBase: construct with a time interval. Regardless of how   * frequently you call post(), LLEventThrottle will pass on an event to   * its listeners no more often than once per specified interval.   * @@ -268,21 +268,24 @@ private:   * 17: post(): immediately passed to listeners, next no sooner than 20   *   * For a deferred event, the LLSD blob delivered to listeners is from the most - * recent deferred post() call. However, you may obtain the previous event - * blob by calling pending(), modify it however you want and post() the new - * value. Each time an event is delivered to listeners, the pending() value is - * reset to isUndefined(). + * recent deferred post() call. However, a sender may obtain the previous + * event blob by calling pending(), modifying it as desired and post()ing the + * new value. Each time an event is delivered to listeners, the pending() + * value is reset to isUndefined().   *   * You may also call flush() to immediately pass along any deferred events to   * all listeners. + * + * @NOTE This is an abstract base class so that, for testing, we can use an + * alternate "timer" that doesn't actually consume real time.   */ -class LL_COMMON_API LLEventThrottle: public LLEventFilter +class LL_COMMON_API LLEventThrottleBase: public LLEventFilter  {  public:      // pass time interval -    LLEventThrottle(F32 interval); +    LLEventThrottleBase(F32 interval);      // construct and connect -    LLEventThrottle(LLEventPump& source, F32 interval); +    LLEventThrottleBase(LLEventPump& source, F32 interval);      // force out any deferred events      void flush(); @@ -301,7 +304,17 @@ public:      std::size_t getPostCount() const { return mPosts; }      // time until next event would be passed through, 0.0 if now -    F32 getDelay() const { return mTimer.getRemainingTimeF32(); } +    F32 getDelay() const; + +protected: +    // Implement these time-related methods for a valid LLEventThrottleBase +    // subclass (see LLEventThrottle). For testing, we use a subclass that +    // doesn't involve actual elapsed time. +    virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) = 0; +    virtual bool alarmRunning() const = 0; +    virtual void alarmCancel() = 0; +    virtual void timerSet(F32 interval) = 0; +    virtual F32  timerGetRemaining() const = 0;  private:      // remember throttle interval @@ -310,6 +323,24 @@ private:      std::size_t mPosts;      // pending event data from most recent deferred event      LLSD mPending; +}; + +/** + * Production implementation of LLEventThrottle. + */ +class LLEventThrottle: public LLEventThrottleBase +{ +public: +    LLEventThrottle(F32 interval); +    LLEventThrottle(LLEventPump& source, F32 interval); + +private: +    virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) override; +    virtual bool alarmRunning() const override; +    virtual void alarmCancel() override; +    virtual void timerSet(F32 interval) override; +    virtual F32  timerGetRemaining() const override; +      // use this to arrange a deferred flush() call      LLEventTimeout mAlarm;      // use this to track whether we're within mInterval of last flush() diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h index 9c5c18a150..6072060bb6 100644 --- a/indra/llcommon/tests/listener.h +++ b/indra/llcommon/tests/listener.h @@ -138,4 +138,15 @@ struct Collect      StringVec result;  }; +struct Concat +{ +    bool operator()(const LLSD& event) +    { +        result += event.asString(); +        return false; +    } +    void clear() { result.clear(); } +    std::string result; +}; +  #endif /* ! defined(LL_LISTENER_H) */ diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index 2cdfb52f2f..712864bf63 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -70,6 +70,85 @@ private:      bool mElapsed;  }; +// Similar remarks about LLEventThrottle: we're actually testing the logic in +// LLEventThrottleBase, dummying out the LLTimer and LLEventTimeout used by +// the production LLEventThrottle class. +class TestEventThrottle: public LLEventThrottleBase +{ +public: +    TestEventThrottle(F32 interval): +        LLEventThrottleBase(interval), +        mAlarmRemaining(-1), +        mTimerRemaining(-1) +    {} +    TestEventThrottle(LLEventPump& source, F32 interval): +        LLEventThrottleBase(source, interval), +        mAlarmRemaining(-1), +        mTimerRemaining(-1) +    {} + +    /*----- implementation of LLEventThrottleBase timing functionality -----*/ +    virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) override +    { +        mAlarmRemaining = interval; +        mAlarmAction = action; +    } + +    virtual bool alarmRunning() const override +    { +        // decrementing to exactly 0 should mean the alarm fires +        return mAlarmRemaining > 0; +    } + +    virtual void alarmCancel() override +    { +        mAlarmRemaining = -1; +    } + +    virtual void timerSet(F32 interval) override +    { +        mTimerRemaining = interval; +    } + +    virtual F32  timerGetRemaining() const override +    { +        // LLTimer.getRemainingTimeF32() never returns negative; 0.0 means expired +        return (mTimerRemaining > 0.0)? mTimerRemaining : 0.0; +    } + +    /*------------------- methods for manipulating time --------------------*/ +    void alarmAdvance(F32 delta) +    { +        bool wasRunning = alarmRunning(); +        mAlarmRemaining -= delta; +        if (wasRunning && ! alarmRunning()) +        { +            mAlarmAction(); +        } +    } + +    void timerAdvance(F32 delta) +    { +        // This simple implementation, like alarmAdvance(), completely ignores +        // HOW negative mTimerRemaining might go. All that matters is whether +        // it's negative. We trust that no test method in this source will +        // drive it beyond the capacity of an F32. Seems like a safe assumption. +        mTimerRemaining -= delta; +    } + +    void advance(F32 delta) +    { +        // Advance the timer first because it has no side effects. +        // alarmAdvance() might call flush(), which will need to see the +        // change in the timer. +        timerAdvance(delta); +        alarmAdvance(delta); +    } + +    F32 mAlarmRemaining, mTimerRemaining; +    LLEventTimeoutBase::Action mAlarmAction; +}; +  /*****************************************************************************  *   TUT  *****************************************************************************/ @@ -116,7 +195,9 @@ namespace tut              listener0.listenTo(driver));          // Construct a pattern LLSD: desired Event must have a key "foo"          // containing string "bar" -        LLEventMatching filter(driver, LLSD().insert("foo", "bar")); +        LLSD pattern; +        pattern.insert("foo", "bar"); +        LLEventMatching filter(driver, pattern);          listener1.reset(0);          LLTempBoundListener temp2(              listener1.listenTo(filter)); @@ -285,6 +366,47 @@ namespace tut          mainloop.post(17);          check_listener("no timeout 3", listener0, LLSD(0));      } + +    template<> template<> +    void filter_object::test<5>() +    { +        set_test_name("LLEventThrottle"); +        TestEventThrottle throttle(3); +        Concat cat; +        throttle.listen("concat", boost::ref(cat)); + +        // (sequence taken from LLEventThrottleBase Doxygen comments) +        //  1: post(): event immediately passed to listeners, next no sooner than 4 +        throttle.advance(1); +        throttle.post("1"); +        ensure_equals("1", cat.result, "1"); // delivered immediately +        //  2: post(): deferred: waiting for 3 seconds to elapse +        throttle.advance(1); +        throttle.post("2"); +        ensure_equals("2", cat.result, "1"); // "2" not yet delivered +        //  3: post(): deferred +        throttle.advance(1); +        throttle.post("3"); +        ensure_equals("3", cat.result, "1"); // "3" not yet delivered +        //  4: no post() call, but event delivered to listeners; next no sooner than 7 +        throttle.advance(1); +        ensure_equals("4", cat.result, "13"); // "3" delivered +        //  6: post(): deferred +        throttle.advance(2); +        throttle.post("6"); +        ensure_equals("6", cat.result, "13"); // "6" not yet delivered +        //  7: no post() call, but event delivered; next no sooner than 10 +        throttle.advance(1); +        ensure_equals("7", cat.result, "136"); // "6" delivered +        // 12: post(): immediately passed to listeners, next no sooner than 15 +        throttle.advance(5); +        throttle.post(";12"); +        ensure_equals("12", cat.result, "136;12"); // "12" delivered +        // 17: post(): immediately passed to listeners, next no sooner than 20 +        throttle.advance(5); +        throttle.post(";17"); +        ensure_equals("17", cat.result, "136;12;17"); // "17" delivered +    }  } // namespace tut  /***************************************************************************** | 
