1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 03:32:32 +00:00

Merge pull request #895 from TomHarte/JITSleeper

Works `ClockingHint` logic into `JustInTimeActor`.
This commit is contained in:
Thomas Harte 2021-04-04 17:11:05 -04:00 committed by GitHub
commit 9ff392279a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 61 deletions

View File

@ -10,6 +10,7 @@
#define JustInTime_h #define JustInTime_h
#include "../Concurrency/AsyncTaskQueue.hpp" #include "../Concurrency/AsyncTaskQueue.hpp"
#include "ClockingHintSource.hpp"
#include "ForceInline.hpp" #include "ForceInline.hpp"
/*! /*!
@ -24,29 +25,69 @@
If the held object implements get_next_sequence_point() then it'll be used to flush implicitly If the held object implements get_next_sequence_point() then it'll be used to flush implicitly
as and when sequence points are hit. Callers can use will_flush() to predict these. as and when sequence points are hit. Callers can use will_flush() to predict these.
If the held object is a subclass of ClockingHint::Source, this template will register as an
observer and potentially stop clocking or stop delaying clocking until just-in-time references
as directed.
TODO: incorporate and codify AsyncJustInTimeActor.
*/ */
template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = HalfCycles, class TargetTimeScale = LocalTimeScale> class JustInTimeActor { template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = HalfCycles> class JustInTimeActor:
public ClockingHint::Observer {
private: private:
/*!
A std::unique_ptr deleter which causes an update_sequence_point to occur on the actor supplied
to it at construction if it implements get_next_sequence_point(). Otherwise destruction is a no-op.
**Does not delete the object.**
This is used by the -> operators below, which provide a unique pointer to the enclosed object and
update their sequence points upon its destruction i.e. after the caller has made whatever call
or calls as were relevant to the enclosed object.
*/
class SequencePointAwareDeleter { class SequencePointAwareDeleter {
public: public:
explicit SequencePointAwareDeleter(JustInTimeActor<T, multiplier, divider, LocalTimeScale, TargetTimeScale> *actor) : actor_(actor) {} explicit SequencePointAwareDeleter(JustInTimeActor<T, multiplier, divider, LocalTimeScale> *actor) noexcept
: actor_(actor) {}
void operator ()(const T *const) const { forceinline void operator ()(const T *const) const {
if constexpr (has_sequence_points<T>::value) { if constexpr (has_sequence_points<T>::value) {
actor_->update_sequence_point(); actor_->update_sequence_point();
} }
} }
private: private:
JustInTimeActor<T, multiplier, divider, LocalTimeScale, TargetTimeScale> *const actor_; JustInTimeActor<T, multiplier, divider, LocalTimeScale> *const actor_;
}; };
// This block of SFINAE determines whether objects of type T accepts Cycles or HalfCycles.
using HalfRunFor = void (T::*const)(HalfCycles);
static uint8_t half_sig(...);
static uint16_t half_sig(HalfRunFor);
using TargetTimeScale =
std::conditional_t<
sizeof(half_sig(&T::run_for)) == sizeof(uint16_t),
HalfCycles,
Cycles>;
public: public:
/// Constructs a new JustInTimeActor using the same construction arguments as the included object. /// Constructs a new JustInTimeActor using the same construction arguments as the included object.
template<typename... Args> JustInTimeActor(Args&&... args) : object_(std::forward<Args>(args)...) {} template<typename... Args> JustInTimeActor(Args&&... args) : object_(std::forward<Args>(args)...) {
if constexpr (std::is_base_of<ClockingHint::Source, T>::value) {
object_.set_clocking_hint_observer(this);
}
}
/// Adds time to the actor. /// Adds time to the actor.
forceinline void operator += (LocalTimeScale rhs) { ///
/// @returns @c true if adding time caused a flush; @c false otherwise.
forceinline bool operator += (LocalTimeScale rhs) {
if constexpr (std::is_base_of<ClockingHint::Source, T>::value) {
if(clocking_preference_ == ClockingHint::Preference::None) {
return false;
}
}
if constexpr (multiplier != 1) { if constexpr (multiplier != 1) {
time_since_update_ += rhs * multiplier; time_since_update_ += rhs * multiplier;
} else { } else {
@ -54,20 +95,29 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
} }
is_flushed_ = false; is_flushed_ = false;
if constexpr (std::is_base_of<ClockingHint::Source, T>::value) {
if (clocking_preference_ == ClockingHint::Preference::RealTime) {
flush();
return true;
}
}
if constexpr (has_sequence_points<T>::value) { if constexpr (has_sequence_points<T>::value) {
time_until_event_ -= rhs; time_until_event_ -= rhs;
if(time_until_event_ <= LocalTimeScale(0)) { if(time_until_event_ <= LocalTimeScale(0)) {
flush(); flush();
update_sequence_point(); return true;
} }
} }
return false;
} }
/// Flushes all accumulated time and returns a pointer to the included object. /// Flushes all accumulated time and returns a pointer to the included object.
/// ///
/// If this object provides sequence points, checks for changes to the next /// If this object provides sequence points, checks for changes to the next
/// sequence point upon deletion of the pointer. /// sequence point upon deletion of the pointer.
forceinline auto operator->() { [[nodiscard]] forceinline auto operator->() {
flush(); flush();
return std::unique_ptr<T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(this)); return std::unique_ptr<T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(this));
} }
@ -75,19 +125,19 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
/// Acts exactly as per the standard ->, but preserves constness. /// Acts exactly as per the standard ->, but preserves constness.
/// ///
/// Despite being const, this will flush the object and, if relevant, update the next sequence point. /// Despite being const, this will flush the object and, if relevant, update the next sequence point.
forceinline auto operator -> () const { [[nodiscard]] forceinline auto operator -> () const {
auto non_const_this = const_cast<JustInTimeActor<T, multiplier, divider, LocalTimeScale, TargetTimeScale> *>(this); auto non_const_this = const_cast<JustInTimeActor<T, multiplier, divider, LocalTimeScale> *>(this);
non_const_this->flush(); non_const_this->flush();
return std::unique_ptr<const T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(non_const_this)); return std::unique_ptr<const T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(non_const_this));
} }
/// @returns a pointer to the included object, without flushing time. /// @returns a pointer to the included object, without flushing time.
forceinline T *last_valid() { [[nodiscard]] forceinline T *last_valid() {
return &object_; return &object_;
} }
/// @returns the amount of time since the object was last flushed, in the target time scale. /// @returns the amount of time since the object was last flushed, in the target time scale.
forceinline TargetTimeScale time_since_flush() const { [[nodiscard]] forceinline TargetTimeScale time_since_flush() const {
// TODO: does this handle conversions properly where TargetTimeScale != LocalTimeScale? // TODO: does this handle conversions properly where TargetTimeScale != LocalTimeScale?
if constexpr (divider == 1) { if constexpr (divider == 1) {
return time_since_update_; return time_since_update_;
@ -113,7 +163,7 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
} }
/// Indicates whether a flush has occurred since the last call to did_flush(). /// Indicates whether a flush has occurred since the last call to did_flush().
forceinline bool did_flush() { [[nodiscard]] forceinline bool did_flush() {
const bool did_flush = did_flush_; const bool did_flush = did_flush_;
did_flush_ = false; did_flush_ = false;
return did_flush; return did_flush;
@ -121,12 +171,12 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
/// @returns the number of cycles until the next sequence-point-based flush, if the embedded object /// @returns the number of cycles until the next sequence-point-based flush, if the embedded object
/// supports sequence points; @c LocalTimeScale() otherwise. /// supports sequence points; @c LocalTimeScale() otherwise.
LocalTimeScale cycles_until_implicit_flush() const { [[nodiscard]] LocalTimeScale cycles_until_implicit_flush() const {
return time_until_event_; return time_until_event_;
} }
/// Indicates whether a sequence-point-caused flush will occur if the specified period is added. /// Indicates whether a sequence-point-caused flush will occur if the specified period is added.
forceinline bool will_flush(LocalTimeScale rhs) const { [[nodiscard]] forceinline bool will_flush(LocalTimeScale rhs) const {
if constexpr (!has_sequence_points<T>::value) { if constexpr (!has_sequence_points<T>::value) {
return false; return false;
} }
@ -149,52 +199,15 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
template <typename S, typename = void> struct has_sequence_points : std::false_type {}; template <typename S, typename = void> struct has_sequence_points : std::false_type {};
template <typename S> struct has_sequence_points<S, decltype(void(std::declval<S &>().get_next_sequence_point()))> : std::true_type {}; template <typename S> struct has_sequence_points<S, decltype(void(std::declval<S &>().get_next_sequence_point()))> : std::true_type {};
ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::JustInTime;
void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference clocking) {
clocking_preference_ = clocking;
}
}; };
/*! /*!
A RealTimeActor presents the same interface as a JustInTimeActor but doesn't defer work. An AsyncJustInTimeActor acts like a JustInTimeActor but additionally contains an AsyncTaskQueue.
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_;
};
/*!
A AsyncJustInTimeActor acts like a JustInTimeActor but additionally contains an AsyncTaskQueue.
Any time the amount of accumulated time crosses a threshold provided at construction time, Any time the amount of accumulated time crosses a threshold provided at construction time,
the object will be updated on the AsyncTaskQueue. the object will be updated on the AsyncTaskQueue.
*/ */

View File

@ -647,7 +647,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
return mouse_; return mouse_;
} }
using IWMActor = JustInTimeActor<IWM, 1, 1, HalfCycles, Cycles>; using IWMActor = JustInTimeActor<IWM>;
class VIAPortHandler: public MOS::MOS6522::PortHandler { class VIAPortHandler: public MOS::MOS6522::PortHandler {
public: public:

View File

@ -85,7 +85,6 @@ template<Model model> class ConcreteMachine:
memcpy(rom_.data(), roms[0]->data(), std::min(rom_.size(), roms[0]->size())); memcpy(rom_.data(), roms[0]->data(), std::min(rom_.size(), roms[0]->size()));
// Register for sleeping notifications. // Register for sleeping notifications.
fdc_->set_clocking_hint_observer(this);
tape_player_.set_clocking_hint_observer(this); tape_player_.set_clocking_hint_observer(this);
// Set up initial memory map. // Set up initial memory map.
@ -384,7 +383,7 @@ template<Model model> class ConcreteMachine:
} }
if constexpr (model == Model::Plus3) { if constexpr (model == Model::Plus3) {
if(!fdc_is_sleeping_) fdc_ += Cycles(duration.as_integral()); fdc_ += Cycles(duration.as_integral());
} }
if(typer_) typer_->run_for(duration); if(typer_) typer_->run_for(duration);
@ -449,7 +448,6 @@ template<Model model> class ConcreteMachine:
// MARK: - ClockingHint::Observer. // MARK: - ClockingHint::Observer.
void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) override { void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) override {
fdc_is_sleeping_ = fdc_.last_valid()->preferred_clocking() == ClockingHint::Preference::None;
tape_player_is_sleeping_ = tape_player_.preferred_clocking() == ClockingHint::Preference::None; tape_player_is_sleeping_ = tape_player_.preferred_clocking() == ClockingHint::Preference::None;
} }
@ -667,7 +665,6 @@ template<Model model> class ConcreteMachine:
// MARK: - Disc. // MARK: - Disc.
JustInTimeActor<Amstrad::FDC, 1, 1, Cycles> fdc_; JustInTimeActor<Amstrad::FDC, 1, 1, Cycles> fdc_;
bool fdc_is_sleeping_ = false;
// MARK: - Automatic startup. // MARK: - Automatic startup.
Cycles duration_to_press_enter_; Cycles duration_to_press_enter_;