From 1b197d0bb2ae72d2d81c6d38acf1c008a5e74c75 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 22 Aug 2022 17:01:41 -0400 Subject: [PATCH] Resolve crash of machines that require the ROM requester. --- Concurrency/AsyncTaskQueue.hpp | 63 +++++++++++-------- .../Mac/Clock Signal/Machine/CSMachine.mm | 5 +- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/Concurrency/AsyncTaskQueue.hpp b/Concurrency/AsyncTaskQueue.hpp index 7a8452734..f0de9f640 100644 --- a/Concurrency/AsyncTaskQueue.hpp +++ b/Concurrency/AsyncTaskQueue.hpp @@ -65,34 +65,14 @@ template <> struct TaskQueueStorage { action occupies the asynchronous thread for long enough. So it is not true that @c perform will be called once per action. */ -template class AsyncTaskQueue: public TaskQueueStorage { +template class AsyncTaskQueue: public TaskQueueStorage { public: template AsyncTaskQueue(Args&&... args) : - TaskQueueStorage(std::forward(args)...), - thread_{ - [this] { - 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 (which is possibly a no-op). - TaskQueueStorage::update(); - - // Perform the actions and destroy them. - for(const auto &action: actions) { - action(); - } - actions.clear(); - } - } - } {} + TaskQueueStorage(std::forward(args)...) { + if constexpr (start_immediately) { + start(); + } + } /// Enqueus @c post_action to be performed asynchronously at some point /// in the future. If @c perform_automatically is @c true then the action @@ -136,6 +116,37 @@ template class AsyncTask } } + /// Starts the queue if it has never been started before. + /// + /// This is not guaranteed safely to restart a stopped queue. + void start() { + thread_ = std::move(std::thread{ + [this] { + ActionVector actions; + + // Continue until told to quit. + 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 (which is possibly a no-op). + TaskQueueStorage::update(); + + // Perform the actions and destroy them. + for(const auto &action: actions) { + action(); + } + actions.clear(); + } + } + }); + } + /// Schedules any remaining unscheduled work, then blocks synchronously /// until all scheduled work has been performed. void flush() { diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index c3912a080..3ad335c17 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -122,7 +122,7 @@ struct ActivityObserver: public Activity::Observer { CSJoystickManager *_joystickManager; NSMutableArray *_leds; - Concurrency::AsyncTaskQueue updater; + Concurrency::AsyncTaskQueue updater; Time::ScanSynchroniser _scanSynchroniser; NSTimer *_joystickTimer; @@ -149,6 +149,9 @@ struct ActivityObserver: public Activity::Observer { return nil; } updater.performer.machine = _machine->timed_machine(); + if(updater.performer.machine) { + updater.start(); + } // Use the keyboard as a joystick if the machine has no keyboard, or if it has a 'non-exclusive' keyboard. _inputMode =