From 8c4fb0f688fc946917169151c7e845c9ae111712 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 29 Jan 2020 21:49:52 -0500 Subject: [PATCH] Extends the DeferredQueue to allow out-of-order enqueing. --- ClockReceiver/DeferredQueue.hpp | 59 ++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/ClockReceiver/DeferredQueue.hpp b/ClockReceiver/DeferredQueue.hpp index d2680f322..92293cb0b 100644 --- a/ClockReceiver/DeferredQueue.hpp +++ b/ClockReceiver/DeferredQueue.hpp @@ -16,6 +16,8 @@ A DeferredQueue maintains a list of ordered actions and the times at which they should happen, and divides a total execution period up into the portions that occur between those actions, triggering each action when it is reached. + + This list is efficient only for short queues. */ template class DeferredQueue { public: @@ -24,12 +26,30 @@ template class DeferredQueue { /*! Schedules @c action to occur in @c delay units of time. - - Actions must be scheduled in the order they will occur. It is undefined behaviour - to schedule them out of order. */ void defer(TimeUnit delay, const std::function &action) { - pending_actions_.emplace_back(delay, action); + // Apply immediately if there's no delay (or a negative delay). + if(delay <= 0) { + action(); + return; + } + + if(!pending_actions_.empty()) { + // Otherwise enqueue, having subtracted the delay for any preceding events, + // and subtracting from the subsequent, if any. + auto insertion_point = pending_actions_.begin(); + while(insertion_point != pending_actions_.end() && insertion_point->delay < delay) { + delay -= insertion_point->delay; + ++insertion_point; + } + if(insertion_point != pending_actions_.end()) { + insertion_point->delay -= delay; + } + + pending_actions_.emplace(insertion_point, delay, action); + } else { + pending_actions_.emplace_back(delay, action); + } } /*! @@ -47,23 +67,24 @@ template class DeferredQueue { } // Divide the time to run according to the pending actions. - while(length > TimeUnit(0)) { - TimeUnit next_period = pending_actions_.empty() ? length : std::min(length, pending_actions_[0].delay); - target_(next_period); - length -= next_period; - - off_t performances = 0; - for(auto &action: pending_actions_) { - action.delay -= next_period; - if(!action.delay) { - action.action(); - ++performances; - } - } - if(performances) { - pending_actions_.erase(pending_actions_.begin(), pending_actions_.begin() + performances); + auto erase_iterator = pending_actions_.begin(); + while(erase_iterator != pending_actions_.end()) { + erase_iterator->delay -= length; + if(erase_iterator->delay <= TimeUnit(0)) { + target_(length + erase_iterator->delay); + length = -erase_iterator->delay; + erase_iterator->action(); + ++erase_iterator; + } else { + break; } } + if(erase_iterator != pending_actions_.begin()) { + pending_actions_.erase(pending_actions_.begin(), erase_iterator); + } + if(length != TimeUnit(0)) { + target_(length); + } } private: