Merge remote-tracking branch 'upstream/master'

This commit is contained in:
pelya
2021-01-25 00:50:42 +02:00
1076 changed files with 25433 additions and 61762 deletions

View File

@@ -0,0 +1,22 @@
add_files(
demands.cpp
demands.h
flowmapper.cpp
flowmapper.h
init.h
linkgraph.cpp
linkgraph.h
linkgraph_base.h
linkgraph_gui.cpp
linkgraph_gui.h
linkgraph_type.h
linkgraphjob.cpp
linkgraphjob.h
linkgraphjob_base.h
linkgraphschedule.cpp
linkgraphschedule.h
mcf.cpp
mcf.h
refresh.cpp
refresh.h
)

View File

@@ -45,7 +45,7 @@ public:
*/
inline void SetDemandPerNode(uint num_demands)
{
this->demand_per_node = max(this->supply_sum / num_demands, 1U);
this->demand_per_node = std::max(this->supply_sum / num_demands, 1U);
}
/**
@@ -57,7 +57,7 @@ public:
*/
inline uint EffectiveSupply(const Node &from, const Node &to)
{
return max(from.Supply() * max(1U, to.Supply()) * this->mod_size / 100 / this->demand_per_node, 1U);
return std::max(from.Supply() * std::max(1U, to.Supply()) * this->mod_size / 100 / this->demand_per_node, 1U);
}
/**
@@ -134,7 +134,7 @@ void SymmetricScaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id
uint undelivered = job[to_id].UndeliveredSupply();
if (demand_back > undelivered) {
demand_back = undelivered;
demand_forw = max(1U, demand_back * 100 / this->mod_size);
demand_forw = std::max(1U, demand_back * 100 / this->mod_size);
}
this->Scaler::SetDemands(job, to_id, from_id, demand_back);
}
@@ -230,7 +230,7 @@ void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler)
demand_forw = 1;
}
demand_forw = min(demand_forw, job[from_id].UndeliveredSupply());
demand_forw = std::min(demand_forw, job[from_id].UndeliveredSupply());
scaler.SetDemands(job, from_id, to_id, demand_forw);

View File

@@ -71,7 +71,7 @@ void LinkGraph::Compress()
for (NodeID node2 = 0; node2 < this->Size(); ++node2) {
BaseEdge &edge = this->edges[node1][node2];
if (edge.capacity > 0) {
edge.capacity = max(1U, edge.capacity / 2);
edge.capacity = std::max(1U, edge.capacity / 2);
edge.usage /= 2;
}
}
@@ -163,8 +163,7 @@ NodeID LinkGraph::AddNode(const Station *st)
this->nodes.emplace_back();
/* Avoid reducing the height of the matrix as that is expensive and we
* most likely will increase it again later which is again expensive. */
this->edges.Resize(new_node + 1U,
max(new_node + 1U, this->edges.Height()));
this->edges.Resize(new_node + 1U, std::max(new_node + 1U, this->edges.Height()));
this->nodes[new_node].Init(st->xy, st->index,
HasBit(good.status, GoodsEntry::GES_ACCEPTANCE));
@@ -266,8 +265,8 @@ void LinkGraph::Edge::Update(uint capacity, uint usage, EdgeUpdateMode mode)
this->edge.capacity += capacity;
this->edge.usage += usage;
} else if (mode & EUM_REFRESH) {
this->edge.capacity = max(this->edge.capacity, capacity);
this->edge.usage = max(this->edge.usage, usage);
this->edge.capacity = std::max(this->edge.capacity, capacity);
this->edge.usage = std::max(this->edge.usage, usage);
}
if (mode & EUM_UNRESTRICTED) this->edge.last_unrestricted_update = _date;
if (mode & EUM_RESTRICTED) this->edge.last_restricted_update = _date;

View File

@@ -17,6 +17,7 @@
#include "../cargotype.h"
#include "../date_func.h"
#include "linkgraph_type.h"
#include <utility>
struct SaveLoad;
class LinkGraph;
@@ -112,7 +113,7 @@ public:
* Get the date of the last update to any part of the edge's capacity.
* @return Last update.
*/
Date LastUpdate() const { return max(this->edge.last_unrestricted_update, this->edge.last_restricted_update); }
Date LastUpdate() const { return std::max(this->edge.last_unrestricted_update, this->edge.last_restricted_update); }
};
/**
@@ -188,20 +189,20 @@ public:
* to return something that implements operator->, but isn't a pointer
* from operator->. A fake pointer.
*/
class FakePointer : public SmallPair<NodeID, Tedge_wrapper> {
class FakePointer : public std::pair<NodeID, Tedge_wrapper> {
public:
/**
* Construct a fake pointer from a pair of NodeID and edge.
* @param pair Pair to be "pointed" to (in fact shallow-copied).
*/
FakePointer(const SmallPair<NodeID, Tedge_wrapper> &pair) : SmallPair<NodeID, Tedge_wrapper>(pair) {}
FakePointer(const std::pair<NodeID, Tedge_wrapper> &pair) : std::pair<NodeID, Tedge_wrapper>(pair) {}
/**
* Retrieve the pair by operator->.
* @return Pair being "pointed" to.
*/
SmallPair<NodeID, Tedge_wrapper> *operator->() { return this; }
std::pair<NodeID, Tedge_wrapper> *operator->() { return this; }
};
public:
@@ -266,9 +267,9 @@ public:
* Dereference with operator*.
* @return Pair of current target NodeID and edge object.
*/
SmallPair<NodeID, Tedge_wrapper> operator*() const
std::pair<NodeID, Tedge_wrapper> operator*() const
{
return SmallPair<NodeID, Tedge_wrapper>(this->current, Tedge_wrapper(this->base[this->current]));
return std::pair<NodeID, Tedge_wrapper>(this->current, Tedge_wrapper(this->base[this->current]));
}
/**
@@ -452,7 +453,7 @@ public:
*/
inline static uint Scale(uint val, uint target_age, uint orig_age)
{
return val > 0 ? max(1U, val * target_age / orig_age) : 0;
return val > 0 ? std::max(1U, val * target_age / orig_age) : 0;
}
/** Bare constructor, only for save/load. */

View File

@@ -224,7 +224,7 @@ void LinkGraphOverlay::AddLinks(const Station *from, const Station *to)
{
/* multiply the numbers by 32 in order to avoid comparing to 0 too often. */
if (cargo.capacity == 0 ||
max(cargo.usage, cargo.planned) * 32 / (cargo.capacity + 1) < max(new_usg, new_plan) * 32 / (new_cap + 1)) {
std::max(cargo.usage, cargo.planned) * 32 / (cargo.capacity + 1) < std::max(new_usg, new_plan) * 32 / (new_cap + 1)) {
cargo.capacity = new_cap;
cargo.usage = new_usg;
cargo.planned = new_plan;
@@ -272,7 +272,7 @@ void LinkGraphOverlay::DrawLinks(const DrawPixelInfo *dpi) const
*/
void LinkGraphOverlay::DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const
{
uint usage_or_plan = min(cargo.capacity * 2 + 1, max(cargo.usage, cargo.planned));
uint usage_or_plan = std::min(cargo.capacity * 2 + 1, std::max(cargo.usage, cargo.planned));
int colour = LinkGraphOverlay::LINK_COLOURS[usage_or_plan * lengthof(LinkGraphOverlay::LINK_COLOURS) / (cargo.capacity * 2 + 2)];
int dash = cargo.shared ? this->scale * 4 : 0;
@@ -302,7 +302,7 @@ void LinkGraphOverlay::DrawStationDots(const DrawPixelInfo *dpi) const
Point pt = this->GetStationMiddle(st);
if (!this->IsPointVisible(pt, dpi, 3 * this->scale)) continue;
uint r = this->scale * 2 + this->scale * 2 * min(200, i->second) / 200;
uint r = this->scale * 2 + this->scale * 2 * std::min(200U, i->second) / 200;
LinkGraphOverlay::DrawVertex(pt.x, pt.y, r,
_colour_gradient[st->owner != OWNER_NONE ?
@@ -455,7 +455,7 @@ static const NWidgetPart _nested_linkgraph_legend_widgets[] = {
EndContainer()
};
assert_compile(WID_LGL_SATURATION_LAST - WID_LGL_SATURATION_FIRST ==
static_assert(WID_LGL_SATURATION_LAST - WID_LGL_SATURATION_FIRST ==
lengthof(LinkGraphOverlay::LINK_COLOURS) - 1);
static WindowDesc _linkgraph_legend_desc(

View File

@@ -37,7 +37,9 @@ 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),
job_aborted(false)
{
}
@@ -91,6 +93,11 @@ LinkGraphJob::~LinkGraphJob()
* Accessing other pools may be invalid. */
if (CleaningPool()) return;
/* 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;
@@ -216,8 +223,8 @@ void LinkGraphJob::NodeAnnotation::Init(uint supply)
*/
void Path::Fork(Path *base, uint cap, int free_cap, uint dist)
{
this->capacity = min(base->capacity, cap);
this->free_capacity = min(base->free_capacity, free_cap);
this->capacity = std::min(base->capacity, cap);
this->free_capacity = std::min(base->free_capacity, free_cap);
this->distance = base->distance + dist;
assert(this->distance > 0);
if (this->parent != base) {
@@ -243,7 +250,7 @@ uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation)
if (max_saturation != UINT_MAX) {
uint usable_cap = edge.Capacity() * max_saturation / 100;
if (usable_cap > edge.Flow()) {
new_flow = min(new_flow, usable_cap - edge.Flow());
new_flow = std::min(new_flow, usable_cap - edge.Flow());
} else {
return 0;
}

View File

@@ -13,6 +13,7 @@
#include "../thread.h"
#include "linkgraph.h"
#include <list>
#include <atomic>
class LinkGraphJob;
class Path;
@@ -61,6 +62,8 @@ 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.
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();
@@ -160,9 +163,9 @@ public:
* @return Pair of the edge currently pointed to and the ID of its
* other end.
*/
SmallPair<NodeID, Edge> operator*() const
std::pair<NodeID, Edge> operator*() const
{
return SmallPair<NodeID, Edge>(this->current, Edge(this->base[this->current], this->base_anno[this->current]));
return std::pair<NodeID, Edge>(this->current, Edge(this->base[this->current], this->base_anno[this->current]));
}
/**
@@ -265,18 +268,40 @@ 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), job_aborted(false) {}
LinkGraphJob(const LinkGraph &orig);
~LinkGraphJob();
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 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.
* @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.
@@ -367,7 +392,7 @@ public:
*/
inline static int GetCapacityRatio(int free, uint total)
{
return Clamp(free, PATH_CAP_MIN_FREE, PATH_CAP_MAX_FREE) * PATH_CAP_MULTIPLIER / max(total, 1U);
return Clamp(free, PATH_CAP_MIN_FREE, PATH_CAP_MAX_FREE) * PATH_CAP_MULTIPLIER / std::max(total, 1U);
}
/**

View File

@@ -14,6 +14,8 @@
#include "mcf.h"
#include "flowmapper.h"
#include "../framerate_type.h"
#include "../command_func.h"
#include "../network/network.h"
#include "../safeguards.h"
@@ -48,6 +50,17 @@ 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.
*/
@@ -55,7 +68,7 @@ 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
@@ -73,8 +86,21 @@ void LinkGraphSchedule::JoinNext()
/* static */ void LinkGraphSchedule::Run(LinkGraphJob *job)
{
for (uint i = 0; i < lengthof(instance.handlers); ++i) {
if (job->IsJobAborted()) return;
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);
}
/**
@@ -94,7 +120,7 @@ void LinkGraphSchedule::SpawnAll()
/* static */ void LinkGraphSchedule::Clear()
{
for (JobList::iterator i(instance.running.begin()); i != instance.running.end(); ++i) {
(*i)->JoinThread();
(*i)->AbortJob();
}
instance.running.clear();
instance.schedule.clear();
@@ -135,6 +161,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.
@@ -146,8 +208,13 @@ void OnTick_LinkGraph()
if (offset == 0) {
LinkGraphSchedule::instance.SpawnNext();
} else if (offset == _settings_game.linkgraph.recalc_interval / 2) {
PerformanceMeasurer framerate(PFE_GL_LINKGRAPH);
LinkGraphSchedule::instance.JoinNext();
if (!_networking || _network_server) {
PerformanceMeasurer::SetInactive(PFE_GL_LINKGRAPH);
LinkGraphSchedule::instance.JoinNext();
} else {
PerformanceMeasurer framerate(PFE_GL_LINKGRAPH);
LinkGraphSchedule::instance.JoinNext();
}
}
}

View File

@@ -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 */

View File

@@ -235,7 +235,7 @@ bool DistanceAnnotation::IsBetter(const DistanceAnnotation *base, uint cap,
bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap,
int free_cap, uint dist) const
{
int min_cap = Path::GetCapacityRatio(min(base->free_capacity, free_cap), min(base->capacity, cap));
int min_cap = Path::GetCapacityRatio(std::min(base->free_capacity, free_cap), std::min(base->capacity, cap));
int this_cap = this->GetCapacityRatio();
if (min_cap == this_cap) {
/* If the capacities are the same and the other path isn't disconnected
@@ -354,7 +354,7 @@ uint MCF1stPass::FindCycleFlow(const PathVector &path, const Path *cycle_begin)
uint flow = UINT_MAX;
const Path *cycle_end = cycle_begin;
do {
flow = min(flow, cycle_begin->GetFlow());
flow = std::min(flow, cycle_begin->GetFlow());
cycle_begin = path[cycle_begin->GetNode()];
} while (cycle_begin != cycle_end);
return flow;
@@ -528,7 +528,7 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job)
finished_sources[source] = !source_demand_left;
this->CleanupPaths(source, paths);
}
} while (more_loops || this->EliminateCycles());
} while ((more_loops || this->EliminateCycles()) && !job.IsJobAborted());
}
/**
@@ -544,7 +544,7 @@ MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job)
uint accuracy = job.Settings().accuracy;
bool demand_left = true;
std::vector<bool> finished_sources(size);
while (demand_left) {
while (demand_left && !job.IsJobAborted()) {
demand_left = false;
for (NodeID source = 0; source < size; ++source) {
if (finished_sources[source]) continue;