diff --git a/ClockReceiver/JustInTime.hpp b/ClockReceiver/JustInTime.hpp index 6d5a89583..35edfae7f 100644 --- a/ClockReceiver/JustInTime.hpp +++ b/ClockReceiver/JustInTime.hpp @@ -10,6 +10,7 @@ #define JustInTime_h #include "../Concurrency/AsyncTaskQueue.hpp" +#include "ClockingHintSource.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 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 JustInTimeActor { +template class JustInTimeActor: + public ClockingHint::Observer { 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 { public: - explicit SequencePointAwareDeleter(JustInTimeActor *actor) : actor_(actor) {} + explicit SequencePointAwareDeleter(JustInTimeActor *actor) noexcept + : actor_(actor) {} - void operator ()(const T *const) const { + forceinline void operator ()(const T *const) const { if constexpr (has_sequence_points::value) { actor_->update_sequence_point(); } } private: - JustInTimeActor *const actor_; + JustInTimeActor *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: /// Constructs a new JustInTimeActor using the same construction arguments as the included object. - template JustInTimeActor(Args&&... args) : object_(std::forward(args)...) {} + template JustInTimeActor(Args&&... args) : object_(std::forward(args)...) { + if constexpr (std::is_base_of::value) { + object_.set_clocking_hint_observer(this); + } + } /// 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::value) { + if(clocking_preference_ == ClockingHint::Preference::None) { + return false; + } + } + if constexpr (multiplier != 1) { time_since_update_ += rhs * multiplier; } else { @@ -54,20 +95,29 @@ template ::value) { + if (clocking_preference_ == ClockingHint::Preference::RealTime) { + flush(); + return true; + } + } + if constexpr (has_sequence_points::value) { time_until_event_ -= rhs; if(time_until_event_ <= LocalTimeScale(0)) { flush(); - update_sequence_point(); + return true; } } + + return false; } /// Flushes all accumulated time and returns a pointer to the included object. /// /// If this object provides sequence points, checks for changes to the next /// sequence point upon deletion of the pointer. - forceinline auto operator->() { + [[nodiscard]] forceinline auto operator->() { flush(); return std::unique_ptr(&object_, SequencePointAwareDeleter(this)); } @@ -75,19 +125,19 @@ template , but preserves constness. /// /// Despite being const, this will flush the object and, if relevant, update the next sequence point. - forceinline auto operator -> () const { - auto non_const_this = const_cast *>(this); + [[nodiscard]] forceinline auto operator -> () const { + auto non_const_this = const_cast *>(this); non_const_this->flush(); return std::unique_ptr(&object_, SequencePointAwareDeleter(non_const_this)); } /// @returns a pointer to the included object, without flushing time. - forceinline T *last_valid() { + [[nodiscard]] forceinline T *last_valid() { return &object_; } /// @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? if constexpr (divider == 1) { return time_since_update_; @@ -113,7 +163,7 @@ template ::value) { return false; } @@ -149,52 +199,15 @@ template struct has_sequence_points : std::false_type {}; template struct has_sequence_points().get_next_sequence_point()))> : std::true_type {}; -}; -/*! - 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 RealTimeActor { - public: - template RealTimeActor(Args&&... args) : object_(std::forward(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(); - object_.run_for(duration); - } else { - const auto duration = accumulated_time_.template divide(LocalTimeScale(divider)); - if(duration > TargetTimeScale(0)) - object_.run_for(duration); - } + ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::JustInTime; + void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference clocking) { + clocking_preference_ = clocking; } - - 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. + An AsyncJustInTimeActor acts like a JustInTimeActor but additionally contains an AsyncTaskQueue. Any time the amount of accumulated time crosses a threshold provided at construction time, the object will be updated on the AsyncTaskQueue. */ diff --git a/Machines/Apple/AppleIIgs/AppleIIgs.cpp b/Machines/Apple/AppleIIgs/AppleIIgs.cpp index 7c0d8ccc5..218da7d9e 100644 --- a/Machines/Apple/AppleIIgs/AppleIIgs.cpp +++ b/Machines/Apple/AppleIIgs/AppleIIgs.cpp @@ -1093,10 +1093,10 @@ class ConcreteMachine: // MARK: - Other components. Apple::Clock::ParallelClock clock_; - JustInTimeActor video_; // i.e. run video at 7Mhz. - JustInTimeActor adb_glu_; // i.e. 3,579,545Mhz. + JustInTimeActor video_; // i.e. run video at 7Mhz. + JustInTimeActor adb_glu_; // i.e. 3,579,545Mhz. Zilog::SCC::z8530 scc_; - JustInTimeActor iwm_; + JustInTimeActor iwm_; Cycles cycles_since_clock_tick_; Apple::Macintosh::DoubleDensityDrive drives35_[2]; Apple::Disk::DiskIIDrive drives525_[2]; diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 6f16cd1fb..1d8ca3b35 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -647,7 +647,7 @@ template class ConcreteMachin return mouse_; } - using IWMActor = JustInTimeActor; + using IWMActor = JustInTimeActor; class VIAPortHandler: public MOS::MOS6522::PortHandler { public: diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 17800a525..1c3f496e0 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -479,9 +479,9 @@ class ConcreteMachine: JustInTimeActor