Linkgraph: Allow job threads to be aborted early when clearing schedule.

This commit is contained in:
Jonathan G Rennison
2016-11-26 00:33:50 +00:00
parent d08d1cbd67
commit 375dbdbfe1
3 changed files with 43 additions and 2 deletions

View File

@@ -50,7 +50,8 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig, uint duration_multiplier) :
settings(_settings_game.linkgraph), settings(_settings_game.linkgraph),
join_date_ticks(GetLinkGraphJobJoinDateTicks(duration_multiplier)), join_date_ticks(GetLinkGraphJobJoinDateTicks(duration_multiplier)),
start_date_ticks((_date * DAY_TICKS) + _date_fract), 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 #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. * 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 * This is done after the constructor so that we can do it in the calculation

View File

@@ -69,10 +69,12 @@ protected:
NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation. NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation.
EdgeAnnotationMatrix edges; ///< Extra edge 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 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 EraseFlows(NodeID from);
void JoinThread(); void JoinThread();
void SetJobGroup(std::shared_ptr<LinkGraphJobGroup> group); void SetJobGroup(std::shared_ptr<LinkGraphJobGroup> group);
bool IsJobAborted() const;
public: public:
@@ -282,6 +284,7 @@ public:
void FinaliseJob(); void FinaliseJob();
bool IsJobCompleted() const; bool IsJobCompleted() const;
void AbortJob();
/** /**
* Check if job is supposed to be finished. * Check if job is supposed to be finished.

View File

@@ -130,6 +130,7 @@ void LinkGraphSchedule::JoinNext()
this->running.pop_front(); this->running.pop_front();
LinkGraphID id = next->LinkGraphIndex(); LinkGraphID id = next->LinkGraphIndex();
next->FinaliseJob(); // joins the thread and finalises the job next->FinaliseJob(); // joins the thread and finalises the job
assert(!next->IsJobAborted());
next.reset(); next.reset();
if (LinkGraph::IsValidID(id)) { if (LinkGraph::IsValidID(id)) {
LinkGraph *lg = LinkGraph::Get(id); LinkGraph *lg = LinkGraph::Get(id);
@@ -148,6 +149,7 @@ void LinkGraphSchedule::JoinNext()
{ {
LinkGraphJob *job = (LinkGraphJob *)j; LinkGraphJob *job = (LinkGraphJob *)j;
for (uint i = 0; i < lengthof(instance.handlers); ++i) { for (uint i = 0; i < lengthof(instance.handlers); ++i) {
if (job->IsJobAborted()) return;
instance.handlers[i]->Run(*job); instance.handlers[i]->Run(*job);
} }
@@ -186,7 +188,7 @@ void LinkGraphSchedule::SpawnAll()
/* static */ void LinkGraphSchedule::Clear() /* static */ void LinkGraphSchedule::Clear()
{ {
for (JobList::iterator i(instance.running.begin()); i != instance.running.end(); ++i) { for (JobList::iterator i(instance.running.begin()); i != instance.running.end(); ++i) {
(*i)->JoinThread(); (*i)->AbortJob();
} }
instance.running.clear(); instance.running.clear();
instance.schedule.clear(); instance.schedule.clear();