summaryrefslogtreecommitdiff
path: root/src/linkgraph/linkgraphschedule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/linkgraph/linkgraphschedule.cpp')
-rw-r--r--src/linkgraph/linkgraphschedule.cpp62
1 files changed, 61 insertions, 1 deletions
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.
*/