diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-05-02 23:57:29 -0400 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-05-02 23:57:29 -0400 |
commit | 48e1979abaecc03af96e7e752e65c645083a4268 (patch) | |
tree | 3d6f361da0d6e03000844e73c37cf3834d44b19b /indra/llcommon/llcallbacklist.cpp | |
parent | c231c97eeefc484b74198ba86251054b7dc0e6bb (diff) |
Introduce LLLater::getRemaining(handle).
Some timer use cases need to know not only whether the timer is active, but
how much time remains before it (next) fires.
Introduce LLLater::mDoneTimes to track, for each handle, the timestamp at
which it's expected to fire. We can't just look up the target timestamp in
mQueue's func_at entry because there's no documented way to navigate from a
handle_type to a node iterator or pointer. Nor can we store it in mHandles
because of order dependency: we need the mDoneTimes iterator so we can bind it
into the Periodic functor for doPeriodically(), but we need the mQueue handle
to store in mHandles. If we could find the mQueue node from the new handle, we
could update the func_at entry after emplace() -- but if we could find the
mQueue node from a handle, we wouldn't need to store the target timestamp
separately anyway.
Split LLLater::doAtTime() into internal doAtTime1() and doAtTime2(): the first
creates an mDoneTimes entry and returns an iterator, the second finishes
creating new mQueue and mHandles entries based on that mDoneTimes entry.
This lets doPeriodically()'s Periodic bind the mDoneTimes iterator. Then
instead of continually incrementing an internal data member, it increments the
mDoneTimes entry to set the next upcoming timestamp. That lets getRemaining()
report the next upcoming timestamp rather than only the original one.
Add LLEventTimer::isRunning() and getRemaining(), forwarding to its LLLater
handle.
Fix various LLEventTimer subclass references to mEventTimer.stop(), etc.
Fix non-inline LLEventTimer subclass tick() overrides for bool, not BOOL.
Remove LLAppViewer::idle() call to LLEventTimer::updateClass(). Since
LLApp::stepFrame() already calls LLCallbackList::callFunctions(), assume we've
already handled that every tick.
Diffstat (limited to 'indra/llcommon/llcallbacklist.cpp')
-rw-r--r-- | indra/llcommon/llcallbacklist.cpp | 64 |
1 files changed, 51 insertions, 13 deletions
diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp index 7f7fdc7370..85c73fec8f 100644 --- a/indra/llcommon/llcallbacklist.cpp +++ b/indra/llcommon/llcallbacklist.cpp @@ -130,22 +130,40 @@ LLCallbackList::handle_t LLCallbackList::doOnIdleRepeating( const bool_func_t& f *****************************************************************************/ LLLater::LLLater() {} -// Call a given callable once at specified timestamp. -LLLater::handle_t LLLater::doAtTime(nullary_func_t callable, LLDate::timestamp time) +LLLater::DoneMap::iterator LLLater::doAtTime1(LLDate::timestamp time) { - bool first{ mQueue.empty() }; // Pick token FIRST to store a self-reference in mQueue's managed node as // well as in mHandles. Pre-increment to distinguish 0 from any live // handle_t. token_t token{ ++mToken }; - auto handle{ mQueue.emplace(callable, time, token) }; - mHandles.emplace(token, handle); + auto [iter, inserted]{ mDoneTimes.emplace(token, time) }; + llassert(inserted); + return iter; +} + +LLLater::handle_t LLLater::doAtTime2(nullary_func_t callable, DoneMap::iterator iter) +{ + bool first{ mQueue.empty() }; + // DoneMap::iterator references (token, time) pair + auto handle{ mQueue.emplace(callable, iter->first, iter->second) }; + auto hiter{ mHandles.emplace(iter->first, handle).first }; + // When called by Periodic, we're passed an existing DoneMap entry, + // meaning the token already exists, meaning emplace() will tell us it + // found an existing entry rather than creating a new one. In that case, + // it's essential to update the handle. + hiter->second = handle; if (first) { // If this is our first entry, register for regular callbacks. mLive = LLCallbackList::instance().doOnIdleRepeating([this]{ return tick(); }); } - return handle_t{ token }; + return { iter->first }; +} + +// Call a given callable once at specified timestamp. +LLLater::handle_t LLLater::doAtTime(nullary_func_t callable, LLDate::timestamp time) +{ + return doAtTime2(callable, doAtTime1(time)); } // Call a given callable once after specified interval. @@ -160,11 +178,11 @@ LLLater::handle_t LLLater::doAfterInterval(nullary_func_t callable, F32 seconds) // For doPeriodically(), we need a struct rather than a lambda because a // struct, unlike a lambda, has access to 'this'. -struct Periodic +struct LLLater::Periodic { LLLater* mLater; + DoneMap::iterator mDone; bool_func_t mCallable; - LLDate::timestamp mNext; F32 mSeconds; void operator()() @@ -175,8 +193,9 @@ struct Periodic // Don't call doAfterInterval(), which rereads LLDate::now(), // since that would defer by however long it took us to wake // up and notice plus however long callable() took to run. - mNext += mSeconds; - mLater->doAtTime(*this, mNext); + // Bump our mDoneTimes entry so getRemaining() can track. + mDone->second += mSeconds; + mLater->doAtTime2(*this, mDone); } } }; @@ -186,17 +205,32 @@ LLLater::handle_t LLLater::doPeriodically(bool_func_t callable, F32 seconds) { // Passing seconds <= 0 will produce an infinite loop. llassert(seconds > 0); - auto next{ LLDate::now().secondsSinceEpoch() + seconds }; - return doAtTime(Periodic{ this, callable, next, seconds }, next); + auto iter{ doAtTime1(LLDate::now().secondsSinceEpoch() + seconds) }; + // The whole reason we split doAtTime() into doAtTime1() and doAtTime2() + // is to be able to bind the mDoneTimes entry into Periodic. + return doAtTime2(Periodic{ this, iter, callable, seconds }, iter); } -bool LLLater::isRunning(handle_t timer) +bool LLLater::isRunning(handle_t timer) const { // A default-constructed timer isn't running. // A timer we don't find in mHandles has fired or been canceled. return timer && mHandles.find(timer.token) != mHandles.end(); } +F32 LLLater::getRemaining(handle_t timer) const +{ + auto found{ mDoneTimes.find(timer.token) }; + if (found == mDoneTimes.end()) + { + return 0.f; + } + else + { + return found->second - LLDate::now().secondsSinceEpoch(); + } +} + // Cancel a future timer set by doAtTime(), doAfterInterval(), doPeriodically() bool LLLater::cancel(handle_t& timer) { @@ -240,6 +274,8 @@ bool LLLater::cancel(const handle_t& timer) mQueue.erase(found->second); // before erasing timer.token from mHandles mHandles.erase(found); + // don't forget to erase mDoneTimes entry + mDoneTimes.erase(timer.token); if (mQueue.empty()) { // If that was the last active timer, unregister for callbacks. @@ -282,6 +318,8 @@ bool LLLater::tick() auto current{ top }; // remove the mHandles entry referencing this task mHandles.erase(current.mToken); + // and the mDoneTimes entry + mDoneTimes.erase(current.mToken); // before removing the mQueue task entry itself mQueue.pop(); // okay, NOW run |