From aedef4f60bcd8fa63dda0e51ef00b4931647c4fa Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Tue, 1 Sep 2026 08:51:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frame_policy/Throttled_Latest_Only.cpp | 361 ++++++++++++++++++ .../frame_policy/Throttled_Latest_Only.hpp | 41 ++ .../frame_policy/Throttled_Latest_Only.ipp | 52 +++ .../detail/Throttled_Latest_Only_State.cpp | 354 ----------------- .../detail/Throttled_Latest_Only_State.hpp | 36 -- .../detail/Throttled_Latest_Only_State.ipp | 34 -- .../include/function/frame_policy/global.cpp | 39 +- .../include/function/frame_policy/global.hpp | 36 -- .../include/function/frame_policy/global.ipp | 11 - kernel/kernel/include/model/Model.hpp | 5 +- kernel/kernel/include/model/Model.ipp | 16 +- .../Throttled_Latest_Only_Tests.cpp | 36 +- 12 files changed, 489 insertions(+), 532 deletions(-) create mode 100644 kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.cpp create mode 100644 kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.hpp create mode 100644 kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.ipp delete mode 100644 kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.cpp delete mode 100644 kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.hpp delete mode 100644 kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.ipp diff --git a/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.cpp b/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.cpp new file mode 100644 index 0000000..6295d18 --- /dev/null +++ b/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.cpp @@ -0,0 +1,361 @@ +#include "Throttled_Latest_Only.hpp" + +#include +#include +#include +#include +#include +#include + +namespace aethera { +namespace { +template +struct Scope_Exit { + explicit Scope_Exit(Function function) : + function(std::move(function)) {} + + ~Scope_Exit() noexcept { + if (active) { + function(); + } + } + + Scope_Exit(const Scope_Exit&) = delete; + Scope_Exit& operator=(const Scope_Exit&) = delete; + + void release() noexcept { + active = false; + } + + Function function; /* 离开当前作用域时执行的异常安全清理。 */ + bool active{true}; /* release 后不再执行清理。 */ +}; + +std::expected +frame_interval(double frames_per_second) noexcept { + using Start_Result = Throttled_Latest_only::Start_Result; + if (!std::isfinite(frames_per_second) || frames_per_second <= 0.0) { + return std::unexpected(Start_Result::invalid_frames_per_second); + } + + constexpr long double Nanoseconds_Per_Second = 1'000'000'000.0L; + const long double interval = + Nanoseconds_Per_Second / + static_cast(frames_per_second); + constexpr auto Maximum_Interval = static_cast( + std::numeric_limits::max()); + if (!std::isfinite(interval) || interval > Maximum_Interval) { + return std::unexpected(Start_Result::interval_out_of_range); + } + return std::chrono::nanoseconds{ + static_cast( + std::max(1.0L, std::ceil(interval)))}; +} +} + +Throttled_Latest_only::Throttled_Latest_only( + proxy timer_service, + proxy scene, + proxy sink) : + Def(std::move(timer_service), std::move(scene), std::move(sink)) {} + +Throttled_Latest_only::~Throttled_Latest_only() noexcept { + if (!d->destructible()) { + std::terminate(); + } +} + +Throttled_Latest_only::Start_Result +Throttled_Latest_only::start(double frames_per_second) { + return d->start(frames_per_second); +} + +Throttled_Latest_only::Stop_Result +Throttled_Latest_only::stop(Stop_Completion completion) { + return d->stop(std::move(completion)); +} + +Throttled_Latest_only::Private::Private( + proxy timer_service, + proxy scene, + proxy sink) : + timer_service(std::move(timer_service)), + scene(std::move(scene)), + sink(std::move(sink)) {} + +Throttled_Latest_only::Start_Result +Throttled_Latest_only::Private::start(double frames_per_second) { + const auto interval = frame_interval(frames_per_second); + if (!interval) { + return interval.error(); + } + + const auto prepared = prepare_start(); + if (prepared != Start_Result::started) { + return prepared; + } + + Timer_Id scheduled_timer_id{}; + try { + scheduled_timer_id = timer_service->schedule_every( + *interval, + [this] { + frame_due(); + }); + } + catch (...) { + start_failed(); + throw; + } + timer_started(scheduled_timer_id); + return Start_Result::started; +} + +Throttled_Latest_only::Stop_Result +Throttled_Latest_only::Private::stop(Stop_Completion completion) { + if (!completion) { + return Stop_Result::completion_missing; + } + + Timer_Id cancelled_timer_id{}; + { + std::lock_guard lock(mutex); + switch (phase) { + case Phase::stopped: + return Stop_Result::already_stopped; + case Phase::starting: + return Stop_Result::start_in_progress; + case Phase::stopping: + return Stop_Result::already_stopping; + case Phase::running: + break; + } + if (!timer_id) { + std::terminate(); + } + cancelled_timer_id = *timer_id; + phase = Phase::stopping; + stop_completion = std::move(completion); + timer_cancel_confirmed = false; + } + + try { + timer_service->cancel( + cancelled_timer_id, + [this] { + timer_cancelled(); + }); + } + catch (...) { + cancel_failed(); + throw; + } + return Stop_Result::stopping; +} + +Throttled_Latest_only::Start_Result +Throttled_Latest_only::Private::prepare_start() { + { + std::lock_guard lock(mutex); + switch (phase) { + case Phase::running: + return Start_Result::already_running; + case Phase::starting: + return Start_Result::start_in_progress; + case Phase::stopping: + return Start_Result::stop_in_progress; + case Phase::stopped: + break; + } + if (!timer_service || !scene || !sink) { + return Start_Result::dependency_unavailable; + } + phase = Phase::starting; + } + + Scope_Exit rollback([this] { + std::lock_guard lock(mutex); + if (phase == Phase::starting) { + phase = Phase::stopped; + } + }); + auto created_frame = scene->create_frame(); + if (!created_frame) { + return Start_Result::frame_unavailable; + } + + { + std::lock_guard lock(mutex); + if (phase != Phase::starting || frame) { + std::terminate(); + } + frame = std::move(created_frame); + frame_phase = Frame_Phase::idle; + timer_cancel_confirmed = false; + } + rollback.release(); + return Start_Result::started; +} + +void Throttled_Latest_only::Private::timer_started(Timer_Id id) { + std::lock_guard lock(mutex); + if (id == 0 || + phase != Phase::starting || + !frame || + timer_id.has_value()) { + std::terminate(); + } + timer_id = id; + phase = Phase::running; +} + +void Throttled_Latest_only::Private::start_failed() { + proxy retired_frame; + { + std::lock_guard lock(mutex); + if (phase != Phase::starting || timer_id) { + std::terminate(); + } + retired_frame = std::move(frame); + frame_phase = Frame_Phase::idle; + phase = Phase::stopped; + } + retired_frame.reset(); +} + +void Throttled_Latest_only::Private::cancel_failed() { + std::lock_guard lock(mutex); + if (phase != Phase::stopping || timer_cancel_confirmed) { + std::terminate(); + } + stop_completion = {}; + phase = Phase::running; +} + +void Throttled_Latest_only::Private::timer_cancelled() { + { + std::lock_guard lock(mutex); + if (phase != Phase::stopping || timer_cancel_confirmed) { + std::terminate(); + } + timer_id.reset(); + timer_cancel_confirmed = true; + } + finish_stop_if_ready(); +} + +void Throttled_Latest_only::Private::frame_due() { + { + std::lock_guard lock(mutex); + if (phase != Phase::running || frame_phase != Frame_Phase::idle) { + return; + } + frame_phase = Frame_Phase::rendering; + } + + try { + scene->render( + frame, + [this](proxy& completed_frame) { + rendered(completed_frame); + }); + } + catch (...) { + abandon_render(); + throw; + } +} + +void Throttled_Latest_only::Private::rendered( + proxy& completed_frame) { + { + std::lock_guard lock(mutex); + if (std::addressof(completed_frame) != std::addressof(frame) || + frame_phase != Frame_Phase::rendering) { + std::terminate(); + } + frame_phase = Frame_Phase::sending; + } + + try { + sink->send( + frame, + [this](proxy& sent_frame) { + sent(sent_frame); + }); + } + catch (...) { + abandon_send(); + throw; + } +} + +void Throttled_Latest_only::Private::sent( + proxy& completed_frame) { + { + std::lock_guard lock(mutex); + if (std::addressof(completed_frame) != std::addressof(frame) || + frame_phase != Frame_Phase::sending) { + std::terminate(); + } + frame_phase = Frame_Phase::idle; + } + finish_stop_if_ready(); +} + +void Throttled_Latest_only::Private::abandon_render() { + { + std::lock_guard lock(mutex); + if (frame_phase != Frame_Phase::rendering) { + return; + } + frame_phase = Frame_Phase::idle; + } + finish_stop_if_ready(); +} + +void Throttled_Latest_only::Private::abandon_send() { + { + std::lock_guard lock(mutex); + if (frame_phase != Frame_Phase::sending) { + return; + } + frame_phase = Frame_Phase::idle; + } + finish_stop_if_ready(); +} + +void Throttled_Latest_only::Private::finish_stop_if_ready() { + proxy retired_frame; + Stop_Completion completion; + { + std::lock_guard lock(mutex); + if (phase != Phase::stopping || + !timer_cancel_confirmed || + frame_phase != Frame_Phase::idle) { + return; + } + retired_frame = std::move(frame); + completion = std::move(stop_completion); + timer_cancel_confirmed = false; + phase = Phase::stopped; + } + + retired_frame.reset(); + try { + completion(); + } + catch (...) { + std::terminate(); + } +} + +bool Throttled_Latest_only::Private::destructible() const noexcept { + std::lock_guard lock(mutex); + return phase == Phase::stopped && + frame_phase == Frame_Phase::idle && + !frame && + !timer_id && + !stop_completion; +} +} diff --git a/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.hpp b/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.hpp new file mode 100644 index 0000000..6d64ae9 --- /dev/null +++ b/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.hpp @@ -0,0 +1,41 @@ +#pragma once +#include "function/frame_policy/global.hpp" +#include "model/Model.hpp" +#include +#include +namespace aethera { +/* + * 固定帧率、最多一帧在途的策略。Scene 创建具体帧;策略持有并重复使用, + * stop completion 执行前会排空 Scene/Sink 借用并销毁该帧。 + */ +struct Throttled_Latest_only : +Def> { + enum struct Start_Result : std::uint8_t { + started, + already_running, + start_in_progress, + stop_in_progress, + dependency_unavailable, + frame_unavailable, + invalid_frames_per_second, + interval_out_of_range + }; + enum struct Stop_Result : std::uint8_t { + stopping, + already_stopped, + already_stopping, + start_in_progress, + completion_missing + }; + using Stop_Completion = std::function; + struct Prop : Prev_Prop {}; + struct Private; + Throttled_Latest_only(proxy timer_service, + proxy scene, + proxy sink); + ~Throttled_Latest_only() noexcept; + Start_Result start(double frames_per_second); + Stop_Result stop(Stop_Completion completion); +}; +} +#include "Throttled_Latest_Only.ipp" diff --git a/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.ipp b/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.ipp new file mode 100644 index 0000000..53a2da2 --- /dev/null +++ b/kernel/kernel/include/function/frame_policy/Throttled_Latest_Only.ipp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +namespace aethera { +struct Throttled_Latest_only::Private : Prev_Private { + enum struct Phase : std::uint8_t { + stopped, + starting, + running, + stopping + }; + enum struct Frame_Phase : std::uint8_t { + idle, + rendering, + sending + }; + + Private(proxy timer_service, + proxy scene, + proxy sink); + + Start_Result start(double frames_per_second); + Stop_Result stop(Stop_Completion completion); + [[nodiscard]] bool destructible() const noexcept; + +private: + Start_Result prepare_start(); + void timer_started(Timer_Id id); + void start_failed(); + void cancel_failed(); + void timer_cancelled(); + void frame_due(); + void rendered(proxy& completed_frame); + void sent(proxy& completed_frame); + void abandon_render(); + void abandon_send(); + void finish_stop_if_ready(); + + proxy timer_service; /* 周期调度和异步取消的必填业务能力所有权。 */ + proxy scene; /* 帧创建及渲染借用的必填业务能力所有权。 */ + proxy sink; /* 已渲染帧媒体分派的必填业务能力所有权。 */ + proxy frame; /* 策略创建、复用并在停止完成前销毁的唯一帧。 */ + Stop_Completion stop_completion; /* 本次异步停止的唯一完成回调。 */ + std::optional timer_id; /* running/stopping 阶段的周期计时器标识。 */ + mutable std::mutex mutex; /* 保护生命周期和跨线程帧借用阶段。 */ + Phase phase{Phase::stopped}; /* start/stop 生命周期的唯一权威状态。 */ + Frame_Phase frame_phase{Frame_Phase::idle}; /* 唯一帧当前借用方的权威状态。 */ + bool timer_cancel_confirmed{}; /* stopping 阶段的异步取消确认。 */ +}; +} diff --git a/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.cpp b/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.cpp deleted file mode 100644 index f5c2487..0000000 --- a/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.cpp +++ /dev/null @@ -1,354 +0,0 @@ -#include "Throttled_Latest_Only_State.ipp" - -#include -#include -#include -#include -#include -#include - -namespace aethera::detail { -namespace { -template -struct Scope_Exit { - explicit Scope_Exit(Function function) : - function(std::move(function)) {} - - ~Scope_Exit() noexcept { - if (active) { - function(); - } - } - - Scope_Exit(const Scope_Exit&) = delete; - Scope_Exit& operator=(const Scope_Exit&) = delete; - - void release() noexcept { - active = false; - } - - Function function; /* 离开当前作用域时执行的异常安全清理。 */ - bool active{true}; /* release 后不再执行清理。 */ -}; - -std::expected< - std::chrono::nanoseconds, - Start_Throttled_Latest_Only_Result> -frame_interval(double frames_per_second) noexcept { - if (!std::isfinite(frames_per_second) || frames_per_second <= 0.0) { - return std::unexpected( - Start_Throttled_Latest_Only_Result::invalid_frames_per_second); - } - - constexpr long double Nanoseconds_Per_Second = 1'000'000'000.0L; - const long double interval = - Nanoseconds_Per_Second / - static_cast(frames_per_second); - constexpr auto Maximum_Interval = static_cast( - std::numeric_limits::max()); - if (!std::isfinite(interval) || interval > Maximum_Interval) { - return std::unexpected( - Start_Throttled_Latest_Only_Result::interval_out_of_range); - } - return std::chrono::nanoseconds{ - static_cast( - std::max(1.0L, std::ceil(interval)))}; -} -} - -Throttled_Latest_Only_State::Throttled_Latest_Only_State( - proxy timer_service, - proxy scene, - proxy sink) : - d(std::make_unique()) { - d->timer_service = std::move(timer_service); - d->scene = std::move(scene); - d->sink = std::move(sink); -} - -Throttled_Latest_Only_State::~Throttled_Latest_Only_State() = default; - -Start_Throttled_Latest_Only_Result -Throttled_Latest_Only_State::start(double frames_per_second) { - const auto interval = frame_interval(frames_per_second); - if (!interval) { - return interval.error(); - } - - const auto prepared = prepare_start(); - if (prepared != Start_Throttled_Latest_Only_Result::started) { - return prepared; - } - - Timer_Id timer_id{}; - try { - auto self = shared_from_this(); - timer_id = d->timer_service->schedule_every( - *interval, - [self = std::move(self)] { - self->frame_due(); - }); - } - catch (...) { - start_failed(); - throw; - } - timer_started(timer_id); - return Start_Throttled_Latest_Only_Result::started; -} - -Stop_Throttled_Latest_Only_Result -Throttled_Latest_Only_State::stop( - Throttled_Latest_Only_Stop_Completion completion) { - if (!completion) { - return Stop_Throttled_Latest_Only_Result::completion_missing; - } - - Timer_Id timer_id{}; - { - std::lock_guard lock(d->mutex); - switch (d->phase) { - case Private::Phase::stopped: - return Stop_Throttled_Latest_Only_Result::already_stopped; - case Private::Phase::starting: - return Stop_Throttled_Latest_Only_Result::start_in_progress; - case Private::Phase::stopping: - return Stop_Throttled_Latest_Only_Result::already_stopping; - case Private::Phase::running: - break; - } - if (!d->timer_id) { - std::terminate(); - } - timer_id = *d->timer_id; - d->phase = Private::Phase::stopping; - d->stop_completion = std::move(completion); - d->timer_cancelled = false; - } - - try { - auto self = shared_from_this(); - d->timer_service->cancel( - timer_id, - [self = std::move(self)] { - self->timer_cancelled(); - }); - } - catch (...) { - cancel_failed(); - throw; - } - return Stop_Throttled_Latest_Only_Result::stopping; -} - -Start_Throttled_Latest_Only_Result -Throttled_Latest_Only_State::prepare_start() { - { - std::lock_guard lock(d->mutex); - switch (d->phase) { - case Private::Phase::running: - return Start_Throttled_Latest_Only_Result::already_running; - case Private::Phase::starting: - return Start_Throttled_Latest_Only_Result::start_in_progress; - case Private::Phase::stopping: - return Start_Throttled_Latest_Only_Result::stop_in_progress; - case Private::Phase::stopped: - break; - } - if (!d->timer_service || !d->scene || !d->sink) { - return Start_Throttled_Latest_Only_Result::dependency_unavailable; - } - d->phase = Private::Phase::starting; - } - - Scope_Exit rollback([this] { - std::lock_guard lock(d->mutex); - if (d->phase == Private::Phase::starting) { - d->phase = Private::Phase::stopped; - } - }); - auto frame = d->scene->create_frame(); - if (!frame) { - return Start_Throttled_Latest_Only_Result::frame_unavailable; - } - - { - std::lock_guard lock(d->mutex); - if (d->phase != Private::Phase::starting || d->frame) { - std::terminate(); - } - d->frame = std::move(frame); - d->frame_phase = Private::Frame_Phase::idle; - d->timer_cancelled = false; - } - rollback.release(); - return Start_Throttled_Latest_Only_Result::started; -} - -void Throttled_Latest_Only_State::timer_started(Timer_Id id) { - std::lock_guard lock(d->mutex); - if (id == 0 || - d->phase != Private::Phase::starting || - !d->frame || - d->timer_id.has_value()) { - std::terminate(); - } - d->timer_id = id; - d->phase = Private::Phase::running; -} - -void Throttled_Latest_Only_State::start_failed() { - proxy retired_frame; - { - std::lock_guard lock(d->mutex); - if (d->phase != Private::Phase::starting || d->timer_id) { - std::terminate(); - } - retired_frame = std::move(d->frame); - d->frame_phase = Private::Frame_Phase::idle; - d->phase = Private::Phase::stopped; - } - retired_frame.reset(); -} - -void Throttled_Latest_Only_State::cancel_failed() { - std::lock_guard lock(d->mutex); - if (d->phase != Private::Phase::stopping || d->timer_cancelled) { - std::terminate(); - } - d->stop_completion = {}; - d->phase = Private::Phase::running; -} - -void Throttled_Latest_Only_State::timer_cancelled() { - { - std::lock_guard lock(d->mutex); - if (d->phase != Private::Phase::stopping || d->timer_cancelled) { - std::terminate(); - } - d->timer_id.reset(); - d->timer_cancelled = true; - } - finish_stop_if_ready(); -} - -void Throttled_Latest_Only_State::frame_due() { - { - std::lock_guard lock(d->mutex); - if (d->phase != Private::Phase::running || - d->frame_phase != Private::Frame_Phase::idle) { - return; - } - d->frame_phase = Private::Frame_Phase::rendering; - } - - auto self = shared_from_this(); - try { - d->scene->render( - d->frame, - [self = std::move(self)](proxy& frame) { - self->rendered(frame); - }); - } - catch (...) { - abandon_render(); - throw; - } -} - -void Throttled_Latest_Only_State::rendered( - proxy& completed_frame) { - { - std::lock_guard lock(d->mutex); - if (std::addressof(completed_frame) != - std::addressof(d->frame) || - d->frame_phase != Private::Frame_Phase::rendering) { - std::terminate(); - } - d->frame_phase = Private::Frame_Phase::sending; - } - - auto self = shared_from_this(); - try { - d->sink->send( - d->frame, - [self = std::move(self)](proxy& frame) { - self->sent(frame); - }); - } - catch (...) { - abandon_send(); - throw; - } -} - -void Throttled_Latest_Only_State::sent( - proxy& completed_frame) { - { - std::lock_guard lock(d->mutex); - if (std::addressof(completed_frame) != - std::addressof(d->frame) || - d->frame_phase != Private::Frame_Phase::sending) { - std::terminate(); - } - d->frame_phase = Private::Frame_Phase::idle; - } - finish_stop_if_ready(); -} - -void Throttled_Latest_Only_State::abandon_render() { - { - std::lock_guard lock(d->mutex); - if (d->frame_phase != Private::Frame_Phase::rendering) { - return; - } - d->frame_phase = Private::Frame_Phase::idle; - } - finish_stop_if_ready(); -} - -void Throttled_Latest_Only_State::abandon_send() { - { - std::lock_guard lock(d->mutex); - if (d->frame_phase != Private::Frame_Phase::sending) { - return; - } - d->frame_phase = Private::Frame_Phase::idle; - } - finish_stop_if_ready(); -} - -void Throttled_Latest_Only_State::finish_stop_if_ready() { - proxy retired_frame; - Throttled_Latest_Only_Stop_Completion completion; - { - std::lock_guard lock(d->mutex); - if (d->phase != Private::Phase::stopping || - !d->timer_cancelled || - d->frame_phase != Private::Frame_Phase::idle) { - return; - } - retired_frame = std::move(d->frame); - completion = std::move(d->stop_completion); - d->timer_cancelled = false; - d->phase = Private::Phase::stopped; - } - - retired_frame.reset(); - try { - completion(); - } - catch (...) { - std::terminate(); - } -} - -bool Throttled_Latest_Only_State::destructible() const noexcept { - std::lock_guard lock(d->mutex); - return d->phase == Private::Phase::stopped && - d->frame_phase == Private::Frame_Phase::idle && - !d->frame && - !d->timer_id && - !d->stop_completion; -} -} diff --git a/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.hpp b/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.hpp deleted file mode 100644 index d4fc60c..0000000 --- a/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "function/frame_policy/global.hpp" - -#include - -namespace aethera::detail { -struct Throttled_Latest_Only_State : - Non_Copyable, - std::enable_shared_from_this { - Throttled_Latest_Only_State(proxy timer_service, - proxy scene, - proxy sink); - ~Throttled_Latest_Only_State(); - - Start_Throttled_Latest_Only_Result start(double frames_per_second); - Stop_Throttled_Latest_Only_Result stop( - Throttled_Latest_Only_Stop_Completion completion); - [[nodiscard]] bool destructible() const noexcept; -private: - Start_Throttled_Latest_Only_Result prepare_start(); - void timer_started(Timer_Id id); - void start_failed(); - void cancel_failed(); - void timer_cancelled(); - void frame_due(); - void rendered(proxy& completed_frame); - void sent(proxy& completed_frame); - void abandon_render(); - void abandon_send(); - void finish_stop_if_ready(); - - struct Private; - std::unique_ptr d; /* 策略异步生命周期和唯一帧状态的所有权。 */ -}; -} diff --git a/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.ipp b/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.ipp deleted file mode 100644 index 3e1ea24..0000000 --- a/kernel/kernel/include/function/frame_policy/detail/Throttled_Latest_Only_State.ipp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "Throttled_Latest_Only_State.hpp" - -#include -#include - -namespace aethera::detail { -struct Throttled_Latest_Only_State::Private { - enum struct Phase : std::uint8_t { - stopped, - starting, - running, - stopping - }; - - enum struct Frame_Phase : std::uint8_t { - idle, - rendering, - sending - }; - - proxy timer_service; /* 周期调度和异步取消的业务能力所有权。 */ - proxy scene; /* 帧类型和渲染借用的业务实现所有权。 */ - proxy sink; /* 当前帧媒体分派的业务实现所有权。 */ - proxy frame; /* 策略创建、复用并在停止完成前销毁的唯一帧。 */ - Throttled_Latest_Only_Stop_Completion stop_completion; /* 本次异步停止的唯一完成回调。 */ - std::optional timer_id; /* running/stopping 阶段的周期计时器标识。 */ - mutable std::mutex mutex; /* 保护生命周期和跨线程帧借用阶段。 */ - Phase phase{Phase::stopped}; /* start/stop 生命周期的唯一权威状态。 */ - Frame_Phase frame_phase{Frame_Phase::idle}; /* 唯一帧当前借用方的权威状态。 */ - bool timer_cancelled{}; /* stopping 阶段的异步取消确认。 */ -}; -} diff --git a/kernel/kernel/include/function/frame_policy/global.cpp b/kernel/kernel/include/function/frame_policy/global.cpp index a752436..5d86027 100644 --- a/kernel/kernel/include/function/frame_policy/global.cpp +++ b/kernel/kernel/include/function/frame_policy/global.cpp @@ -1,41 +1,4 @@ #include "global.ipp" - #include #include - -namespace aethera { -Throttled_Latest_only::Private::Private( - proxy timer_service, - proxy scene, - proxy sink) : - state(std::make_shared( - std::move(timer_service), - std::move(scene), - std::move(sink))) {} - -Throttled_Latest_only::Throttled_Latest_only( - proxy timer_service, - proxy scene, - proxy sink) : - d(std::make_unique( - std::move(timer_service), - std::move(scene), - std::move(sink))) {} - -Throttled_Latest_only::~Throttled_Latest_only() noexcept { - if (!d->state->destructible()) { - std::terminate(); - } -} - -Start_Throttled_Latest_Only_Result -Throttled_Latest_only::start(double frames_per_second) { - return d->state->start(frames_per_second); -} - -Stop_Throttled_Latest_Only_Result -Throttled_Latest_only::stop( - Throttled_Latest_Only_Stop_Completion completion) { - return d->state->stop(std::move(completion)); -} -} +namespace aethera {} diff --git a/kernel/kernel/include/function/frame_policy/global.hpp b/kernel/kernel/include/function/frame_policy/global.hpp index a0bc1aa..04294c2 100644 --- a/kernel/kernel/include/function/frame_policy/global.hpp +++ b/kernel/kernel/include/function/frame_policy/global.hpp @@ -23,40 +23,4 @@ PRO_DEF_MEM_DISPATCH(FP_send, send); struct FP_Sink : facade_builder ::add_convention&, FP_Frame_Completion)> ::build {}; -enum struct Start_Throttled_Latest_Only_Result : std::uint8_t { - started, - already_running, - start_in_progress, - stop_in_progress, - dependency_unavailable, - frame_unavailable, - invalid_frames_per_second, - interval_out_of_range -}; -enum struct Stop_Throttled_Latest_Only_Result : std::uint8_t { - stopping, - already_stopped, - already_stopping, - start_in_progress, - completion_missing -}; -using Throttled_Latest_Only_Stop_Completion = -std::function; -/* - * 固定帧率、最多一帧在途的策略。Scene 创建具体帧;策略持有并重复使用, - * stop completion 执行前会排空 Scene/Sink 借用并销毁该帧。 - */ -struct Throttled_Latest_only : Immovable { - Throttled_Latest_only(proxy timer_service, - proxy scene, - proxy sink); - ~Throttled_Latest_only() noexcept; - Start_Throttled_Latest_Only_Result start( - double frames_per_second); - Stop_Throttled_Latest_Only_Result stop( - Throttled_Latest_Only_Stop_Completion completion); -private: - struct Private; - std::unique_ptr d; /* 策略外壳实现的唯一所有权。 */ -}; } diff --git a/kernel/kernel/include/function/frame_policy/global.ipp b/kernel/kernel/include/function/frame_policy/global.ipp index 04c9e6c..6d3ae94 100644 --- a/kernel/kernel/include/function/frame_policy/global.ipp +++ b/kernel/kernel/include/function/frame_policy/global.ipp @@ -1,14 +1,3 @@ #pragma once #include "global.hpp" -#include "detail/Throttled_Latest_Only_State.hpp" - -namespace aethera { -struct Throttled_Latest_only::Private { - Private(proxy timer_service, - proxy scene, - proxy sink); - - std::shared_ptr state; /* 帧、借用阶段和停止排空的共享所有权。 */ -}; -} diff --git a/kernel/kernel/include/model/Model.hpp b/kernel/kernel/include/model/Model.hpp index 0bf862b..e9fba93 100644 --- a/kernel/kernel/include/model/Model.hpp +++ b/kernel/kernel/include/model/Model.hpp @@ -11,7 +11,8 @@ template struct Def : Base { struct Private; struct Builder { using Prop = typename Self::Prop; - Builder(); + template + explicit Builder(Args&&... args); std::unique_ptr build(); template T> Builder& set_prop(Member Prop::* member, T&& value); std::unique_ptr ret; /* 构建期间唯一拥有尚未发布的对象。 */ @@ -20,6 +21,8 @@ template struct Def : Base { using Prev_Prop = Base::Prop; using Prev_Private = Base::Private; using Prev_Builder = Base::Builder; + template + explicit Def(Args&&... args); std::unique_ptr d; /* 唯一拥有本定义层的并发属性实现。 */ }; } diff --git a/kernel/kernel/include/model/Model.ipp b/kernel/kernel/include/model/Model.ipp index 92eff2a..4ade493 100644 --- a/kernel/kernel/include/model/Model.ipp +++ b/kernel/kernel/include/model/Model.ipp @@ -5,14 +5,22 @@ namespace aethera { template struct Def::Private : Self::Private { + template + explicit Private(Args&&... args) : + Self::Private(std::forward(args)...) {} + Import_Struct_With_Dirty prop; }; template -Def::Builder::Builder() - : ret(std::make_unique()) { - ret->d = std::make_unique(); -} +template +Def::Builder::Builder(Args&&... args) : + ret(std::make_unique(std::forward(args)...)) {} + +template +template +Def::Def(Args&&... args) : + d(std::make_unique(std::forward(args)...)) {} template std::unique_ptr Def::Builder::build() { diff --git a/kernel/kernel/test/function/frame_policy/Throttled_Latest_Only_Tests.cpp b/kernel/kernel/test/function/frame_policy/Throttled_Latest_Only_Tests.cpp index efad52c..f0ee307 100644 --- a/kernel/kernel/test/function/frame_policy/Throttled_Latest_Only_Tests.cpp +++ b/kernel/kernel/test/function/frame_policy/Throttled_Latest_Only_Tests.cpp @@ -1,4 +1,4 @@ -#include "function/frame_policy/global.hpp" +#include "function/frame_policy/Throttled_Latest_Only.hpp" #include @@ -166,10 +166,9 @@ std::unique_ptr make_policy( Test_Timer_Service>(control); auto scene = pro::make_proxy(control); auto sink = pro::make_proxy(control); - return std::make_unique( - std::move(timer_service), - std::move(scene), - std::move(sink)); + aethera::Throttled_Latest_only::Builder builder( + std::move(timer_service), std::move(scene), std::move(sink)); + return builder.build(); } TEST(Throttled_Latest_Only, Reuses_One_Frame_And_Drops_Busy_Timer_Ticks) { @@ -177,7 +176,7 @@ TEST(Throttled_Latest_Only, Reuses_One_Frame_And_Drops_Busy_Timer_Ticks) { auto policy = make_policy(control); ASSERT_EQ( policy->start(50.0), - aethera::Start_Throttled_Latest_Only_Result::started); + aethera::Throttled_Latest_only::Start_Result::started); EXPECT_EQ(control->scheduled_interval, 20ms); EXPECT_EQ(control->frames_created, 1); EXPECT_EQ(control->frames_alive, 1); @@ -204,7 +203,7 @@ TEST(Throttled_Latest_Only, Reuses_One_Frame_And_Drops_Busy_Timer_Ticks) { policy->stop([&stopped] { stopped = true; }), - aethera::Stop_Throttled_Latest_Only_Result::stopping); + aethera::Throttled_Latest_only::Stop_Result::stopping); EXPECT_EQ( control->cancelled_timer_id, Test_Timer_Service::Timer_Id); @@ -220,7 +219,7 @@ TEST(Throttled_Latest_Only, Stop_Completes_After_Timer_And_Frame_Borrows_Return) auto policy = make_policy(control); ASSERT_EQ( policy->start(60.0), - aethera::Start_Throttled_Latest_Only_Result::started); + aethera::Throttled_Latest_only::Start_Result::started); control->fire_timer(); bool stopped = false; @@ -228,7 +227,7 @@ TEST(Throttled_Latest_Only, Stop_Completes_After_Timer_And_Frame_Borrows_Return) policy->stop([&stopped] { stopped = true; }), - aethera::Stop_Throttled_Latest_Only_Result::stopping); + aethera::Throttled_Latest_only::Stop_Result::stopping); control->complete_cancel(); EXPECT_FALSE(stopped); EXPECT_EQ(control->frames_alive, 1); @@ -243,26 +242,27 @@ TEST(Throttled_Latest_Only, Stop_Completes_After_Timer_And_Frame_Borrows_Return) } TEST(Throttled_Latest_Only, Reports_Missing_Dependencies_And_Invalid_Fps) { - aethera::Throttled_Latest_only missing_dependencies( + aethera::Throttled_Latest_only::Builder missing_builder( aethera::proxy{}, aethera::proxy{}, aethera::proxy{}); + auto missing_dependencies = missing_builder.build(); EXPECT_EQ( - missing_dependencies.start(60.0), - aethera::Start_Throttled_Latest_Only_Result::dependency_unavailable); + missing_dependencies->start(60.0), + aethera::Throttled_Latest_only::Start_Result::dependency_unavailable); EXPECT_EQ( - missing_dependencies.stop({}), - aethera::Stop_Throttled_Latest_Only_Result::completion_missing); + missing_dependencies->stop({}), + aethera::Throttled_Latest_only::Stop_Result::completion_missing); auto control = std::make_shared(); auto policy = make_policy(control); EXPECT_EQ( policy->start(0.0), - aethera::Start_Throttled_Latest_Only_Result::invalid_frames_per_second); + aethera::Throttled_Latest_only::Start_Result::invalid_frames_per_second); EXPECT_EQ( policy->start(std::numeric_limits::denorm_min()), - aethera::Start_Throttled_Latest_Only_Result::interval_out_of_range); + aethera::Throttled_Latest_only::Start_Result::interval_out_of_range); } TEST(Throttled_Latest_Only, Synchronous_Completions_And_Failure_Restore_Frame) { @@ -272,7 +272,7 @@ TEST(Throttled_Latest_Only, Synchronous_Completions_And_Failure_Restore_Frame) { auto policy = make_policy(control); ASSERT_EQ( policy->start(60.0), - aethera::Start_Throttled_Latest_Only_Result::started); + aethera::Throttled_Latest_only::Start_Result::started); EXPECT_THROW(control->fire_timer(), std::runtime_error); control->throw_from_send = false; @@ -286,7 +286,7 @@ TEST(Throttled_Latest_Only, Synchronous_Completions_And_Failure_Restore_Frame) { policy->stop([&stopped] { stopped = true; }), - aethera::Stop_Throttled_Latest_Only_Result::stopping); + aethera::Throttled_Latest_only::Stop_Result::stopping); control->complete_cancel(); EXPECT_TRUE(stopped); }