summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan G Rennison <j.g.rennison@gmail.com>2020-12-24 23:36:36 +0000
committerGitHub <noreply@github.com>2020-12-25 00:36:36 +0100
commit94d629d79bcd623a5c77daa3db742cd8fd43d7b4 (patch)
tree25691cf3b5a2babd913b350275e8753b6b053ce4 /src
parentad47e3d9e6d07e5ee60a53ff53a8671058680da7 (diff)
downloadopenttd-94d629d79bcd623a5c77daa3db742cd8fd43d7b4.tar.xz
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.
Diffstat (limited to 'src')
-rw-r--r--src/linkgraph/linkgraphjob.cpp8
-rw-r--r--src/linkgraph/linkgraphjob.h18
-rw-r--r--src/linkgraph/linkgraphschedule.cpp3
-rw-r--r--src/linkgraph/mcf.cpp4
4 files changed, 28 insertions, 5 deletions
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<bool> job_completed; ///< Is the job still running. This is accessed by multiple threads and reads may be stale.
+ std::atomic<bool> 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();
@@ -282,6 +283,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<bool> 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;