// // BestEffortUpdater.cpp // Clock Signal // // Created by Thomas Harte on 04/10/2017. // Copyright 2017 Thomas Harte. All rights reserved. // #include "BestEffortUpdater.hpp" #include using namespace Concurrency; BestEffortUpdater::BestEffortUpdater() : update_thread_([this]() { this->update_loop(); }) {} BestEffortUpdater::~BestEffortUpdater() { // Sever the delegate now, as soon as possible, then wait for any // pending tasks to finish. set_delegate(nullptr); flush(); // Wind up the update thread. should_quit_ = true; update(); update_thread_.join(); } void BestEffortUpdater::update(int flags) { // Bump the requested target time and set the update requested flag. { std::lock_guard lock(update_mutex_); has_skipped_ = update_requested_; update_requested_ = true; flags_ |= flags; target_time_ = std::chrono::high_resolution_clock::now().time_since_epoch().count(); } update_condition_.notify_one(); } void BestEffortUpdater::update_loop() { while(true) { std::unique_lock lock(update_mutex_); is_updating_ = false; // Wait to be signalled. update_condition_.wait(lock, [this]() -> bool { return update_requested_; }); // Possibly this signalling really means 'quit'. if(should_quit_) return; // Note update started, crib the target time. auto target_time = target_time_; update_requested_ = false; // If this was actually the first update request, silently swallow it. if(!has_previous_time_point_) { has_previous_time_point_ = true; previous_time_point_ = target_time; continue; } // Release the lock on requesting new updates. is_updating_ = true; const int flags = flags_; flags_ = 0; lock.unlock(); // Invoke the delegate, if supplied, in order to run. const int64_t integer_duration = std::max(target_time - previous_time_point_, int64_t(0)); const auto delegate = delegate_.load(); if(delegate) { // Cap running at 1/5th of a second, to avoid doing a huge amount of work after any // brief system interruption. const double duration = std::min(double(integer_duration) / 1e9, 0.2); const double elapsed_duration = delegate->update(this, duration, has_skipped_, flags); previous_time_point_ += int64_t(elapsed_duration * 1e9); has_skipped_ = false; } } } void BestEffortUpdater::flush() { // Spin lock; this is allowed to be slow. while(true) { std::lock_guard lock(update_mutex_); if(!is_updating_) return; } } void BestEffortUpdater::set_delegate(Delegate *const delegate) { delegate_.store(delegate); }