Skip to content

Commit

Permalink
fix: timer limited repeat (#659)
Browse files Browse the repository at this point in the history
* Aligned TimerLimitedRepeating with TimerRepeating

* Align TimerLimitedRepeatingWithClosingAction

* chore: solved msvc compilation. Unable to capture variable being assigned to

* chore: renamed namespace details -> detail

---------

Co-authored-by: Daan Timmer <[email protected]>
Co-authored-by: Timmer, Daan <[email protected]>
  • Loading branch information
3 people authored Sep 4, 2024
1 parent 7573a82 commit 4f2a49e
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 91 deletions.
43 changes: 24 additions & 19 deletions infra/timer/Timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,45 +142,50 @@ namespace infra
Cancel();
}

TimerRepeating::TimerRepeating(uint32_t timerServiceId)
detail::TimerRepeating::TimerRepeating(uint32_t timerServiceId)
: Timer(timerServiceId)
{}

TimerRepeating::TimerRepeating(Duration duration, const infra::Function<void()>& aAction, uint32_t timerServiceId)
: Timer(timerServiceId)
Duration detail::TimerRepeating::TriggerPeriod() const
{
Start(duration, aAction);
return triggerPeriod;
}

TimerRepeating::TimerRepeating(Duration duration, const infra::Function<void()>& aAction, TriggerImmediately, uint32_t timerServiceId)
: Timer(timerServiceId)
void detail::TimerRepeating::StartTimer(Duration duration, const infra::Function<void()>& action)
{
Start(duration, aAction, triggerImmediately);
triggerPeriod = duration;
SetNextTriggerTime(Now() + TriggerPeriod() + Resolution(), action);
}

void TimerRepeating::Start(Duration duration, const infra::Function<void()>& action)
void detail::TimerRepeating::ComputeNextTriggerTime()
{
triggerPeriod = duration;
SetNextTriggerTime(Now() + TriggerPeriod() + Resolution(), action);
TimePoint now = std::max(Now(), NextTrigger());
Duration diff = (now - NextTrigger()) % triggerPeriod;

SetNextTriggerTime(now - diff + triggerPeriod, Action());
}

void TimerRepeating::Start(Duration duration, const infra::Function<void()>& action, TriggerImmediately)
TimerRepeating::TimerRepeating(Duration duration, const infra::Function<void()>& aAction, uint32_t timerServiceId)
: detail::TimerRepeating(timerServiceId)
{
Start(duration, action);
action();
Start(duration, aAction);
}

Duration TimerRepeating::TriggerPeriod() const
TimerRepeating::TimerRepeating(Duration duration, const infra::Function<void()>& aAction, TriggerImmediately, uint32_t timerServiceId)
: detail::TimerRepeating(timerServiceId)
{
return triggerPeriod;
Start(duration, aAction, triggerImmediately);
}

void TimerRepeating::ComputeNextTriggerTime()
void TimerRepeating::Start(Duration duration, const infra::Function<void()>& action)
{
TimePoint now = std::max(Now(), NextTrigger());
Duration diff = (now - NextTrigger()) % triggerPeriod;
StartTimer(duration, action);
}

SetNextTriggerTime(now - diff + triggerPeriod, Action());
void TimerRepeating::Start(Duration duration, const infra::Function<void()>& action, TriggerImmediately)
{
StartTimer(duration, action);
action();
}
}

Expand Down
30 changes: 20 additions & 10 deletions infra/timer/Timer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,34 @@ namespace infra
void ComputeNextTriggerTime() override;
};

namespace detail
{
class TimerRepeating
: public Timer
{
public:
explicit TimerRepeating(uint32_t timerServiceId = systemTimerServiceId);
Duration TriggerPeriod() const;

protected:
void StartTimer(Duration duration, const infra::Function<void()>& action);
void ComputeNextTriggerTime() override;

private:
Duration triggerPeriod = Duration();
};
}

class TimerRepeating
: public Timer
: public detail::TimerRepeating
{
public:
explicit TimerRepeating(uint32_t timerServiceId = systemTimerServiceId);
using detail::TimerRepeating::TimerRepeating;
TimerRepeating(Duration duration, const infra::Function<void()>& action, uint32_t timerServiceId = systemTimerServiceId);
TimerRepeating(Duration duration, const infra::Function<void()>& action, TriggerImmediately, uint32_t timerServiceId = systemTimerServiceId);

void Start(Duration duration, const infra::Function<void()>& action);
void Start(Duration duration, const infra::Function<void()>& action, TriggerImmediately);

Duration TriggerPeriod() const;

protected:
void ComputeNextTriggerTime() override;

private:
Duration triggerPeriod = Duration();
};
}

Expand Down
31 changes: 8 additions & 23 deletions infra/timer/TimerLimitedRepeating.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,26 @@

namespace infra
{
TimerLimitedRepeating::TimerLimitedRepeating(uint32_t timerServiceId)
: Timer(timerServiceId)
{}

TimerLimitedRepeating::TimerLimitedRepeating(int aHowMany, Duration duration, const infra::Function<void()>& aAction, uint32_t timerServiceId)
: Timer(timerServiceId)
: detail::TimerRepeating(timerServiceId)
{
Start(aHowMany, duration, aAction);
}

void TimerLimitedRepeating::Start(int aHowMany, Duration duration, const infra::Function<void()>& aAction)
{
action = aAction;

triggerStart = Now() + Resolution();
triggerPeriod = duration;
howMany = aHowMany;

ComputeNextTriggerTime();
if (howMany > 0)
StartTimer(duration, aAction);
else
Cancel();
}

void TimerLimitedRepeating::ComputeNextTriggerTime()
{
if (howMany != 0)
{
--howMany;

TimePoint now = Now();

Duration diff = now - triggerStart;
if (diff < Duration())
now += triggerPeriod;
diff %= triggerPeriod;
SetNextTriggerTime(now - diff + triggerPeriod, action);
}
--howMany;
if (howMany > 0)
detail::TimerRepeating::ComputeNextTriggerTime();
else
Cancel();
}
Expand Down
7 changes: 2 additions & 5 deletions infra/timer/TimerLimitedRepeating.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
namespace infra
{
class TimerLimitedRepeating
: public Timer
: public detail::TimerRepeating
{
public:
explicit TimerLimitedRepeating(uint32_t timerServiceId = systemTimerServiceId);
using detail::TimerRepeating::TimerRepeating;
TimerLimitedRepeating(int aHowMany, Duration duration, const infra::Function<void()>& action, uint32_t timerServiceId = systemTimerServiceId);

void Start(int aHowMany, Duration duration, const infra::Function<void()>& action);
Expand All @@ -18,10 +18,7 @@ namespace infra
void ComputeNextTriggerTime() override;

private:
TimePoint triggerStart;
Duration triggerPeriod;
int howMany{ 0 };
infra::Function<void()> action;
};
}

Expand Down
41 changes: 14 additions & 27 deletions infra/timer/TimerLimitedRepeatingWithClosingAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,37 @@

namespace infra
{
TimerLimitedRepeatingWithClosingAction::TimerLimitedRepeatingWithClosingAction(uint32_t timerServiceId)
: Timer(timerServiceId)
{}

TimerLimitedRepeatingWithClosingAction::TimerLimitedRepeatingWithClosingAction(int aHowMany, Duration duration, const infra::Function<void()>& aAction, const infra::Function<void()>& aClosingAction, uint32_t timerServiceId)
: Timer(timerServiceId)
: detail::TimerRepeating(timerServiceId)
{
Start(aHowMany, duration, aAction, aClosingAction);
}

void TimerLimitedRepeatingWithClosingAction::Start(int aHowMany, Duration duration, const infra::Function<void()>& aAction, const infra::Function<void()>& aClosingAction)
{
closingAction = aClosingAction;
action = aAction;

triggerStart = Now() + Resolution();
triggerPeriod = duration;
howMany = aHowMany;

ComputeNextTriggerTime();
closingAction = aClosingAction;
if (howMany > 0)
{
StartTimer(duration, aAction);
}
else
{
Cancel();
closingAction();
}
}

const infra::Function<void()>& TimerLimitedRepeatingWithClosingAction::Action() const
{
if (howMany >= 0)
return action;
else
return closingAction;
return howMany > 0 ? Timer::Action() : closingAction;
}

void TimerLimitedRepeatingWithClosingAction::ComputeNextTriggerTime()
{
--howMany;
if (howMany >= 0)
{
--howMany;

TimePoint now = Now();

Duration diff = now - triggerStart;
if (diff < Duration())
now += triggerPeriod;
diff %= triggerPeriod;
SetNextTriggerTime(now - diff + triggerPeriod, infra::emptyFunction);
}
detail::TimerRepeating::ComputeNextTriggerTime();
else
Cancel();
}
Expand Down
11 changes: 4 additions & 7 deletions infra/timer/TimerLimitedRepeatingWithClosingAction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,26 @@

#include "infra/timer/Timer.hpp"

// Trigers a number of times, then triggers another action
// Triggers a number of times, then triggers another action
// Useful in a protocol where after a number of retries failure has to be indicated

namespace infra
{
class TimerLimitedRepeatingWithClosingAction
: public Timer
: public detail::TimerRepeating
{
public:
explicit TimerLimitedRepeatingWithClosingAction(uint32_t timerServiceId = systemTimerServiceId);
using detail::TimerRepeating::TimerRepeating;
TimerLimitedRepeatingWithClosingAction(int aHowMany, Duration duration, const infra::Function<void()>& action, const infra::Function<void()>& aClosingAction, uint32_t timerServiceId = systemTimerServiceId);

void Start(int aHowMany, Duration duration, const infra::Function<void()>& action, const infra::Function<void()>& aClosingAction);
const infra::Function<void()>& Action() const override;

protected:
const infra::Function<void()>& Action() const override;
void ComputeNextTriggerTime() override;

private:
TimePoint triggerStart;
Duration triggerPeriod;
int howMany{ 0 };
infra::Function<void()> action;
infra::Function<void()> closingAction;
};
}
Expand Down
33 changes: 33 additions & 0 deletions infra/timer/test/TestTimerLimitedRepeating.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,36 @@ TEST_F(TimerLimitedRepeatingTest, ResolutionIsTakenIntoAccount)

ForwardTime(std::chrono::seconds(5));
}

TEST_F(TimerLimitedRepeatingTest, CancelAfterFirstCallback)
{
infra::MockCallback<void()> callback;
EXPECT_CALL(callback, callback()).With(After(std::chrono::seconds(1)));

infra::TimerLimitedRepeating timer;
timer.Start(2, std::chrono::seconds(1), [&callback, &timer]()
{
callback.callback();
timer.Cancel();
});

ForwardTime(std::chrono::seconds(5));
}

TEST_F(TimerLimitedRepeatingTest, Restart0AfterFirstCallback)
{
infra::MockCallback<void()> callback;
EXPECT_CALL(callback, callback()).With(After(std::chrono::seconds(1)));

infra::TimerLimitedRepeating timer;
timer.Start(2, std::chrono::seconds(1), [&callback, &timer]()
{
callback.callback();
timer.Start(0, std::chrono::seconds(1), [&callback]()
{
callback.callback();
});
});

ForwardTime(std::chrono::seconds(5));
}
48 changes: 48 additions & 0 deletions infra/timer/test/TestTimerLimitedRepeatingWithClosingAction.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "infra/timer/TimerLimitedRepeatingWithClosingAction.hpp"
#include "infra/timer/test_helper/ClockFixture.hpp"
#include "infra/util/test_helper/MockCallback.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

class TimerLimitedRepeatingWithClosingActionTest
Expand Down Expand Up @@ -67,3 +68,50 @@ TEST_F(TimerLimitedRepeatingWithClosingActionTest, ResolutionIsTakenIntoAccount)

ForwardTime(std::chrono::seconds(5));
}

TEST_F(TimerLimitedRepeatingWithClosingActionTest, CancelAfterFirstCallback)
{
infra::MockCallback<void()> callback;
infra::MockCallback<void()> extra;
EXPECT_CALL(callback, callback()).With(After(std::chrono::seconds(1)));

infra::TimerLimitedRepeatingWithClosingAction timer;
timer.Start(
2, std::chrono::seconds(1), [&callback, &timer]()
{
callback.callback();
timer.Cancel();
},
[&extra]()
{
extra.callback();
});

ForwardTime(std::chrono::seconds(5));
}

TEST_F(TimerLimitedRepeatingWithClosingActionTest, Restart0AfterFirstCallback)
{
infra::MockCallback<void()> callback;
infra::MockCallback<void()> extra;
EXPECT_CALL(callback, callback()).With(After(std::chrono::seconds(1)));

infra::TimerLimitedRepeatingWithClosingAction timer;
timer.Start(
2, std::chrono::seconds(1), [&callback, &timer]()
{
callback.callback();
timer.Start(
0, std::chrono::seconds(1), [&callback]()
{
callback.callback();
},
[]() {});
},
[&extra]()
{
extra.callback();
});

ForwardTime(std::chrono::seconds(5));
}

0 comments on commit 4f2a49e

Please sign in to comment.