From 94d629d79bcd623a5c77daa3db742cd8fd43d7b4 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 24 Dec 2020 23:36:36 +0000 Subject: Change: [Linkgraph] Allow job threads to be aborted early when clearing schedule (#8416) When link graph jobs are cleared due to abandoning the game or exiting, flag the job as aborted. The link graph job running in a separate thread checks the aborted flag periodically and terminates processing early if set. This reduces the delay at game abandon or exit if a long-running job would otherwise still be running. --- src/linkgraph/linkgraphjob.cpp | 8 +++++++- src/linkgraph/linkgraphjob.h | 18 +++++++++++++++++- src/linkgraph/linkgraphschedule.cpp | 3 ++- src/linkgraph/mcf.cpp | 4 ++-- 4 files changed, 28 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index c66ddeac7..e23c23b47 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -38,7 +38,8 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig) : link_graph(orig), settings(_settings_game.linkgraph), join_date(_date + _settings_game.linkgraph.recalc_time), - job_completed(false) + job_completed(false), + job_aborted(false) { } @@ -92,6 +93,11 @@ LinkGraphJob::~LinkGraphJob() * Accessing other pools may be invalid. */ if (CleaningPool()) return; + /* If the job has been aborted, the job state is invalid. + * This should never be reached, as once the job has been marked as aborted + * the only valid job operation is to clear the LinkGraphJob pool. */ + assert(!this->IsJobAborted()); + /* Link graph has been merged into another one. */ if (!LinkGraph::IsValidID(this->link_graph.index)) return; diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index ab5e07fb1..764365921 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -63,6 +63,7 @@ protected: NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation. EdgeAnnotationMatrix edges; ///< Extra edge data necessary for link graph calculation. std::atomic job_completed; ///< Is the job still running. This is accessed by multiple threads and reads may be stale. + std::atomic job_aborted; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale. void EraseFlows(NodeID from); void JoinThread(); @@ -267,7 +268,7 @@ public: * settings have to be brutally const-casted in order to populate them. */ LinkGraphJob() : settings(_settings_game.linkgraph), - join_date(INVALID_DATE), job_completed(false) {} + join_date(INVALID_DATE), job_completed(false), job_aborted(false) {} LinkGraphJob(const LinkGraph &orig); ~LinkGraphJob(); @@ -281,6 +282,21 @@ public: */ inline bool IsJobCompleted() const { return this->job_completed.load(std::memory_order_acquire); } + /** + * Check if job has been aborted. + * This is allowed to spuriously return false incorrectly, but is not allowed to incorrectly return true. + * @return True if job has been aborted. + */ + inline bool IsJobAborted() const { return this->job_aborted.load(std::memory_order_acquire); } + + /** + * Abort job. + * The job may exit early at the next available opportunity. + * After this method has been called the state of the job is undefined, and the only valid operation + * is to join the thread and discard the job data. + */ + inline void AbortJob() { this->job_aborted.store(true, std::memory_order_release); } + /** * Check if job is supposed to be finished. * @return True if job should be finished by now, false if not. diff --git a/src/linkgraph/linkgraphschedule.cpp b/src/linkgraph/linkgraphschedule.cpp index 87ab4818a..ce28ec3d8 100644 --- a/src/linkgraph/linkgraphschedule.cpp +++ b/src/linkgraph/linkgraphschedule.cpp @@ -86,6 +86,7 @@ void LinkGraphSchedule::JoinNext() /* static */ void LinkGraphSchedule::Run(LinkGraphJob *job) { for (uint i = 0; i < lengthof(instance.handlers); ++i) { + if (job->IsJobAborted()) return; instance.handlers[i]->Run(*job); } @@ -119,7 +120,7 @@ void LinkGraphSchedule::SpawnAll() /* static */ void LinkGraphSchedule::Clear() { for (JobList::iterator i(instance.running.begin()); i != instance.running.end(); ++i) { - (*i)->JoinThread(); + (*i)->AbortJob(); } instance.running.clear(); instance.schedule.clear(); diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index c8c031ea3..ea1040941 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -528,7 +528,7 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job) finished_sources[source] = !source_demand_left; this->CleanupPaths(source, paths); } - } while (more_loops || this->EliminateCycles()); + } while ((more_loops || this->EliminateCycles()) && !job.IsJobAborted()); } /** @@ -544,7 +544,7 @@ MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job) uint accuracy = job.Settings().accuracy; bool demand_left = true; std::vector finished_sources(size); - while (demand_left) { + while (demand_left && !job.IsJobAborted()) { demand_left = false; for (NodeID source = 0; source < size; ++source) { if (finished_sources[source]) continue; -- cgit v1.2.3-54-g00ecf