mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-11-04 00:16:26 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			126 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//
 | 
						|
//  DeferredQueue.hpp
 | 
						|
//  Clock Signal
 | 
						|
//
 | 
						|
//  Created by Thomas Harte on 23/08/2018.
 | 
						|
//  Copyright © 2018 Thomas Harte. All rights reserved.
 | 
						|
//
 | 
						|
 | 
						|
#pragma once
 | 
						|
 | 
						|
#include <functional>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
/*!
 | 
						|
	Provides the logic to insert into and traverse a list of future scheduled items.
 | 
						|
*/
 | 
						|
template <typename TimeUnit> class DeferredQueue {
 | 
						|
public:
 | 
						|
	/*!
 | 
						|
		Schedules @c action to occur in @c delay units of time.
 | 
						|
	*/
 | 
						|
	void defer(TimeUnit delay, const std::function<void(void)> &action) {
 | 
						|
		// Apply immediately if there's no delay (or a negative delay).
 | 
						|
		if(delay <= TimeUnit(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);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/*!
 | 
						|
		@returns The amount of time until the next enqueued action will occur,
 | 
						|
			or TimeUnit(-1) if the queue is empty.
 | 
						|
	*/
 | 
						|
	TimeUnit time_until_next_action() const {
 | 
						|
		if(pending_actions_.empty()) return TimeUnit(-1);
 | 
						|
		return pending_actions_.front().delay;
 | 
						|
	}
 | 
						|
 | 
						|
	/*!
 | 
						|
		Advances the queue the specified amount of time, performing any actions it reaches.
 | 
						|
	*/
 | 
						|
	void advance(TimeUnit time) {
 | 
						|
		auto erase_iterator = pending_actions_.begin();
 | 
						|
		while(erase_iterator != pending_actions_.end()) {
 | 
						|
			erase_iterator->delay -= time;
 | 
						|
			if(erase_iterator->delay <= TimeUnit(0)) {
 | 
						|
				time = -erase_iterator->delay;
 | 
						|
				erase_iterator->action();
 | 
						|
				++erase_iterator;
 | 
						|
			} else {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(erase_iterator != pending_actions_.begin()) {
 | 
						|
			pending_actions_.erase(pending_actions_.begin(), erase_iterator);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/// @returns @c true if no actions are enqueued; @c false otherwise.
 | 
						|
	bool empty() const {
 | 
						|
		return pending_actions_.empty();
 | 
						|
	}
 | 
						|
 | 
						|
private:
 | 
						|
	// The list of deferred actions.
 | 
						|
	struct DeferredAction {
 | 
						|
		TimeUnit delay;
 | 
						|
		std::function<void(void)> action;
 | 
						|
 | 
						|
		DeferredAction(TimeUnit delay, const std::function<void(void)> &action) :
 | 
						|
			delay(delay), action(std::move(action)) {}
 | 
						|
	};
 | 
						|
	std::vector<DeferredAction> pending_actions_;
 | 
						|
};
 | 
						|
 | 
						|
/*!
 | 
						|
	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 <typename TimeUnit> class DeferredQueuePerformer: public DeferredQueue<TimeUnit> {
 | 
						|
public:
 | 
						|
	/// Constructs a DeferredQueue that will call target(period) in between deferred actions.
 | 
						|
	constexpr DeferredQueuePerformer(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
 | 
						|
 | 
						|
	/*!
 | 
						|
		Runs for @c length units of time.
 | 
						|
 | 
						|
		The constructor-supplied target will be called with one or more periods that add up to @c length;
 | 
						|
		any scheduled actions will be called between periods.
 | 
						|
	*/
 | 
						|
	void run_for(TimeUnit length) {
 | 
						|
		auto time_to_next = DeferredQueue<TimeUnit>::time_until_next_action();
 | 
						|
		while(time_to_next != TimeUnit(-1) && time_to_next <= length) {
 | 
						|
			target_(time_to_next);
 | 
						|
			length -= time_to_next;
 | 
						|
			DeferredQueue<TimeUnit>::advance(time_to_next);
 | 
						|
		}
 | 
						|
 | 
						|
		DeferredQueue<TimeUnit>::advance(length);
 | 
						|
		target_(length);
 | 
						|
	}
 | 
						|
 | 
						|
private:
 | 
						|
	std::function<void(TimeUnit)> target_;
 | 
						|
};
 |