summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-05-02 23:57:29 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-05-02 23:57:29 -0400
commit48e1979abaecc03af96e7e752e65c645083a4268 (patch)
tree3d6f361da0d6e03000844e73c37cf3834d44b19b /indra/llcommon
parentc231c97eeefc484b74198ba86251054b7dc0e6bb (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')
-rw-r--r--indra/llcommon/llcallbacklist.cpp64
-rw-r--r--indra/llcommon/llcallbacklist.h23
-rw-r--r--indra/llcommon/lleventtimer.cpp10
-rw-r--r--indra/llcommon/lleventtimer.h2
4 files changed, 80 insertions, 19 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
diff --git a/indra/llcommon/llcallbacklist.h b/indra/llcommon/llcallbacklist.h
index 522a9b838b..17adb7f431 100644
--- a/indra/llcommon/llcallbacklist.h
+++ b/indra/llcommon/llcallbacklist.h
@@ -114,13 +114,13 @@ class LLLater: public LLSingleton<LLLater>
struct func_at
{
nullary_func_t mFunc;
- LLDate::timestamp mTime;
token_t mToken;
+ LLDate::timestamp mTime;
- func_at(const nullary_func_t& func, LLDate::timestamp tm, token_t token):
+ func_at(const nullary_func_t& func, token_t token, LLDate::timestamp tm):
mFunc(func),
- mTime(tm),
- mToken(token)
+ mToken(token),
+ mTime(tm)
{}
friend bool operator<(const func_at& lhs, const func_at& rhs)
@@ -165,7 +165,9 @@ public:
handle_t doPeriodically(bool_func_t callable, F32 seconds);
// test whether specified handle is still live
- bool isRunning(handle_t timer);
+ bool isRunning(handle_t timer) const;
+ // check remaining time
+ F32 getRemaining(handle_t timer) const;
// Cancel a future timer set by doAtTime(), doAfterInterval(), doPeriodically().
// Return true iff the handle corresponds to a live timer.
@@ -239,10 +241,19 @@ private:
// the heap aka priority queue
queue_t mQueue;
// handles we've returned that haven't yet canceled
- std::unordered_map<token_t, queue_t::handle_type> mHandles;
+ using HandleMap = std::unordered_map<token_t, queue_t::handle_type>;
+ HandleMap mHandles;
+ using DoneMap = std::unordered_map<token_t, LLDate::timestamp>;
+ DoneMap mDoneTimes;
token_t mToken{ 0 };
// While mQueue is non-empty, register for regular callbacks.
LLCallbackList::temp_handle_t mLive;
+
+ struct Periodic;
+
+ // internal implementation for doAtTime()
+ DoneMap::iterator doAtTime1(LLDate::timestamp time);
+ handle_t doAtTime2(nullary_func_t callable, DoneMap::iterator iter);
};
/*-------------------- legacy names in global namespace --------------------*/
diff --git a/indra/llcommon/lleventtimer.cpp b/indra/llcommon/lleventtimer.cpp
index b163ad375c..0f8d1e636f 100644
--- a/indra/llcommon/lleventtimer.cpp
+++ b/indra/llcommon/lleventtimer.cpp
@@ -56,3 +56,13 @@ void LLEventTimer::stop()
{
LLLater::instance().cancel(mTimer);
}
+
+bool LLEventTimer::isRunning()
+{
+ return LLLater::instance().isRunning(mTimer);
+}
+
+F32 LLEventTimer::getRemaining()
+{
+ return LLLater::instance().getRemaining(mTimer);
+}
diff --git a/indra/llcommon/lleventtimer.h b/indra/llcommon/lleventtimer.h
index 34ff157e22..05d8bc038d 100644
--- a/indra/llcommon/lleventtimer.h
+++ b/indra/llcommon/lleventtimer.h
@@ -42,6 +42,8 @@ public:
void start();
void stop();
+ bool isRunning();
+ F32 getRemaining();
//function to be called at the supplied frequency
// Normally return false; true will delete the timer after the function returns.