mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-09 15:41:15 +00:00
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:
parent
ad47e3d9e6
commit
94d629d79b
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user