diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 07ed30d947..3d97dd2c8c 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -50,7 +50,8 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig, uint duration_multiplier) : settings(_settings_game.linkgraph), join_date_ticks(GetLinkGraphJobJoinDateTicks(duration_multiplier)), start_date_ticks((_date * DAY_TICKS) + _date_fract), - job_completed(false) + job_completed(false), + abort_job(false) { } @@ -184,6 +185,41 @@ bool LinkGraphJob::IsJobCompleted() const #endif } +/** + * Check if job has been requested to be aborted. + * This is allowed to spuriously return a falsely negative value. + * @return True if job abort has been requested. + */ +bool LinkGraphJob::IsJobAborted() const +{ +#if defined(__GNUC__) || defined(__clang__) + return __atomic_load_n(&abort_job, __ATOMIC_RELAXED); +#else + return abort_job; +#endif +} + +/** + * 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. + */ +void LinkGraphJob::AbortJob() +{ + /* + * Note that this it not guaranteed to be an atomic write and there are no memory barriers or other protections. + * Readers of this variable in another thread may see an out of date value. + * However this is OK as if this method is called the state of the job/thread does not matter anyway. + */ + +#if defined(__GNUC__) || defined(__clang__) + __atomic_store_n(&(abort_job), true, __ATOMIC_RELAXED); +#else + abort_job = true; +#endif +} + /** * Initialize the link graph job: Resize nodes and edges and populate them. * This is done after the constructor so that we can do it in the calculation diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index 7539911807..400e8e19ec 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -69,10 +69,12 @@ protected: NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation. EdgeAnnotationMatrix edges; ///< Extra edge data necessary for link graph calculation. bool job_completed; ///< Is the job still running. This is accessed by multiple threads and is permitted to be spuriously incorrect. + bool abort_job; ///< Abort the job at the next available opportunity. This is accessed by multiple threads. void EraseFlows(NodeID from); void JoinThread(); void SetJobGroup(std::shared_ptr group); + bool IsJobAborted() const; public: @@ -282,6 +284,7 @@ public: void FinaliseJob(); bool IsJobCompleted() const; + void AbortJob(); /** * Check if job is supposed to be finished. diff --git a/src/linkgraph/linkgraphschedule.cpp b/src/linkgraph/linkgraphschedule.cpp index d855a5f602..bf9119eaab 100644 --- a/src/linkgraph/linkgraphschedule.cpp +++ b/src/linkgraph/linkgraphschedule.cpp @@ -130,6 +130,7 @@ void LinkGraphSchedule::JoinNext() this->running.pop_front(); LinkGraphID id = next->LinkGraphIndex(); next->FinaliseJob(); // joins the thread and finalises the job + assert(!next->IsJobAborted()); next.reset(); if (LinkGraph::IsValidID(id)) { LinkGraph *lg = LinkGraph::Get(id); @@ -148,6 +149,7 @@ void LinkGraphSchedule::JoinNext() { LinkGraphJob *job = (LinkGraphJob *)j; for (uint i = 0; i < lengthof(instance.handlers); ++i) { + if (job->IsJobAborted()) return; instance.handlers[i]->Run(*job); } @@ -186,7 +188,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();