summaryrefslogtreecommitdiff
path: root/src/linkgraph
diff options
context:
space:
mode:
Diffstat (limited to 'src/linkgraph')
-rw-r--r--src/linkgraph/linkgraphjob.cpp3
-rw-r--r--src/linkgraph/linkgraphjob.h13
-rw-r--r--src/linkgraph/linkgraphschedule.cpp62
-rw-r--r--src/linkgraph/linkgraphschedule.h4
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 */