diff --git a/Concurrency/AsyncTaskQueue.hpp b/Concurrency/AsyncTaskQueue.hpp index 8a470dd70..f4a097935 100644 --- a/Concurrency/AsyncTaskQueue.hpp +++ b/Concurrency/AsyncTaskQueue.hpp @@ -74,7 +74,7 @@ public: template AsyncTaskQueue(Args&&... args) : TaskQueueStorage(std::forward(args)...) { if constexpr (start_immediately) { - start(); + start_impl(); } } @@ -99,6 +99,7 @@ public: /// Causes any enqueued actions that are not yet scheduled to be scheduled. void perform() { + static_assert(!perform_automatically); if(actions_.empty()) { return; } @@ -111,7 +112,7 @@ public: /// The queue cannot be restarted; this is a destructive action. void stop() { if(thread_.joinable()) { - should_quit_ = true; + should_quit_.store(true, std::memory_order_relaxed); enqueue([] {}); if constexpr (!perform_automatically) { perform(); @@ -124,31 +125,8 @@ public: /// /// This is not guaranteed safely to restart a stopped queue. void start() { - thread_ = 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() && !should_quit_) { - 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(); - } - } - }; + static_assert(!start_immediately); + start_impl(); } /// Schedules any remaining unscheduled work, then blocks synchronously @@ -177,6 +155,34 @@ public: } private: + void start_impl() { + thread_ = std::thread{ + [this] { + ActionVector actions; + + // Continue until told to quit. + while(!should_quit_.load(std::memory_order_relaxed)) { + // Wait for new actions to be signalled, and grab them. + std::unique_lock lock(condition_mutex_); + while(actions_.empty() && !should_quit_.load(std::memory_order_relaxed)) { + 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(); + } + } + }; + } + // The list of actions waiting be performed. These will be elided, // increasing their latency, if the emulation thread falls behind. using ActionVector = std::vector>;