diff options
Diffstat (limited to 'src/linkgraph')
-rw-r--r-- | src/linkgraph/linkgraphjob.cpp | 3 | ||||
-rw-r--r-- | src/linkgraph/linkgraphjob.h | 13 | ||||
-rw-r--r-- | src/linkgraph/linkgraphschedule.cpp | 62 | ||||
-rw-r--r-- | src/linkgraph/linkgraphschedule.h | 4 |
4 files changed, 78 insertions, 4 deletions
diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 2d7b407da..c66ddeac7 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -37,7 +37,8 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig) : * This is on purpose. */ link_graph(orig), settings(_settings_game.linkgraph), - join_date(_date + _settings_game.linkgraph.recalc_time) + join_date(_date + _settings_game.linkgraph.recalc_time), + job_completed(false) { } diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index cd7ece4b1..ab5e07fb1 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -13,6 +13,7 @@ #include "../thread.h" #include "linkgraph.h" #include <list> +#include <atomic> class LinkGraphJob; class Path; @@ -61,6 +62,7 @@ protected: Date join_date; ///< Date when the job is to be joined. 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. void EraseFlows(NodeID from); void JoinThread(); @@ -265,7 +267,7 @@ public: * settings have to be brutally const-casted in order to populate them. */ LinkGraphJob() : settings(_settings_game.linkgraph), - join_date(INVALID_DATE) {} + join_date(INVALID_DATE), job_completed(false) {} LinkGraphJob(const LinkGraph &orig); ~LinkGraphJob(); @@ -273,10 +275,17 @@ public: void Init(); /** + * 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 is supposed to be finished. * @return True if job should be finished by now, false if not. */ - inline bool IsFinished() const { return this->join_date <= _date; } + inline bool IsScheduledToBeJoined() const { return this->join_date <= _date; } /** * Get the date when the job should be finished. diff --git a/src/linkgraph/linkgraphschedule.cpp b/src/linkgraph/linkgraphschedule.cpp index 964744509..2638b77ea 100644 --- a/src/linkgraph/linkgraphschedule.cpp +++ b/src/linkgraph/linkgraphschedule.cpp @@ -14,6 +14,7 @@ #include "mcf.h" #include "flowmapper.h" #include "../framerate_type.h" +#include "../command_func.h" #include "../safeguards.h" @@ -49,13 +50,24 @@ void LinkGraphSchedule::SpawnNext() } /** + * Check if the next job is supposed to be finished, but has not yet completed. + * @return True if job should be finished by now but is still running, false if not. + */ +bool LinkGraphSchedule::IsJoinWithUnfinishedJobDue() const +{ + if (this->running.empty()) return false; + const LinkGraphJob *next = this->running.front(); + return next->IsScheduledToBeJoined() && !next->IsJobCompleted(); +} + +/** * Join the next finished job, if available. */ void LinkGraphSchedule::JoinNext() { if (this->running.empty()) return; LinkGraphJob *next = this->running.front(); - if (!next->IsFinished()) return; + if (!next->IsScheduledToBeJoined()) return; this->running.pop_front(); LinkGraphID id = next->LinkGraphIndex(); delete next; // implicitly joins the thread @@ -75,6 +87,18 @@ void LinkGraphSchedule::JoinNext() for (uint i = 0; i < lengthof(instance.handlers); ++i) { instance.handlers[i]->Run(*job); } + + /* + * 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. + */ + + job->job_completed.store(true, std::memory_order_release); } /** @@ -136,6 +160,42 @@ LinkGraphSchedule::~LinkGraphSchedule() } /** + * 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. + * 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() +{ + if (_pause_mode & PM_PAUSED_LINK_GRAPH) { + /* We are paused waiting on a job, check the job every tick. */ + if (!LinkGraphSchedule::instance.IsJoinWithUnfinishedJobDue()) { + DoCommandP(0, PM_PAUSED_LINK_GRAPH, 0, CMD_PAUSE); + } + } else if (_pause_mode == PM_UNPAUSED && + _date_fract == LinkGraphSchedule::SPAWN_JOIN_TICK - 2 && + _date % _settings_game.linkgraph.recalc_interval == _settings_game.linkgraph.recalc_interval / 2 && + LinkGraphSchedule::instance.IsJoinWithUnfinishedJobDue()) { + /* Perform check two _date_fract ticks before we would join, to make + * sure it also works in multiplayer. */ + DoCommandP(0, PM_PAUSED_LINK_GRAPH, 1, CMD_PAUSE); + } +} + +/** + * 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() +{ + if (LinkGraphSchedule::instance.IsJoinWithUnfinishedJobDue()) { + _pause_mode |= PM_PAUSED_LINK_GRAPH; + } +} + +/** * Spawn or join a link graph job or compress a link graph if any link graph is * due to do so. */ diff --git a/src/linkgraph/linkgraphschedule.h b/src/linkgraph/linkgraphschedule.h index 62ca2b0c1..6a6dff697 100644 --- a/src/linkgraph/linkgraphschedule.h +++ b/src/linkgraph/linkgraphschedule.h @@ -55,6 +55,7 @@ public: static void Clear(); void SpawnNext(); + bool IsJoinWithUnfinishedJobDue() const; void JoinNext(); void SpawnAll(); void ShiftDates(int interval); @@ -76,4 +77,7 @@ public: void Unqueue(LinkGraph *lg) { this->schedule.remove(lg); } }; +void StateGameLoop_LinkGraphPauseControl(); +void AfterLoad_LinkGraphPauseControl(); + #endif /* LINKGRAPHSCHEDULE_H */ |