Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion source/common/event/scaled_range_timer_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,11 @@ void ScaledRangeTimerManagerImpl::removeTimer(ScalingTimerHandle handle) {
handle.queue_.range_timers_.erase(handle.iterator_);
// Don't keep around empty queues
if (handle.queue_.range_timers_.empty()) {
queues_.erase(handle.queue_);
// Skip erasing the queue if we're in the middle of processing timers for the queue. The
// queue will be erased in `onQueueTimerFired` after the queue entries have been processed.
if (!handle.queue_.processing_timers_) {
queues_.erase(handle.queue_);
}
return;
}

Expand Down Expand Up @@ -242,12 +246,14 @@ void ScaledRangeTimerManagerImpl::onQueueTimerFired(Queue& queue) {

// Pop and trigger timers until the one at the front isn't supposed to have expired yet (given the
// current scale factor).
queue.processing_timers_ = true;
while (!timers.empty() &&
computeTriggerTime(timers.front(), queue.duration_, scale_factor_) <= now) {
auto item = std::move(queue.range_timers_.front());
queue.range_timers_.pop_front();
item.timer_.trigger();
}
queue.processing_timers_ = false;

if (queue.range_timers_.empty()) {
// Maintain the invariant that queues are never empty.
Expand Down
4 changes: 4 additions & 0 deletions source/common/event/scaled_range_timer_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class ScaledRangeTimerManagerImpl : public ScaledRangeTimerManager {
// 2) on expiration
// 3) when the scale factor changes
const TimerPtr timer_;

// A flag indicating whether the queue is currently processing timers. Used to guard against
// queue deletion during timer processing.
bool processing_timers_{false};
};

/**
Expand Down
54 changes: 54 additions & 0 deletions test/common/event/scaled_range_timer_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,60 @@ TEST_F(ScaledRangeTimerManagerTest, DisableWhileScalingMax) {
simTime().advanceTimeAndRun(std::chrono::seconds(100), dispatcher_, Dispatcher::RunType::Block);
}

TEST_F(ScaledRangeTimerManagerTest, InCallbackDisableLastTimerInSameQueue) {
ScaledRangeTimerManagerImpl manager(dispatcher_);

MockFunction<TimerCb> callback1;
auto timer1 =
manager.createTimer(AbsoluteMinimum(std::chrono::seconds(0)), callback1.AsStdFunction());
MockFunction<TimerCb> callback2;
auto timer2 =
manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback2.AsStdFunction());

timer1->enableTimer(std::chrono::seconds(95));
timer2->enableTimer(std::chrono::seconds(100));

simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block);

EXPECT_TRUE(timer1->enabled());
EXPECT_TRUE(timer2->enabled());

EXPECT_CALL(callback1, Call).WillOnce(Invoke([&]() {
timer2->disableTimer();
timer2.reset();
}));

// Run the dispatcher to make sure nothing happens when it's not supposed to.
simTime().advanceTimeAndRun(std::chrono::seconds(100), dispatcher_, Dispatcher::RunType::Block);
}

TEST_F(ScaledRangeTimerManagerTest, InCallbackDisableTimerInOtherQueue) {
ScaledRangeTimerManagerImpl manager(dispatcher_);

MockFunction<TimerCb> callback1;
auto timer1 =
manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback1.AsStdFunction());
MockFunction<TimerCb> callback2;
auto timer2 =
manager.createTimer(AbsoluteMinimum(std::chrono::seconds(5)), callback2.AsStdFunction());

timer1->enableTimer(std::chrono::seconds(95));
timer2->enableTimer(std::chrono::seconds(100));

simTime().advanceTimeAndRun(std::chrono::seconds(5), dispatcher_, Dispatcher::RunType::Block);

EXPECT_TRUE(timer1->enabled());
EXPECT_TRUE(timer2->enabled());

EXPECT_CALL(callback1, Call).WillOnce(Invoke([&]() {
timer2->disableTimer();
timer2.reset();
}));

// Run the dispatcher to make sure nothing happens when it's not supposed to.
simTime().advanceTimeAndRun(std::chrono::seconds(100), dispatcher_, Dispatcher::RunType::Block);
}

TEST_F(ScaledRangeTimerManagerTest, DisableWithZeroMinTime) {
ScaledRangeTimerManagerImpl manager(dispatcher_);

Expand Down