From 01a309909b60ced2981c6788108de9595223fa19 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 7 Jul 2022 11:10:54 -0400 Subject: [PATCH] Elide actions when running behind. --- Concurrency/AsyncUpdater.hpp | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/Concurrency/AsyncUpdater.hpp b/Concurrency/AsyncUpdater.hpp index 5596a1dcd..700bd7951 100644 --- a/Concurrency/AsyncUpdater.hpp +++ b/Concurrency/AsyncUpdater.hpp @@ -19,19 +19,21 @@ namespace Concurrency { template class AsyncUpdater { public: - template AsyncUpdater(Args&&... args) - : performer_(std::forward(args)...), + template AsyncUpdater(Args&&... args) : + performer_(std::forward(args)...), + actions_(std::make_unique()), performer_thread_{ [this] { Time::Nanos last_fired = Time::nanos_now(); + auto actions = std::make_unique(); while(!should_quit) { - // Wait for a new action to be signalled, and grab it. + // Wait for new actions to be signalled, and grab them. std::unique_lock lock(condition_mutex_); - while(actions_.empty()) { + while(!actions_) { condition_.wait(lock); } - auto action = actions_.pop_back(); + std::swap(actions, actions_); lock.unlock(); // Update to now. @@ -39,8 +41,11 @@ template class AsyncUpdater { performer_.perform(time_now - last_fired); last_fired = time_now; - // Perform the action. - action(); + // Perform the actions. + for(const auto& action: *actions) { + action(); + } + actions->clear(); } } } {} @@ -49,9 +54,11 @@ template class AsyncUpdater { /// /// @c post_action will be performed asynchronously, on the same /// thread as the performer. + /// + /// Actions may be elided, void update(const std::function &post_action) { std::lock_guard guard(condition_mutex_); - actions_.push_back(post_action); + actions_->push_back(post_action); condition_.notify_all(); } @@ -62,12 +69,18 @@ template class AsyncUpdater { } private: + // The object that will actually receive time advances. Performer performer_; + // The list of actions waiting be performed. These will be elided, + // increasing their latency, if the emulation thread falls behind. + using ActionVector = std::vector>; + std::unique_ptr actions_; + + // Necessary synchronisation parts. std::thread performer_thread_; std::mutex condition_mutex_; std::condition_variable condition_; - std::vector> actions_; std::atomic should_quit = false; };