1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-12 00:30:31 +00:00

Extends the DeferredQueue to allow out-of-order enqueing.

This commit is contained in:
Thomas Harte 2020-01-29 21:49:52 -05:00
parent baa51853c4
commit 8c4fb0f688

View File

@ -16,6 +16,8 @@
A DeferredQueue maintains a list of ordered actions and the times at which 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 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. that occur between those actions, triggering each action when it is reached.
This list is efficient only for short queues.
*/ */
template <typename TimeUnit> class DeferredQueue { template <typename TimeUnit> class DeferredQueue {
public: public:
@ -24,13 +26,31 @@ template <typename TimeUnit> class DeferredQueue {
/*! /*!
Schedules @c action to occur in @c delay units of time. 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<void(void)> &action) { void defer(TimeUnit delay, const std::function<void(void)> &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); pending_actions_.emplace_back(delay, action);
} }
}
/*! /*!
Runs for @c length units of time. Runs for @c length units of time.
@ -47,22 +67,23 @@ template <typename TimeUnit> class DeferredQueue {
} }
// Divide the time to run according to the pending actions. // Divide the time to run according to the pending actions.
while(length > TimeUnit(0)) { auto erase_iterator = pending_actions_.begin();
TimeUnit next_period = pending_actions_.empty() ? length : std::min(length, pending_actions_[0].delay); while(erase_iterator != pending_actions_.end()) {
target_(next_period); erase_iterator->delay -= length;
length -= next_period; if(erase_iterator->delay <= TimeUnit(0)) {
target_(length + erase_iterator->delay);
off_t performances = 0; length = -erase_iterator->delay;
for(auto &action: pending_actions_) { erase_iterator->action();
action.delay -= next_period; ++erase_iterator;
if(!action.delay) { } else {
action.action(); break;
++performances;
} }
} }
if(performances) { if(erase_iterator != pending_actions_.begin()) {
pending_actions_.erase(pending_actions_.begin(), pending_actions_.begin() + performances); pending_actions_.erase(pending_actions_.begin(), erase_iterator);
} }
if(length != TimeUnit(0)) {
target_(length);
} }
} }