From d4f384b4ec55758a7f2ca4338894ea6cacc98eec Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 7 May 2024 16:13:03 -0400 Subject: Refactor LLLater -> LL::Timers to accommodate nonpositive repeats. In the previous design, the tick() method ran each task exactly once. doPeriodically() was implemented by posting a functor that would, after calling the specified callable, repost itself at (timestamp + interval). The trouble with that design is that it required (interval > 0). A nonpositive interval would result in looping over any timers with nonpositive repetition intervals without ever returning from the tick() method. To avoid that, doPeriodically() contained an llassert(interval > 0). Unfortunately the viewer failed that constraint immediately at login, leading to the suspicion that eliminating every such usage might require a protracted search. Lifting that restriction required a redesign. Now the priority queue stores a callable returning bool, and the tick() method itself contains the logic to repost a recurring task -- but defers doing so until after it stops looping over ready tasks, ensuring that a task with a nonpositive interval will at least wait until the following tick() call. This simplifies not only doPeriodically(), but also doAtTime(). The previous split of doAtTime() into doAtTime1() and doAtTime2() was only to accommodate the needs of the Periodic functor class. Ditch Periodic. Per feedback from NickyD, rename doAtTime() to scheduleAt(), which wraps its passed nullary callable into a callable that unconditionally returns true (so tick() will run it only once). Rename the doAfterInterval() method to scheduleAfter(), which similarly wraps its nullary callable. However, the legacy doAfterInterval() free function remains. scheduleAfter() also loses its llassert(seconds > 0). Rename the doPeriodically() method to scheduleRepeating(). However, the legacy doPeriodically() free function remains. Add internal scheduleAtRepeating(), whose role is to accept both a specific timestamp and a repetition interval (which might be ignored, depending on the callable). scheduleAtRepeating() now contains the real logic to add a task. Rename getRemaining() to timeUntilCall(), hopefully resolving the question of "remaining what?" Expand the std::pair metadata stored in Timers's auxiliary unordered_map to a Metadata struct containing the repetition interval plus two bools to mediate deferred cancel() processing. Rename HandleMap to MetaMap, mHandles to mMeta. Defend against the case when cancel(handle) is reached during the call to that handle's callable. Meta::mRunning is set for the duration of that call. When cancel() sees mRunning, instead of immediately deleting map entries, it sets mCancel. Upon return from a task's callable, tick() notices mCancel and behaves as if the callable returned true to stop the series of calls. To guarantee that mRunning doesn't inadvertently remain set even in the case of an exception, introduce local RAII class TempSet whose constructor accepts a non-const variable reference and a desired value. The constructor captures the current value and sets the desired value; the destructor restores the previous value. Defend against exception in a task's callable, and stop calling that task. Use LOG_UNHANDLED_EXCEPTION() to report it. --- indra/llcommon/lleventfilter.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'indra/llcommon/lleventfilter.cpp') diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp index e72ae7ad33..ad61e9298a 100644 --- a/indra/llcommon/lleventfilter.cpp +++ b/indra/llcommon/lleventfilter.cpp @@ -87,7 +87,7 @@ LLEventTimeout::LLEventTimeout(LLEventPump& source): void LLEventTimeout::actionAfter(F32 seconds, const Action& action) { - mTimer = LLLater::instance().doAfterInterval(action, seconds); + mTimer = LL::Timers::instance().scheduleAfter(action, seconds); } void LLEventTimeout::errorAfter(F32 seconds, const std::string& message) @@ -118,7 +118,7 @@ void LLEventTimeout::cancel() bool LLEventTimeout::running() const { - return LLLater::instance().isRunning(mTimer); + return LL::Timers::instance().isRunning(mTimer); } /***************************************************************************** @@ -277,17 +277,17 @@ F32 LLEventThrottle::getDelay() const void LLEventThrottle::alarmActionAfter(F32 interval, const LLEventTimeout::Action& action) { - mAlarm = LLLater::instance().doAfterInterval(action, interval); + mAlarm = LL::Timers::instance().scheduleAfter(action, interval); } bool LLEventThrottle::alarmRunning() const { - return LLLater::instance().isRunning(mAlarm); + return LL::Timers::instance().isRunning(mAlarm); } void LLEventThrottle::alarmCancel() { - LLLater::instance().cancel(mAlarm); + LL::Timers::instance().cancel(mAlarm); } void LLEventThrottle::timerSet(F32 interval) -- cgit v1.2.3