Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
libcore|Timer: Avoid repeating Timer triggering
A timer will not post an event until after the previously posted
event has been handled.
  • Loading branch information
skyjake committed Sep 1, 2019
1 parent 7a2f391 commit a4fe2b2
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 16 deletions.
5 changes: 3 additions & 2 deletions doomsday/libs/core/include/de/core/timer.h
Expand Up @@ -44,11 +44,12 @@ class DE_PUBLIC Timer
start();
}

bool isActive() const;
bool isSingleShot() const;
bool isActive() const;
bool isSingleShot() const;
TimeSpan interval() const;

void trigger();
void post();

/**
* Adds a new trigger callback. This is the same as adding the callback to the Trigger
Expand Down
40 changes: 26 additions & 14 deletions doomsday/libs/core/src/core/timer.cpp
Expand Up @@ -31,7 +31,9 @@ namespace internal {

using TimePoint = sc::system_clock::time_point;

/// Thread that posts timer events when it is time to trigger scheduled timers.
/**
* Thread that posts timer events when it is time to trigger scheduled timers.
*/
struct TimerScheduler : public Thread, public Lockable
{
struct Pending {
Expand Down Expand Up @@ -70,25 +72,14 @@ struct TimerScheduler : public Thread, public Lockable

// We'll have the event loop notify the timer's audience. This way the
// timer scheduling thread won't be blocked by slow trigger handlers.
if (auto *loop = EventLoop::get())
{
Timer *timer = pt.timer;
loop->postEvent(
new CoreEvent(Event::Timer, [timer]() { timer->trigger(); }));
}
else
{
warning("[TimerScheduler] Pending timer %p triggered with no "
"event loop running (event not posted)",
pt.timer);
}
pt.timer->post();

// Schedule the next trigger.
if (pt.repeatDuration > 0.0)
{
TimePoint nextAt = pt.nextAt + sc::microseconds(dint64(pt.repeatDuration * 1.0e6));
#if defined (DE_DEBUG)
// Debugger may halt the process, don't bother spamming with timer events.
// Debugger may halt the process, don't bother retrying to post.
nextAt = de::max(nextAt, sc::system_clock::now());
#endif
pending.push(Pending{nextAt, pt.timer, pt.repeatDuration});
Expand Down Expand Up @@ -153,6 +144,7 @@ DE_PIMPL_NOREF(Timer)
TimeSpan interval = 1.0;
bool isSingleShot = false;
bool isActive = false;
bool isPending = false; // posted but not executed yet

~Impl()
{
Expand Down Expand Up @@ -226,6 +218,26 @@ void Timer::trigger()
}
}

void Timer::post()
{
if (!d->isPending)
{
if (auto *loop = EventLoop::get())
{
d->isPending = true; // not reposted before triggered
loop->postEvent(new CoreEvent(Event::Timer, [this]() {
d->isPending = false;
trigger();
}));
}
else
{
warning("[TimerScheduler] Pending timer %p triggered with no event loop running (event "
"not posted)", this);
}
}
}

Timer &Timer::operator+=(const std::function<void ()> &callback)
{
audienceForTrigger() += callback;
Expand Down

0 comments on commit a4fe2b2

Please sign in to comment.