diff --git a/include/envoy/event/BUILD b/include/envoy/event/BUILD index f4caa201ff14b..0d22a7747ae1a 100644 --- a/include/envoy/event/BUILD +++ b/include/envoy/event/BUILD @@ -41,11 +41,10 @@ envoy_cc_library( ) envoy_cc_library( - name = "range_timer_interface", - hdrs = ["range_timer.h"], + name = "scaled_range_timer_manager_interface", + hdrs = ["scaled_range_timer_manager.h"], deps = [ ":timer_interface", - "//include/envoy/common:time_interface", ], ) diff --git a/include/envoy/event/range_timer.h b/include/envoy/event/range_timer.h deleted file mode 100644 index a4e431047dcc1..0000000000000 --- a/include/envoy/event/range_timer.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "envoy/common/pure.h" -#include "envoy/common/time.h" -#include "envoy/event/timer.h" - -namespace Envoy { -namespace Event { - -/** - * An abstract event timer that can be scheduled for a timeout within a range. The actual timeout - * used is left up to individual implementations. - */ -class RangeTimer { -public: - virtual ~RangeTimer() = default; - - /** - * Disable a pending timeout without destroying the underlying timer. - */ - virtual void disableTimer() PURE; - - /** - * Enable a pending timeout within the given range. If a timeout is already pending, it will be - * reset to the new timeout. - * - * @param min_ms supplies the minimum duration of the alarm in milliseconds. - * @param max_ms supplies the maximum duration of the alarm in milliseconds. - * @param object supplies an optional scope for the duration of the alarm. - */ - virtual void enableTimer(std::chrono::milliseconds min_ms, std::chrono::milliseconds max_ms, - const ScopeTrackedObject* object = nullptr) PURE; - - /** - * Return whether the timer is currently armed. - */ - virtual bool enabled() PURE; -}; - -using RangeTimerPtr = std::unique_ptr; - -} // namespace Event -} // namespace Envoy \ No newline at end of file diff --git a/include/envoy/event/scaled_range_timer_manager.h b/include/envoy/event/scaled_range_timer_manager.h new file mode 100644 index 0000000000000..29f5e6b1ae0b1 --- /dev/null +++ b/include/envoy/event/scaled_range_timer_manager.h @@ -0,0 +1,91 @@ +#pragma once + +#include "envoy/common/pure.h" +#include "envoy/event/timer.h" + +#include "absl/types/variant.h" + +namespace Envoy { +namespace Event { + +/** + * Describes a minimum timer value that is equal to a scale factor applied to the maximum. + */ +struct ScaledMinimum { + explicit ScaledMinimum(double scale_factor) : scale_factor_(scale_factor) {} + const double scale_factor_; +}; + +/** + * Describes a minimum timer value that is an absolute duration. + */ +struct AbsoluteMinimum { + explicit AbsoluteMinimum(std::chrono::milliseconds value) : value_(value) {} + const std::chrono::milliseconds value_; +}; + +/** + * Class that describes how to compute a minimum timeout given a maximum timeout value. It wraps + * ScaledMinimum and AbsoluteMinimum and provides a single computeMinimum() method. + */ +class ScaledTimerMinimum : private absl::variant { +public: + // Use base class constructor. + using absl::variant::variant; + + // Computes the minimum value for a given maximum timeout. If this object was constructed with a + // - ScaledMinimum value: + // the return value is the scale factor applied to the provided maximum. + // - AbsoluteMinimum: + // the return value is that minimum, and the provided maximum is ignored. + std::chrono::milliseconds computeMinimum(std::chrono::milliseconds maximum) const { + struct Visitor { + explicit Visitor(std::chrono::milliseconds value) : value_(value) {} + std::chrono::milliseconds operator()(ScaledMinimum scale_factor) { + return std::chrono::duration_cast(scale_factor.scale_factor_ * + value_); + } + std::chrono::milliseconds operator()(AbsoluteMinimum absolute_value) { + return absolute_value.value_; + } + const std::chrono::milliseconds value_; + }; + return absl::visit&>( + Visitor(maximum), *this); + } +}; + +/** + * Class for creating Timer objects that can be adjusted towards either the minimum or maximum + * of their range by the owner of the manager object. Users of this class can call createTimer() to + * receive a new Timer object that they can then enable or disable at will (but only on the same + * dispatcher), and setScaleFactor() to change the scaling factor. The current scale factor is + * applied to all timers, including those that are created later. + */ +class ScaledRangeTimerManager { +public: + virtual ~ScaledRangeTimerManager() = default; + + /** + * Creates a new timer backed by the manager. Calling enableTimer on the returned object sets the + * maximum duration, while the first argument here controls the minimum. Passing a value of + * ScaleFactor(x) sets the min to (x * max) when the timer is enabled, while AbsoluteValue(y) sets + * the min to the duration y. + */ + virtual TimerPtr createTimer(ScaledTimerMinimum minimum, TimerCb callback) PURE; + + /** + * Sets the scale factor for all timers created through this manager. The value should be between + * 0 and 1, inclusive. The scale factor affects the amount of time timers spend in their target + * range. The timers returned by createTimer will fire after (min + (max - min) * scale_factor). + * This means that a scale factor of 0 causes timers to fire immediately at the min duration, a + * factor of 0.5 causes firing halfway between min and max, and a factor of 1 causes firing at + * max. + */ + virtual void setScaleFactor(double scale_factor) PURE; +}; + +using ScaledRangeTimerManagerPtr = std::unique_ptr; + +} // namespace Event +} // namespace Envoy diff --git a/source/common/event/BUILD b/source/common/event/BUILD index e84aeb0cde04d..5e538bc52cf67 100644 --- a/source/common/event/BUILD +++ b/source/common/event/BUILD @@ -154,12 +154,13 @@ envoy_cc_library( ) envoy_cc_library( - name = "scaled_range_timer_manager", - srcs = ["scaled_range_timer_manager.cc"], - hdrs = ["scaled_range_timer_manager.h"], + name = "scaled_range_timer_manager_lib", + srcs = ["scaled_range_timer_manager_impl.cc"], + hdrs = ["scaled_range_timer_manager_impl.h"], deps = [ "//include/envoy/event:dispatcher_interface", - "//include/envoy/event:range_timer_interface", + "//include/envoy/event:scaled_range_timer_manager_interface", + "//include/envoy/event:timer_interface", "//source/common/common:scope_tracker", ], ) diff --git a/source/common/event/scaled_range_timer_manager.cc b/source/common/event/scaled_range_timer_manager_impl.cc similarity index 72% rename from source/common/event/scaled_range_timer_manager.cc rename to source/common/event/scaled_range_timer_manager_impl.cc index 10ac02b713cda..eca77f2186b0d 100644 --- a/source/common/event/scaled_range_timer_manager.cc +++ b/source/common/event/scaled_range_timer_manager_impl.cc @@ -1,10 +1,9 @@ -#include "common/event/scaled_range_timer_manager.h" +#include "common/event/scaled_range_timer_manager_impl.h" #include #include #include -#include "envoy/event/range_timer.h" #include "envoy/event/timer.h" #include "common/common/assert.h" @@ -14,7 +13,7 @@ namespace Envoy { namespace Event { /** - * Implementation of RangeTimer that can be scaled by the backing manager object. + * Implementation of Timer that can be scaled by the backing manager object. * * Instances of this class exist in one of 3 states: * - inactive: not enabled @@ -31,10 +30,10 @@ namespace Event { * [scaling-max -> inactive -> waiting-for-min -> scaling-max] in a single * method call. The waiting-for-min transitions are elided for efficiency. */ -class ScaledRangeTimerManager::RangeTimerImpl final : public RangeTimer { +class ScaledRangeTimerManagerImpl::RangeTimerImpl final : public Timer { public: - RangeTimerImpl(TimerCb callback, ScaledRangeTimerManager& manager) - : manager_(manager), callback_(std::move(callback)), + RangeTimerImpl(ScaledTimerMinimum minimum, TimerCb callback, ScaledRangeTimerManagerImpl& manager) + : minimum_(minimum), manager_(manager), callback_(std::move(callback)), min_duration_timer_(manager.dispatcher_.createTimer([this] { onMinTimerComplete(); })) {} ~RangeTimerImpl() override { disableTimer(); } @@ -52,12 +51,13 @@ class ScaledRangeTimerManager::RangeTimerImpl final : public RangeTimer { scope_ = nullptr; } - void enableTimer(const std::chrono::milliseconds min_ms, const std::chrono::milliseconds max_ms, + void enableTimer(const std::chrono::milliseconds max_ms, const ScopeTrackedObject* scope) override { disableTimer(); scope_ = scope; - ENVOY_LOG_MISC(trace, "enableTimer called on {} for ({}ms, {}ms)", static_cast(this), - min_ms.count(), max_ms.count()); + const std::chrono::milliseconds min_ms = std::min(minimum_.computeMinimum(max_ms), max_ms); + ENVOY_LOG_MISC(trace, "enableTimer called on {} for {}ms, min is {}ms", + static_cast(this), max_ms.count(), min_ms.count()); if (min_ms <= std::chrono::milliseconds::zero()) { // If the duration spread (max - min) is zero, skip over the waiting-for-min and straight to // the scaling-max state. @@ -65,10 +65,15 @@ class ScaledRangeTimerManager::RangeTimerImpl final : public RangeTimer { state_.emplace(handle); } else { state_.emplace(max_ms - min_ms); - min_duration_timer_->enableTimer(min_ms); + min_duration_timer_->enableTimer(std::min(max_ms, min_ms)); } } + void enableHRTimer(std::chrono::microseconds us, + const ScopeTrackedObject* object = nullptr) override { + enableTimer(std::chrono::duration_cast(us), object); + } + bool enabled() override { return !absl::holds_alternative(state_); } void trigger() { @@ -98,10 +103,10 @@ class ScaledRangeTimerManager::RangeTimerImpl final : public RangeTimer { }; struct ScalingMax { - ScalingMax(ScaledRangeTimerManager::ScalingTimerHandle handle) : handle_(handle) {} + ScalingMax(ScaledRangeTimerManagerImpl::ScalingTimerHandle handle) : handle_(handle) {} // A handle that can be used to disable the timer. - ScaledRangeTimerManager::ScalingTimerHandle handle_; + ScaledRangeTimerManagerImpl::ScalingTimerHandle handle_; }; /** @@ -123,7 +128,8 @@ class ScaledRangeTimerManager::RangeTimerImpl final : public RangeTimer { } } - ScaledRangeTimerManager& manager_; + const ScaledTimerMinimum minimum_; + ScaledRangeTimerManagerImpl& manager_; const TimerCb callback_; const TimerPtr min_duration_timer_; @@ -131,20 +137,20 @@ class ScaledRangeTimerManager::RangeTimerImpl final : public RangeTimer { const ScopeTrackedObject* scope_; }; -ScaledRangeTimerManager::ScaledRangeTimerManager(Dispatcher& dispatcher) +ScaledRangeTimerManagerImpl::ScaledRangeTimerManagerImpl(Dispatcher& dispatcher) : dispatcher_(dispatcher), scale_factor_(1.0) {} -ScaledRangeTimerManager::~ScaledRangeTimerManager() { +ScaledRangeTimerManagerImpl::~ScaledRangeTimerManagerImpl() { // Scaled timers created by the manager shouldn't outlive it. This is // necessary but not sufficient to guarantee that. ASSERT(queues_.empty()); } -RangeTimerPtr ScaledRangeTimerManager::createTimer(TimerCb callback) { - return std::make_unique(callback, *this); +TimerPtr ScaledRangeTimerManagerImpl::createTimer(ScaledTimerMinimum minimum, TimerCb callback) { + return std::make_unique(minimum, callback, *this); } -void ScaledRangeTimerManager::setScaleFactor(double scale_factor) { +void ScaledRangeTimerManagerImpl::setScaleFactor(double scale_factor) { const MonotonicTime now = dispatcher_.approximateMonotonicTime(); scale_factor_ = DurationScaleFactor(scale_factor); for (auto& queue : queues_) { @@ -152,31 +158,32 @@ void ScaledRangeTimerManager::setScaleFactor(double scale_factor) { } } -ScaledRangeTimerManager::Queue::Item::Item(RangeTimerImpl& timer, MonotonicTime active_time) +ScaledRangeTimerManagerImpl::Queue::Item::Item(RangeTimerImpl& timer, MonotonicTime active_time) : timer_(timer), active_time_(active_time) {} -ScaledRangeTimerManager::Queue::Queue(std::chrono::milliseconds duration, - ScaledRangeTimerManager& manager, Dispatcher& dispatcher) +ScaledRangeTimerManagerImpl::Queue::Queue(std::chrono::milliseconds duration, + ScaledRangeTimerManagerImpl& manager, + Dispatcher& dispatcher) : duration_(duration), timer_(dispatcher.createTimer([this, &manager] { manager.onQueueTimerFired(*this); })) {} -ScaledRangeTimerManager::ScalingTimerHandle::ScalingTimerHandle(Queue& queue, - Queue::Iterator iterator) +ScaledRangeTimerManagerImpl::ScalingTimerHandle::ScalingTimerHandle(Queue& queue, + Queue::Iterator iterator) : queue_(queue), iterator_(iterator) {} -ScaledRangeTimerManager::DurationScaleFactor::DurationScaleFactor(double value) +ScaledRangeTimerManagerImpl::DurationScaleFactor::DurationScaleFactor(double value) : value_(std::max(0.0, std::min(value, 1.0))) {} -MonotonicTime ScaledRangeTimerManager::computeTriggerTime(const Queue::Item& item, - std::chrono::milliseconds duration, - DurationScaleFactor scale_factor) { +MonotonicTime ScaledRangeTimerManagerImpl::computeTriggerTime(const Queue::Item& item, + std::chrono::milliseconds duration, + DurationScaleFactor scale_factor) { return item.active_time_ + std::chrono::duration_cast(duration * scale_factor.value()); } -ScaledRangeTimerManager::ScalingTimerHandle -ScaledRangeTimerManager::activateTimer(std::chrono::milliseconds duration, - RangeTimerImpl& range_timer) { +ScaledRangeTimerManagerImpl::ScalingTimerHandle +ScaledRangeTimerManagerImpl::activateTimer(std::chrono::milliseconds duration, + RangeTimerImpl& range_timer) { // Ensure this is being called on the same dispatcher. ASSERT(dispatcher_.isThreadSafe()); @@ -200,7 +207,7 @@ ScaledRangeTimerManager::activateTimer(std::chrono::milliseconds duration, return ScalingTimerHandle(queue, --queue.range_timers_.end()); } -void ScaledRangeTimerManager::removeTimer(ScalingTimerHandle handle) { +void ScaledRangeTimerManagerImpl::removeTimer(ScalingTimerHandle handle) { // Ensure this is being called on the same dispatcher. ASSERT(dispatcher_.isThreadSafe()); @@ -219,7 +226,7 @@ void ScaledRangeTimerManager::removeTimer(ScalingTimerHandle handle) { } } -void ScaledRangeTimerManager::resetQueueTimer(Queue& queue, MonotonicTime now) { +void ScaledRangeTimerManagerImpl::resetQueueTimer(Queue& queue, MonotonicTime now) { ASSERT(!queue.range_timers_.empty()); const MonotonicTime trigger_time = computeTriggerTime(queue.range_timers_.front(), queue.duration_, scale_factor_); @@ -231,7 +238,7 @@ void ScaledRangeTimerManager::resetQueueTimer(Queue& queue, MonotonicTime now) { } } -void ScaledRangeTimerManager::onQueueTimerFired(Queue& queue) { +void ScaledRangeTimerManagerImpl::onQueueTimerFired(Queue& queue) { auto& timers = queue.range_timers_; ASSERT(!timers.empty()); const MonotonicTime now = dispatcher_.approximateMonotonicTime(); diff --git a/source/common/event/scaled_range_timer_manager.h b/source/common/event/scaled_range_timer_manager_impl.h similarity index 64% rename from source/common/event/scaled_range_timer_manager.h rename to source/common/event/scaled_range_timer_manager_impl.h index 1fbd51c8c86ee..d1f6624c3bf10 100644 --- a/source/common/event/scaled_range_timer_manager.h +++ b/source/common/event/scaled_range_timer_manager_impl.h @@ -2,7 +2,7 @@ #include #include "envoy/event/dispatcher.h" -#include "envoy/event/range_timer.h" +#include "envoy/event/scaled_range_timer_manager.h" #include "envoy/event/timer.h" #include "absl/container/flat_hash_map.h" @@ -11,40 +11,22 @@ namespace Envoy { namespace Event { /** - * Class for creating RangeTimer objects that can be adjusted towards either the minimum or maximum - * of their range by the owner of the manager object. Users of this class can call createTimer() to - * receive a new RangeTimer object that they can then enable or disable at will (but only on the - * same dispatcher), and setScaleFactor() to change the scaling factor. The current scale factor is - * applied to all timers, including those that are created later. - * - * Internally, the manager uses a set of queues to track timers. When an enabled timer reaches its - * min duration, it adds a tracker object to the queue corresponding to the duration (max - min). - * Each queue tracks timers of only a single duration, and uses a real Timer object to schedule the - * expiration of the first timer in the queue. The expectation is that the number of (max - min) - * values used to enable timers is small, so the number of queues is tightly bounded. The - * queue-based implementation depends on that expectation for efficient operation. + * Implementation class for ScaledRangeTimerManager. Internally, this uses a set of queues to track + * timers. When an enabled timer reaches its min duration, it adds a tracker object to the queue + * corresponding to the duration (max - min). Each queue tracks timers of only a single duration, + * and uses a real Timer object to schedule the expiration of the first timer in the queue. The + * expectation is that the number of (max - min) values used to enable timers is small, so the + * number of queues is tightly bounded. The queue-based implementation depends on that expectation + * for efficient operation. */ -class ScaledRangeTimerManager { +class ScaledRangeTimerManagerImpl : public ScaledRangeTimerManager { public: - explicit ScaledRangeTimerManager(Dispatcher& dispatcher); - ~ScaledRangeTimerManager(); + explicit ScaledRangeTimerManagerImpl(Dispatcher& dispatcher); + ~ScaledRangeTimerManagerImpl() override; - /** - * Creates a new range timer backed by the manager. The returned timer will be subject to the - * current and future scale factor values set on the manager. All returned timers must be deleted - * before the manager. - */ - RangeTimerPtr createTimer(TimerCb callback); - - /** - * Sets the scale factor for all timers created through this manager. The value should be between - * 0 and 1, inclusive. The scale factor affects the amount of time timers spend in their target - * range. The RangeTimers returned by createTimer will fire after (min + (max - min) * - * scale_factor). This means that a scale factor of 0 causes timers to fire immediately at the min - * duration, a factor of 0.5 causes firing halfway between min and max, and a factor of 1 causes - * firing at max. - */ - void setScaleFactor(double scale_factor); + // ScaledRangeTimerManager impl + TimerPtr createTimer(ScaledTimerMinimum minimum, TimerCb callback) override; + void setScaleFactor(double scale_factor) override; private: class RangeTimerImpl; @@ -62,7 +44,7 @@ class ScaledRangeTimerManager { // Typedef for convenience. using Iterator = std::list::iterator; - Queue(std::chrono::milliseconds duration, ScaledRangeTimerManager& manager, + Queue(std::chrono::milliseconds duration, ScaledRangeTimerManagerImpl& manager, Dispatcher& dispatcher); // The (max - min) value for all timers in range_timers_. diff --git a/test/common/event/BUILD b/test/common/event/BUILD index 036d0f424359b..882d2afa95068 100644 --- a/test/common/event/BUILD +++ b/test/common/event/BUILD @@ -43,10 +43,10 @@ envoy_cc_test( ) envoy_cc_test( - name = "scaled_range_timer_manager_test", - srcs = ["scaled_range_timer_manager_test.cc"], + name = "scaled_range_timer_manager_impl_test", + srcs = ["scaled_range_timer_manager_impl_test.cc"], deps = [ - "//source/common/event:scaled_range_timer_manager", + "//source/common/event:scaled_range_timer_manager_lib", "//test/mocks/event:wrapped_dispatcher", "//test/test_common:simulated_time_system_lib", ], diff --git a/test/common/event/scaled_range_timer_manager_test.cc b/test/common/event/scaled_range_timer_manager_impl_test.cc similarity index 68% rename from test/common/event/scaled_range_timer_manager_test.cc rename to test/common/event/scaled_range_timer_manager_impl_test.cc index ff9bcab087726..2e0afed349b19 100644 --- a/test/common/event/scaled_range_timer_manager_test.cc +++ b/test/common/event/scaled_range_timer_manager_impl_test.cc @@ -3,7 +3,7 @@ #include "envoy/event/timer.h" #include "common/event/dispatcher_impl.h" -#include "common/event/scaled_range_timer_manager.h" +#include "common/event/scaled_range_timer_manager_impl.h" #include "test/mocks/common.h" #include "test/mocks/event/wrapped_dispatcher.h" @@ -46,36 +46,37 @@ class ScaledRangeTimerManagerTest : public testing::Test, public TestUsingSimula ScopeTrackingDispatcher dispatcher_; }; -struct TrackedTimer { - explicit TrackedTimer(ScaledRangeTimerManager& manager, TimeSystem& time_system) - : timer(manager.createTimer([trigger_times = trigger_times.get(), &time_system] { +struct TrackedRangeTimer { + explicit TrackedRangeTimer(ScaledTimerMinimum minimum, ScaledRangeTimerManagerImpl& manager, + TimeSystem& time_system) + : timer(manager.createTimer(minimum, [trigger_times = trigger_times.get(), &time_system] { trigger_times->push_back(time_system.monotonicTime()); })) {} std::unique_ptr> trigger_times{ std::make_unique>()}; - RangeTimerPtr timer; + TimerPtr timer; }; TEST_F(ScaledRangeTimerManagerTest, CreateAndDestroy) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); } TEST_F(ScaledRangeTimerManagerTest, CreateAndDestroyTimer) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); { MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = manager.createTimer(ScaledMinimum(1.0), callback.AsStdFunction()); } } TEST_F(ScaledRangeTimerManagerTest, CreateSingleScaledTimer) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = manager.createTimer(ScaledMinimum(0.5), callback.AsStdFunction()); - timer->enableTimer(std::chrono::seconds(5), std::chrono::seconds(10)); + timer->enableTimer(std::chrono::seconds(10)); EXPECT_TRUE(timer->enabled()); simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); @@ -87,12 +88,13 @@ TEST_F(ScaledRangeTimerManagerTest, CreateSingleScaledTimer) { } TEST_F(ScaledRangeTimerManagerTest, EnableAndDisableTimer) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback.AsStdFunction()); - timer->enableTimer(std::chrono::seconds(5), std::chrono::seconds(30)); + timer->enableTimer(std::chrono::seconds(30)); EXPECT_TRUE(timer->enabled()); timer->disableTimer(); @@ -105,10 +107,10 @@ TEST_F(ScaledRangeTimerManagerTest, EnableAndDisableTimer) { } TEST_F(ScaledRangeTimerManagerTest, DisableWhileDisabled) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = manager.createTimer(ScaledMinimum(1.0), callback.AsStdFunction()); EXPECT_FALSE(timer->enabled()); timer->disableTimer(); @@ -117,11 +119,12 @@ TEST_F(ScaledRangeTimerManagerTest, DisableWhileDisabled) { } TEST_F(ScaledRangeTimerManagerTest, DisableWhileWaitingForMin) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); - timer->enableTimer(std::chrono::seconds(10), std::chrono::seconds(100)); + auto timer = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(10)), callback.AsStdFunction()); + timer->enableTimer(std::chrono::seconds(100)); EXPECT_TRUE(timer->enabled()); timer->disableTimer(); @@ -129,12 +132,13 @@ TEST_F(ScaledRangeTimerManagerTest, DisableWhileWaitingForMin) { } TEST_F(ScaledRangeTimerManagerTest, DisableWhileScalingMax) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback.AsStdFunction()); - timer->enableTimer(std::chrono::seconds(5), std::chrono::seconds(100)); + timer->enableTimer(std::chrono::seconds(100)); simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); @@ -148,12 +152,12 @@ TEST_F(ScaledRangeTimerManagerTest, DisableWhileScalingMax) { } TEST_F(ScaledRangeTimerManagerTest, DisableWithZeroMinTime) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = manager.createTimer(ScaledMinimum(0.0), callback.AsStdFunction()); - timer->enableTimer(std::chrono::seconds(0), std::chrono::seconds(100)); + timer->enableTimer(std::chrono::seconds(100)); EXPECT_TRUE(timer->enabled()); @@ -165,12 +169,13 @@ TEST_F(ScaledRangeTimerManagerTest, DisableWithZeroMinTime) { } TEST_F(ScaledRangeTimerManagerTest, TriggerWithZeroMinTime) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(0)), callback.AsStdFunction()); - timer->enableTimer(std::chrono::seconds(0), std::chrono::seconds(10)); + timer->enableTimer(std::chrono::seconds(10)); simTime().advanceTimeAndRun(std::chrono::seconds(9), dispatcher_, Dispatcher::RunType::Block); EXPECT_CALL(callback, Call); @@ -178,15 +183,17 @@ TEST_F(ScaledRangeTimerManagerTest, TriggerWithZeroMinTime) { } TEST_F(ScaledRangeTimerManagerTest, DisableFrontScalingMaxTimer) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback1, callback2; - auto timer1 = manager.createTimer(callback1.AsStdFunction()); - auto timer2 = manager.createTimer(callback2.AsStdFunction()); + auto timer1 = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback1.AsStdFunction()); + auto timer2 = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(10)), callback2.AsStdFunction()); // These timers have the same max-min. - timer1->enableTimer(std::chrono::seconds(5), std::chrono::seconds(30)); - timer2->enableTimer(std::chrono::seconds(10), std::chrono::seconds(35)); + timer1->enableTimer(std::chrono::seconds(30)); + timer2->enableTimer(std::chrono::seconds(35)); simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); @@ -204,15 +211,17 @@ TEST_F(ScaledRangeTimerManagerTest, DisableFrontScalingMaxTimer) { } TEST_F(ScaledRangeTimerManagerTest, DisableLaterScalingMaxTimer) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback1, callback2; - auto timer1 = manager.createTimer(callback1.AsStdFunction()); - auto timer2 = manager.createTimer(callback2.AsStdFunction()); + auto timer1 = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback1.AsStdFunction()); + auto timer2 = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(10)), callback2.AsStdFunction()); // These timers have the same max-min. - timer1->enableTimer(std::chrono::seconds(5), std::chrono::seconds(30)); - timer2->enableTimer(std::chrono::seconds(10), std::chrono::seconds(35)); + timer1->enableTimer(std::chrono::seconds(30)); + timer2->enableTimer(std::chrono::seconds(35)); simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); @@ -234,22 +243,23 @@ class ScaledRangeTimerManagerTestWithScope : public ScaledRangeTimerManagerTest, }; TEST_P(ScaledRangeTimerManagerTestWithScope, ReRegisterOnCallback) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(1)), callback.AsStdFunction()); EXPECT_EQ(dispatcher_.scope_, nullptr); { InSequence s; EXPECT_CALL(callback, Call).WillOnce([&] { EXPECT_EQ(dispatcher_.scope_, getScope()); - timer->enableTimer(std::chrono::seconds(1), std::chrono::seconds(2), getScope()); + timer->enableTimer(std::chrono::seconds(2), getScope()); }); EXPECT_CALL(callback, Call).WillOnce([&] { EXPECT_EQ(dispatcher_.scope_, getScope()); }); } - timer->enableTimer(std::chrono::seconds(1), std::chrono::seconds(2), getScope()); + timer->enableTimer(std::chrono::seconds(2), getScope()); simTime().advanceTimeAndRun(std::chrono::seconds(1), dispatcher_, Dispatcher::RunType::Block); EXPECT_EQ(dispatcher_.scope_, nullptr); @@ -265,15 +275,16 @@ TEST_P(ScaledRangeTimerManagerTestWithScope, ReRegisterOnCallback) { }; TEST_P(ScaledRangeTimerManagerTestWithScope, ScheduleWithScalingFactorZero) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(0)), callback.AsStdFunction()); manager.setScaleFactor(0); EXPECT_CALL(callback, Call).WillOnce([&] { EXPECT_EQ(dispatcher_.scope_, getScope()); }); - timer->enableTimer(std::chrono::seconds(0), std::chrono::seconds(1), getScope()); + timer->enableTimer(std::chrono::seconds(1), getScope()); simTime().advanceTimeAndRun(std::chrono::milliseconds(1), dispatcher_, Dispatcher::RunType::Block); } @@ -282,14 +293,15 @@ INSTANTIATE_TEST_SUITE_P(WithAndWithoutScope, ScaledRangeTimerManagerTestWithSco testing::Bool()); TEST_F(ScaledRangeTimerManagerTest, SingleTimerTriggeredNoScaling) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); bool triggered = false; MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback.AsStdFunction()); EXPECT_CALL(callback, Call()).WillOnce([&] { triggered = true; }); - timer->enableTimer(std::chrono::seconds(5), std::chrono::seconds(9)); + timer->enableTimer(std::chrono::seconds(9)); simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); EXPECT_FALSE(triggered); @@ -304,31 +316,59 @@ TEST_F(ScaledRangeTimerManagerTest, SingleTimerTriggeredNoScaling) { } TEST_F(ScaledRangeTimerManagerTest, SingleTimerSameMinMax) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback; - auto timer = manager.createTimer(callback.AsStdFunction()); + auto timer = manager.createTimer(ScaledMinimum(1.0), callback.AsStdFunction()); EXPECT_CALL(callback, Call()); - timer->enableTimer(std::chrono::seconds(1), std::chrono::seconds(1)); + timer->enableTimer(std::chrono::seconds(1)); simTime().advanceTimeAndRun(std::chrono::seconds(1), dispatcher_, Dispatcher::RunType::Block); EXPECT_FALSE(timer->enabled()); } +TEST_F(ScaledRangeTimerManagerTest, ScaledMinimumFactorGreaterThan1) { + ScaledRangeTimerManagerImpl manager(dispatcher_); + + // If the minimum scale factor is > 1, it should be treated as if it was 1. + MockFunction callback; + auto timer = manager.createTimer(ScaledMinimum(2), callback.AsStdFunction()); + + timer->enableTimer(std::chrono::seconds(10)); + EXPECT_CALL(callback, Call); + simTime().advanceTimeAndRun(std::chrono::seconds(10), dispatcher_, Dispatcher::RunType::Block); + EXPECT_FALSE(timer->enabled()); +} + +TEST_F(ScaledRangeTimerManagerTest, AbsoluteMinimumGreaterThanMax) { + ScaledRangeTimerManagerImpl manager(dispatcher_); + + // If the minimum is greater than the maximum, it should be treated as if it was the same as the + // max. + MockFunction callback; + auto timer = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(20)), callback.AsStdFunction()); + + timer->enableTimer(std::chrono::seconds(10)); + EXPECT_CALL(callback, Call); + simTime().advanceTimeAndRun(std::chrono::seconds(10), dispatcher_, Dispatcher::RunType::Block); + EXPECT_FALSE(timer->enabled()); +} + TEST_F(ScaledRangeTimerManagerTest, MultipleTimersNoScaling) { - ScaledRangeTimerManager manager(dispatcher_); - std::vector timers; + ScaledRangeTimerManagerImpl manager(dispatcher_); + std::vector timers; timers.reserve(3); const MonotonicTime start = simTime().monotonicTime(); - for (int i = 0; i < 3; ++i) { - timers.emplace_back(manager, simTime()); - } + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(1)), manager, simTime()); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(2)), manager, simTime()); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(0)), manager, simTime()); - timers[0].timer->enableTimer(std::chrono::seconds(1), std::chrono::seconds(3)); - timers[1].timer->enableTimer(std::chrono::seconds(2), std::chrono::seconds(6)); - timers[2].timer->enableTimer(std::chrono::seconds(0), std::chrono::seconds(9)); + timers[0].timer->enableTimer(std::chrono::seconds(3)); + timers[1].timer->enableTimer(std::chrono::seconds(6)); + timers[2].timer->enableTimer(std::chrono::seconds(9)); for (int i = 0; i < 10; ++i) { simTime().advanceTimeAndRun(std::chrono::seconds(1), dispatcher_, Dispatcher::RunType::Block); @@ -340,19 +380,19 @@ TEST_F(ScaledRangeTimerManagerTest, MultipleTimersNoScaling) { } TEST_F(ScaledRangeTimerManagerTest, MultipleTimersWithScaling) { - ScaledRangeTimerManager manager(dispatcher_); - std::vector timers; + ScaledRangeTimerManagerImpl manager(dispatcher_); + std::vector timers; timers.reserve(3); - for (int i = 0; i < 3; ++i) { - timers.emplace_back(manager, simTime()); - } + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(1)), manager, simTime()); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(2)), manager, simTime()); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(6)), manager, simTime()); const MonotonicTime start = simTime().monotonicTime(); - timers[0].timer->enableTimer(std::chrono::seconds(1), std::chrono::seconds(3)); - timers[1].timer->enableTimer(std::chrono::seconds(2), std::chrono::seconds(6)); - timers[2].timer->enableTimer(std::chrono::seconds(6), std::chrono::seconds(10)); + timers[0].timer->enableTimer(std::chrono::seconds(3)); + timers[1].timer->enableTimer(std::chrono::seconds(6)); + timers[2].timer->enableTimer(std::chrono::seconds(10)); manager.setScaleFactor(0.5); @@ -381,15 +421,15 @@ TEST_F(ScaledRangeTimerManagerTest, MultipleTimersWithScaling) { } TEST_F(ScaledRangeTimerManagerTest, MultipleTimersSameTimes) { - ScaledRangeTimerManager manager(dispatcher_); - std::vector timers; + ScaledRangeTimerManagerImpl manager(dispatcher_); + std::vector timers; timers.reserve(3); const MonotonicTime start = simTime().monotonicTime(); for (int i = 0; i < 3; ++i) { - timers.emplace_back(manager, simTime()); - timers[i].timer->enableTimer(std::chrono::seconds(1), std::chrono::seconds(2)); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(1)), manager, simTime()); + timers[i].timer->enableTimer(std::chrono::seconds(2)); } simTime().advanceTimeAndRun(std::chrono::seconds(1), dispatcher_, Dispatcher::RunType::Block); @@ -402,15 +442,15 @@ TEST_F(ScaledRangeTimerManagerTest, MultipleTimersSameTimes) { } TEST_F(ScaledRangeTimerManagerTest, MultipleTimersSameTimesFastClock) { - ScaledRangeTimerManager manager(dispatcher_); - std::vector timers; + ScaledRangeTimerManagerImpl manager(dispatcher_); + std::vector timers; timers.reserve(3); const MonotonicTime start = simTime().monotonicTime(); for (int i = 0; i < 3; ++i) { - timers.emplace_back(manager, simTime()); - timers[i].timer->enableTimer(std::chrono::seconds(1), std::chrono::seconds(2)); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(1)), manager, simTime()); + timers[i].timer->enableTimer(std::chrono::seconds(2)); } simTime().advanceTimeAndRun(std::chrono::seconds(1), dispatcher_, Dispatcher::RunType::Block); @@ -423,14 +463,14 @@ TEST_F(ScaledRangeTimerManagerTest, MultipleTimersSameTimesFastClock) { } TEST_F(ScaledRangeTimerManagerTest, ScheduledWithScalingFactorZero) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); manager.setScaleFactor(0); - TrackedTimer timer(manager, simTime()); + TrackedRangeTimer timer(AbsoluteMinimum(std::chrono::seconds(4)), manager, simTime()); // The timer should fire at start = 4 since the scaling factor is 0. const MonotonicTime start = simTime().monotonicTime(); - timer.timer->enableTimer(std::chrono::seconds(4), std::chrono::seconds(10)); + timer.timer->enableTimer(std::chrono::seconds(10)); for (int i = 0; i < 10; ++i) { simTime().advanceTimeAndRun(std::chrono::seconds(4), dispatcher_, Dispatcher::RunType::Block); @@ -442,12 +482,12 @@ TEST_F(ScaledRangeTimerManagerTest, ScheduledWithScalingFactorZero) { TEST_F(ScaledRangeTimerManagerTest, ScheduledWithMaxBeforeMin) { // When max < min, the timer behaves the same as if max == min. This ensures that min is always // respected, and max is respected as much as possible. - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); - TrackedTimer timer(manager, simTime()); + TrackedRangeTimer timer(AbsoluteMinimum(std::chrono::seconds(4)), manager, simTime()); const MonotonicTime start = simTime().monotonicTime(); - timer.timer->enableTimer(std::chrono::seconds(4), std::chrono::seconds(3)); + timer.timer->enableTimer(std::chrono::seconds(3)); for (int i = 0; i < 10; ++i) { simTime().advanceTimeAndRun(std::chrono::seconds(4), dispatcher_, Dispatcher::RunType::Block); @@ -457,16 +497,19 @@ TEST_F(ScaledRangeTimerManagerTest, ScheduledWithMaxBeforeMin) { } TEST_F(ScaledRangeTimerManagerTest, MultipleTimersTriggeredInTheSameEventLoopIteration) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); MockFunction callback1, callback2, callback3; - auto timer1 = manager.createTimer(callback1.AsStdFunction()); - auto timer2 = manager.createTimer(callback2.AsStdFunction()); - auto timer3 = manager.createTimer(callback3.AsStdFunction()); + auto timer1 = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback1.AsStdFunction()); + auto timer2 = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback2.AsStdFunction()); + auto timer3 = + manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback3.AsStdFunction()); - timer1->enableTimer(std::chrono::seconds(5), std::chrono::seconds(10)); - timer2->enableTimer(std::chrono::seconds(5), std::chrono::seconds(10)); - timer3->enableTimer(std::chrono::seconds(5), std::chrono::seconds(10)); + timer1->enableTimer(std::chrono::seconds(10)); + timer2->enableTimer(std::chrono::seconds(10)); + timer3->enableTimer(std::chrono::seconds(10)); simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); @@ -510,22 +553,23 @@ TEST_F(ScaledRangeTimerManagerTest, MultipleTimersTriggeredInTheSameEventLoopIte } TEST_F(ScaledRangeTimerManagerTest, MultipleTimersWithChangeInScalingFactor) { - ScaledRangeTimerManager manager(dispatcher_); + ScaledRangeTimerManagerImpl manager(dispatcher_); const MonotonicTime start = simTime().monotonicTime(); - std::vector timers; + std::vector timers; timers.reserve(4); - for (int i = 0; i < 4; i++) { - timers.emplace_back(manager, simTime()); - } + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(5)), manager, simTime()); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(12)), manager, simTime()); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(7)), manager, simTime()); + timers.emplace_back(AbsoluteMinimum(std::chrono::seconds(10)), manager, simTime()); - timers[0].timer->enableTimer(std::chrono::seconds(5), std::chrono::seconds(15)); - timers[1].timer->enableTimer(std::chrono::seconds(12), std::chrono::seconds(14)); + timers[0].timer->enableTimer(std::chrono::seconds(15)); + timers[1].timer->enableTimer(std::chrono::seconds(14)); manager.setScaleFactor(0.1); - timers[2].timer->enableTimer(std::chrono::seconds(7), std::chrono::seconds(21)); - timers[3].timer->enableTimer(std::chrono::seconds(10), std::chrono::seconds(16)); + timers[2].timer->enableTimer(std::chrono::seconds(21)); + timers[3].timer->enableTimer(std::chrono::seconds(16)); // Advance to timer 0's min. simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block); @@ -551,7 +595,7 @@ TEST_F(ScaledRangeTimerManagerTest, MultipleTimersWithChangeInScalingFactor) { // The time is now start+10. Re-enable timer 0. ASSERT_FALSE(timers[0].timer->enabled()); - timers[0].timer->enableTimer(std::chrono::seconds(5), std::chrono::seconds(13)); + timers[0].timer->enableTimer(std::chrono::seconds(13)); // Fire times are now 0: start+19, 1: start+13, 2: none, 3: start+13. manager.setScaleFactor(0.5);