summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
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.