Merge branch 'master' into jgrpp
# Conflicts: # src/fileio.cpp # src/group_gui.cpp # src/industry.h # src/lang/korean.txt # src/linkgraph/linkgraphjob.cpp # src/linkgraph/linkgraphjob.h # src/linkgraph/linkgraphschedule.cpp # src/linkgraph/linkgraphschedule.h # src/openttd.cpp # src/saveload/saveload.cpp # src/saveload/saveload.h # src/town_cmd.cpp # src/vehicle_gui.cpp # src/vehicle_gui_base.h
This commit is contained in:
@@ -49,7 +49,7 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig, uint duration_multiplier) :
|
||||
join_date_ticks(GetLinkGraphJobJoinDateTicks(duration_multiplier)),
|
||||
start_date_ticks((_date * DAY_TICKS) + _date_fract),
|
||||
job_completed(false),
|
||||
abort_job(false)
|
||||
job_aborted(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -95,6 +95,11 @@ void LinkGraphJob::FinaliseJob()
|
||||
{
|
||||
this->JoinThread();
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -175,55 +180,6 @@ void LinkGraphJob::FinaliseJob()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if job has actually finished.
|
||||
* This is allowed to spuriously return an incorrect value.
|
||||
* @return True if job has actually finished.
|
||||
*/
|
||||
bool LinkGraphJob::IsJobCompleted() const
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __atomic_load_n(&job_completed, __ATOMIC_RELAXED);
|
||||
#else
|
||||
return job_completed;
|
||||
#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
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "linkgraph.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
class LinkGraphJob;
|
||||
class Path;
|
||||
@@ -68,8 +69,8 @@ protected:
|
||||
DateTicks start_date_ticks; ///< Date when the job was started.
|
||||
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.
|
||||
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();
|
||||
@@ -79,8 +80,6 @@ public:
|
||||
|
||||
DynUniformArenaAllocator path_allocator; ///< Arena allocator used for paths
|
||||
|
||||
bool IsJobAborted() const;
|
||||
|
||||
/**
|
||||
* A job edge. Wraps a link graph edge and an edge annotation. The
|
||||
* annotation can be modified, the edge is constant.
|
||||
@@ -293,7 +292,7 @@ public:
|
||||
* settings have to be brutally const-casted in order to populate them.
|
||||
*/
|
||||
LinkGraphJob() : settings(_settings_game.linkgraph),
|
||||
join_date_ticks(INVALID_DATE), start_date_ticks(INVALID_DATE), job_completed(false), abort_job(false) {}
|
||||
join_date_ticks(INVALID_DATE), start_date_ticks(INVALID_DATE), job_completed(false), job_aborted(false) {}
|
||||
|
||||
LinkGraphJob(const LinkGraph &orig, uint duration_multiplier);
|
||||
~LinkGraphJob();
|
||||
@@ -301,15 +300,34 @@ public:
|
||||
void Init();
|
||||
void FinaliseJob();
|
||||
|
||||
bool IsJobCompleted() const;
|
||||
void AbortJob();
|
||||
/**
|
||||
* Check if job has actually finished.
|
||||
* This is allowed to spuriously return an incorrect value.
|
||||
* @return True if job has actually finished.
|
||||
*/
|
||||
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.
|
||||
* @param tick_offset Optional number of ticks to add to the current date
|
||||
* @return True if job should be finished by now, false if not.
|
||||
*/
|
||||
inline bool IsFinished(int tick_offset = 0) const { return this->join_date_ticks <= (_date * DAY_TICKS) + _date_fract + tick_offset; }
|
||||
inline bool IsScheduledToBeJoined(int tick_offset = 0) const { return this->join_date_ticks <= (_date * DAY_TICKS) + _date_fract + tick_offset; }
|
||||
|
||||
/**
|
||||
* Get the date when the job should be finished.
|
||||
|
@@ -115,7 +115,7 @@ void LinkGraphSchedule::SpawnNext()
|
||||
bool LinkGraphSchedule::IsJoinWithUnfinishedJobDue() const
|
||||
{
|
||||
for (JobList::const_iterator it = this->running.begin(); it != this->running.end(); ++it) {
|
||||
if (!((*it)->IsFinished(2))) {
|
||||
if (!((*it)->IsScheduledToBeJoined(2))) {
|
||||
/* job is not due to be joined yet */
|
||||
return false;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ bool LinkGraphSchedule::IsJoinWithUnfinishedJobDue() const
|
||||
void LinkGraphSchedule::JoinNext()
|
||||
{
|
||||
while (!(this->running.empty())) {
|
||||
if (!this->running.front()->IsFinished()) return;
|
||||
if (!this->running.front()->IsScheduledToBeJoined()) return;
|
||||
std::unique_ptr<LinkGraphJob> next = std::move(this->running.front());
|
||||
this->running.pop_front();
|
||||
LinkGraphID id = next->LinkGraphIndex();
|
||||
@@ -160,19 +160,16 @@ void LinkGraphSchedule::JoinNext()
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 this will only happen just as a job is completing, and the real synchronisation is provided
|
||||
* by the thread join operation. In the worst case the main thread will be paused for longer than strictly necessary before
|
||||
* joining.
|
||||
* This is just a hint variable to avoid performing the join excessively early and blocking the main thread.
|
||||
* However this is OK as this will only happen just as a job is completing,
|
||||
* and the real synchronisation is provided by the thread join operation.
|
||||
* In the worst case the main thread will be paused for longer than
|
||||
* strictly necessary before joining.
|
||||
* This is just a hint variable to avoid performing the join excessively
|
||||
* early and blocking the main thread.
|
||||
*/
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
__atomic_store_n(&(job->job_completed), true, __ATOMIC_RELAXED);
|
||||
#else
|
||||
job->job_completed = true;
|
||||
#endif
|
||||
job->job_completed.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,9 +307,11 @@ LinkGraphJobGroup::JobInfo::JobInfo(LinkGraphJob *job) :
|
||||
job(job), cost_estimate(job->Graph().CalculateCostEstimate()) { }
|
||||
|
||||
/**
|
||||
* Pause the game if on the next _date_fract tick, we would do a join with the next
|
||||
* Pause the game if in 2 _date_fract ticks, we would do a join with the next
|
||||
* link graph job, but it is still running.
|
||||
* If we previous paused, unpause if the job is now ready to be joined with
|
||||
* The check is done 2 _date_fract ticks early instead of 1, as in multiplayer
|
||||
* calls to DoCommandP are executed after a delay of 1 _date_fract tick.
|
||||
* If we previously paused, unpause if the job is now ready to be joined with.
|
||||
*/
|
||||
void StateGameLoop_LinkGraphPauseControl()
|
||||
{
|
||||
@@ -339,8 +338,9 @@ void StateGameLoop_LinkGraphPauseControl()
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the game on load if we would do a join with the next link graph job, but it is still running, and it would not
|
||||
* be caught by a call to StateGameLoop_LinkGraphPauseControl().
|
||||
* Pause the game on load if we would do a join with the next link graph job,
|
||||
* but it is still running, and it would not be caught by a call to
|
||||
* StateGameLoop_LinkGraphPauseControl().
|
||||
*/
|
||||
void AfterLoad_LinkGraphPauseControl()
|
||||
{
|
||||
|
@@ -107,4 +107,7 @@ public:
|
||||
static void ExecuteJobSet(std::vector<JobInfo> jobs);
|
||||
};
|
||||
|
||||
void StateGameLoop_LinkGraphPauseControl();
|
||||
void AfterLoad_LinkGraphPauseControl();
|
||||
|
||||
#endif /* LINKGRAPHSCHEDULE_H */
|
||||
|
Reference in New Issue
Block a user