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.
This commit is contained in:
Jonathan G Rennison 2020-12-24 23:36:36 +00:00 committed by GitHub
parent ad47e3d9e6
commit 94d629d79b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 28 additions and 5 deletions

View File

@ -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;

View File

@ -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();
@ -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.

View File

@ -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();

View File

@ -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;