mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-28 21:49:27 +00:00
99 lines
2.3 KiB
C++
99 lines
2.3 KiB
C++
//
|
|
// AsyncUpdater.h
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 06/07/2022.
|
|
// Copyright © 2022 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#ifndef AsyncUpdater_hpp
|
|
#define AsyncUpdater_hpp
|
|
|
|
#include <atomic>
|
|
#include <condition_variable>
|
|
#include <mutex>
|
|
|
|
#include "../ClockReceiver/TimeTypes.hpp"
|
|
|
|
namespace Concurrency {
|
|
|
|
template <typename Performer> class AsyncUpdater {
|
|
public:
|
|
template <typename... Args> AsyncUpdater(Args&&... args) :
|
|
performer(std::forward<Args>(args)...),
|
|
performer_thread_{
|
|
[this] {
|
|
Time::Nanos last_fired = Time::nanos_now();
|
|
ActionVector actions;
|
|
|
|
while(!should_quit) {
|
|
// Wait for new actions to be signalled, and grab them.
|
|
std::unique_lock lock(condition_mutex_);
|
|
while(actions_.empty()) {
|
|
condition_.wait(lock);
|
|
}
|
|
std::swap(actions, actions_);
|
|
lock.unlock();
|
|
|
|
// Update to now.
|
|
auto time_now = Time::nanos_now();
|
|
performer.perform(time_now - last_fired);
|
|
last_fired = time_now;
|
|
|
|
// Perform the actions.
|
|
for(const auto& action: actions) {
|
|
action();
|
|
}
|
|
actions.clear();
|
|
}
|
|
}
|
|
} {}
|
|
|
|
/// Run the performer up to 'now' and then perform @c post_action.
|
|
///
|
|
/// @c post_action will be performed asynchronously, on the same
|
|
/// thread as the performer.
|
|
///
|
|
/// Actions may be elided,
|
|
void update(const std::function<void(void)> &post_action) {
|
|
std::lock_guard guard(condition_mutex_);
|
|
actions_.push_back(post_action);
|
|
condition_.notify_all();
|
|
}
|
|
|
|
void stop() {
|
|
if(performer_thread_.joinable()) {
|
|
should_quit = true;
|
|
update([] {});
|
|
performer_thread_.join();
|
|
}
|
|
}
|
|
|
|
~AsyncUpdater() {
|
|
stop();
|
|
}
|
|
|
|
// The object that will actually receive time advances.
|
|
Performer performer;
|
|
|
|
private:
|
|
// 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::function<void(void)>>;
|
|
ActionVector actions_;
|
|
|
|
// Necessary synchronisation parts.
|
|
std::atomic<bool> should_quit = false;
|
|
std::mutex condition_mutex_;
|
|
std::condition_variable condition_;
|
|
|
|
// Ensure the thread isn't constructed until after the mutex
|
|
// and condition variable.
|
|
std::thread performer_thread_;
|
|
};
|
|
|
|
|
|
}
|
|
|
|
#endif /* AsyncUpdater_hpp */
|