2019-07-29 01:49:54 +00:00
|
|
|
//
|
|
|
|
// JustInTime.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 28/07/2019.
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef JustInTime_h
|
|
|
|
#define JustInTime_h
|
|
|
|
|
2019-07-29 20:38:57 +00:00
|
|
|
#include "../Concurrency/AsyncTaskQueue.hpp"
|
2019-10-31 03:18:42 +00:00
|
|
|
#include "ForceInline.hpp"
|
2019-07-29 01:49:54 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
A JustInTimeActor holds (i) an embedded object with a run_for method; and (ii) an amount
|
|
|
|
of time since run_for was last called.
|
|
|
|
|
|
|
|
Time can be added using the += operator. The -> operator can be used to access the
|
|
|
|
embedded object. All time accumulated will be pushed to object before the pointer is returned.
|
|
|
|
|
|
|
|
Machines that accumulate HalfCycle time but supply to a Cycle-counted device may supply a
|
|
|
|
separate @c TargetTimeScale at template declaration.
|
|
|
|
*/
|
2019-10-29 01:35:10 +00:00
|
|
|
template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = HalfCycles, class TargetTimeScale = LocalTimeScale> class JustInTimeActor {
|
2019-07-29 01:49:54 +00:00
|
|
|
public:
|
|
|
|
/// Constructs a new JustInTimeActor using the same construction arguments as the included object.
|
|
|
|
template<typename... Args> JustInTimeActor(Args&&... args) : object_(std::forward<Args>(args)...) {}
|
|
|
|
|
|
|
|
/// Adds time to the actor.
|
2019-10-31 03:18:42 +00:00
|
|
|
forceinline void operator += (const LocalTimeScale &rhs) {
|
2019-12-31 03:58:19 +00:00
|
|
|
if constexpr (multiplier != 1) {
|
2019-10-29 01:35:10 +00:00
|
|
|
time_since_update_ += rhs * multiplier;
|
|
|
|
} else {
|
|
|
|
time_since_update_ += rhs;
|
|
|
|
}
|
2019-07-29 19:38:41 +00:00
|
|
|
is_flushed_ = false;
|
2019-07-29 01:49:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Flushes all accumulated time and returns a pointer to the included object.
|
2019-10-31 03:18:42 +00:00
|
|
|
forceinline T *operator->() {
|
2019-07-29 19:38:41 +00:00
|
|
|
flush();
|
2019-07-29 01:49:54 +00:00
|
|
|
return &object_;
|
|
|
|
}
|
|
|
|
|
2020-01-21 02:45:10 +00:00
|
|
|
/// Acts exactly as per the standard ->, but preserves constness.
|
|
|
|
forceinline const T *operator->() const {
|
|
|
|
auto non_const_this = const_cast<JustInTimeActor<T, multiplier, divider, LocalTimeScale, TargetTimeScale> *>(this);
|
|
|
|
non_const_this->flush();
|
|
|
|
return &object_;
|
|
|
|
}
|
|
|
|
|
2019-07-29 21:17:04 +00:00
|
|
|
/// Returns a pointer to the included object without flushing time.
|
2019-10-31 03:18:42 +00:00
|
|
|
forceinline T *last_valid() {
|
2019-07-29 21:17:04 +00:00
|
|
|
return &object_;
|
|
|
|
}
|
|
|
|
|
2019-07-29 19:38:41 +00:00
|
|
|
/// Flushes all accumulated time.
|
2019-10-31 03:18:42 +00:00
|
|
|
forceinline void flush() {
|
2019-10-13 22:19:39 +00:00
|
|
|
if(!is_flushed_) {
|
2019-10-21 02:10:05 +00:00
|
|
|
is_flushed_ = true;
|
2019-12-31 03:58:19 +00:00
|
|
|
if constexpr (divider == 1) {
|
2020-01-23 00:32:23 +00:00
|
|
|
const auto duration = time_since_update_.template flush<TargetTimeScale>();
|
|
|
|
object_.run_for(duration);
|
2019-10-13 22:19:39 +00:00
|
|
|
} else {
|
2019-10-31 03:18:42 +00:00
|
|
|
const auto duration = time_since_update_.template divide<TargetTimeScale>(LocalTimeScale(divider));
|
|
|
|
if(duration > TargetTimeScale(0))
|
|
|
|
object_.run_for(duration);
|
2019-10-13 22:19:39 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-29 19:38:41 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 22:01:23 +00:00
|
|
|
/// Flushes only if the next window of size @c threshold (measured in LocalTimeScale) has been crossed since the last flush.
|
2020-11-08 00:40:26 +00:00
|
|
|
template<int threshold> forceinline bool check_flush_threshold() {
|
|
|
|
if(time_since_update_.as_integral() >= threshold) {
|
|
|
|
flush();
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-29 01:49:54 +00:00
|
|
|
private:
|
|
|
|
T object_;
|
|
|
|
LocalTimeScale time_since_update_;
|
2019-07-29 19:38:41 +00:00
|
|
|
bool is_flushed_ = true;
|
2019-07-29 01:49:54 +00:00
|
|
|
};
|
|
|
|
|
2020-01-30 02:26:15 +00:00
|
|
|
/*!
|
|
|
|
A RealTimeActor presents the same interface as a JustInTimeActor but doesn't defer work.
|
|
|
|
Time added will be performed immediately.
|
|
|
|
|
|
|
|
Its primary purpose is to allow consumers to remain flexible in their scheduling.
|
|
|
|
*/
|
|
|
|
template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = HalfCycles, class TargetTimeScale = LocalTimeScale> class RealTimeActor {
|
|
|
|
public:
|
|
|
|
template<typename... Args> RealTimeActor(Args&&... args) : object_(std::forward<Args>(args)...) {}
|
|
|
|
|
|
|
|
forceinline void operator += (const LocalTimeScale &rhs) {
|
|
|
|
if constexpr (multiplier == 1 && divider == 1) {
|
|
|
|
object_.run_for(TargetTimeScale(rhs));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (multiplier == 1) {
|
|
|
|
accumulated_time_ += rhs;
|
|
|
|
} else {
|
|
|
|
accumulated_time_ += rhs * multiplier;
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (divider == 1) {
|
|
|
|
const auto duration = accumulated_time_.template flush<TargetTimeScale>();
|
|
|
|
object_.run_for(duration);
|
|
|
|
} else {
|
|
|
|
const auto duration = accumulated_time_.template divide<TargetTimeScale>(LocalTimeScale(divider));
|
|
|
|
if(duration > TargetTimeScale(0))
|
|
|
|
object_.run_for(duration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
forceinline T *operator->() { return &object_; }
|
|
|
|
forceinline const T *operator->() const { return &object_; }
|
|
|
|
forceinline T *last_valid() { return &object_; }
|
|
|
|
forceinline void flush() {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
T object_;
|
|
|
|
LocalTimeScale accumulated_time_;
|
|
|
|
};
|
|
|
|
|
2019-07-29 01:49:54 +00:00
|
|
|
/*!
|
2019-07-29 20:38:57 +00:00
|
|
|
A AsyncJustInTimeActor acts like a JustInTimeActor but additionally contains an AsyncTaskQueue.
|
2019-07-29 01:49:54 +00:00
|
|
|
Any time the amount of accumulated time crosses a threshold provided at construction time,
|
|
|
|
the object will be updated on the AsyncTaskQueue.
|
|
|
|
*/
|
2019-08-08 01:28:02 +00:00
|
|
|
template <class T, class LocalTimeScale = HalfCycles, class TargetTimeScale = LocalTimeScale> class AsyncJustInTimeActor {
|
2019-07-29 20:38:57 +00:00
|
|
|
public:
|
|
|
|
/// Constructs a new AsyncJustInTimeActor using the same construction arguments as the included object.
|
|
|
|
template<typename... Args> AsyncJustInTimeActor(TargetTimeScale threshold, Args&&... args) :
|
|
|
|
object_(std::forward<Args>(args)...),
|
|
|
|
threshold_(threshold) {}
|
2019-07-29 01:49:54 +00:00
|
|
|
|
2019-07-29 20:38:57 +00:00
|
|
|
/// Adds time to the actor.
|
|
|
|
inline void operator += (const LocalTimeScale &rhs) {
|
|
|
|
time_since_update_ += rhs;
|
|
|
|
if(time_since_update_ >= threshold_) {
|
|
|
|
time_since_update_ -= threshold_;
|
|
|
|
task_queue_.enqueue([this] () {
|
|
|
|
object_.run_for(threshold_);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
is_flushed_ = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Flushes all accumulated time and returns a pointer to the included object.
|
|
|
|
inline T *operator->() {
|
|
|
|
flush();
|
|
|
|
return &object_;
|
|
|
|
}
|
|
|
|
|
2019-07-29 21:17:04 +00:00
|
|
|
/// Returns a pointer to the included object without flushing time.
|
|
|
|
inline T *last_valid() {
|
|
|
|
return &object_;
|
|
|
|
}
|
|
|
|
|
2019-07-29 20:38:57 +00:00
|
|
|
/// Flushes all accumulated time.
|
|
|
|
inline void flush() {
|
|
|
|
if(!is_flushed_) {
|
|
|
|
task_queue_.flush();
|
|
|
|
object_.run_for(time_since_update_.template flush<TargetTimeScale>());
|
|
|
|
is_flushed_ = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
T object_;
|
|
|
|
LocalTimeScale time_since_update_;
|
|
|
|
TargetTimeScale threshold_;
|
|
|
|
bool is_flushed_ = true;
|
|
|
|
Concurrency::AsyncTaskQueue task_queue_;
|
2019-07-29 01:49:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* JustInTime_h */
|