Changeset - r22021:daba722f3935
[Not reviewed]
master
0 11 0
frosch - 10 years ago 2015-03-07 18:27:01
frosch@openttd.org
(svn r27178) -Fix [FS#5969]: Data race due to lazy initialisation of objects.
11 files changed with 48 insertions and 36 deletions:
0 comments (0 inline, 0 general)
src/cheat_gui.cpp
Show inline comments
 
@@ -105,13 +105,13 @@ static int32 ClickChangeDateCheat(int32 
 
	ConvertDateToYMD(_date, &ymd);
 

	
 
	p1 = Clamp(p1, MIN_YEAR, MAX_YEAR);
 
	if (p1 == _cur_year) return _cur_year;
 

	
 
	Date new_date = ConvertYMDToDate(p1, ymd.month, ymd.day);
 
	LinkGraphSchedule::Instance()->ShiftDates(new_date - _date);
 
	LinkGraphSchedule::instance.ShiftDates(new_date - _date);
 
	SetDate(new_date, _date_fract);
 
	EnginesMonthlyLoop();
 
	SetWindowDirty(WC_STATUS_BAR, 0);
 
	InvalidateWindowClassesData(WC_BUILD_STATION, 0);
 
	InvalidateWindowClassesData(WC_BUILD_OBJECT, 0);
 
	ResetSignalVariant();
src/linkgraph/linkgraphjob.cpp
Show inline comments
 
@@ -19,12 +19,19 @@
 

	
 
/* Initialize the link-graph-job-pool */
 
LinkGraphJobPool _link_graph_job_pool("LinkGraphJob");
 
INSTANTIATE_POOL_METHODS(LinkGraphJob)
 

	
 
/**
 
 * Static instance of an invalid path.
 
 * Note: This instance is created on task start.
 
 *       Lazy creation on first usage results in a data race between the CDist threads.
 
 */
 
/* static */ Path *Path::invalid_path = new Path(INVALID_NODE, true);
 

	
 
/**
 
 * Create a link graph job from a link graph. The link graph will be copied so
 
 * that the calculations don't interfer with the normal operations on the
 
 * original. The job is immediately started.
 
 * @param orig Original LinkGraph to be copied.
 
 */
 
LinkGraphJob::LinkGraphJob(const LinkGraph &orig) :
src/linkgraph/linkgraphjob.h
Show inline comments
 
@@ -340,12 +340,14 @@ public:
 

	
 
/**
 
 * A leg of a path in the link graph. Paths can form trees by being "forked".
 
 */
 
class Path {
 
public:
 
	static Path *invalid_path;
 

	
 
	Path(NodeID n, bool source = false);
 

	
 
	/** Get the node this leg passes. */
 
	inline NodeID GetNode() const { return this->node; }
 

	
 
	/** Get the overall origin of the path. */
src/linkgraph/linkgraphschedule.cpp
Show inline comments
 
@@ -16,12 +16,19 @@
 
#include "mcf.h"
 
#include "flowmapper.h"
 

	
 
#include "../safeguards.h"
 

	
 
/**
 
 * Static instance of LinkGraphSchedule.
 
 * Note: This instance is created on task start.
 
 *       Lazy creation on first usage results in a data race between the CDist threads.
 
 */
 
/* static */ LinkGraphSchedule LinkGraphSchedule::instance;
 

	
 
/**
 
 * Start the next job in the schedule.
 
 */
 
void LinkGraphSchedule::SpawnNext()
 
{
 
	if (this->schedule.empty()) return;
 
	LinkGraph *next = this->schedule.front();
 
@@ -65,15 +72,14 @@ void LinkGraphSchedule::JoinNext()
 
 * ThreadObject::New.
 
 * @param j Pointer to a link graph job.
 
 */
 
/* static */ void LinkGraphSchedule::Run(void *j)
 
{
 
	LinkGraphJob *job = (LinkGraphJob *)j;
 
	LinkGraphSchedule *schedule = LinkGraphSchedule::Instance();
 
	for (uint i = 0; i < lengthof(schedule->handlers); ++i) {
 
		schedule->handlers[i]->Run(*job);
 
	for (uint i = 0; i < lengthof(instance.handlers); ++i) {
 
		instance.handlers[i]->Run(*job);
 
	}
 
}
 

	
 
/**
 
 * Start all threads in the running list. This is only useful for save/load.
 
 * Usually threads are started when the job is created.
 
@@ -87,18 +93,17 @@ void LinkGraphSchedule::SpawnAll()
 

	
 
/**
 
 * Clear all link graphs and jobs from the schedule.
 
 */
 
/* static */ void LinkGraphSchedule::Clear()
 
{
 
	LinkGraphSchedule *inst = LinkGraphSchedule::Instance();
 
	for (JobList::iterator i(inst->running.begin()); i != inst->running.end(); ++i) {
 
	for (JobList::iterator i(instance.running.begin()); i != instance.running.end(); ++i) {
 
		(*i)->JoinThread();
 
	}
 
	inst->running.clear();
 
	inst->schedule.clear();
 
	instance.running.clear();
 
	instance.schedule.clear();
 
}
 

	
 
/**
 
 * Shift all dates (join dates and edge annotations) of link graphs and link
 
 * graph jobs by the number of days given.
 
 * @param interval Number of days to be added or subtracted.
 
@@ -133,30 +138,21 @@ LinkGraphSchedule::~LinkGraphSchedule()
 
	for (uint i = 0; i < lengthof(this->handlers); ++i) {
 
		delete this->handlers[i];
 
	}
 
}
 

	
 
/**
 
 * Retrieve the link graph schedule or create it if necessary.
 
 */
 
/* static */ LinkGraphSchedule *LinkGraphSchedule::Instance()
 
{
 
	static LinkGraphSchedule inst;
 
	return &inst;
 
}
 

	
 
/**
 
 * Spawn or join a link graph job or compress a link graph if any link graph is
 
 * due to do so.
 
 */
 
void OnTick_LinkGraph()
 
{
 
	if (_date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK) return;
 
	Date offset = _date % _settings_game.linkgraph.recalc_interval;
 
	if (offset == 0) {
 
		LinkGraphSchedule::Instance()->SpawnNext();
 
		LinkGraphSchedule::instance.SpawnNext();
 
	} else if (offset == _settings_game.linkgraph.recalc_interval / 2) {
 
		LinkGraphSchedule::Instance()->JoinNext();
 
		LinkGraphSchedule::instance.JoinNext();
 
	}
 
}
 

	
 

	
src/linkgraph/linkgraphschedule.h
Show inline comments
 
@@ -48,14 +48,14 @@ protected:
 
	GraphList schedule;            ///< Queue for new jobs.
 
	JobList running;               ///< Currently running jobs.
 

	
 
public:
 
	/* This is a tick where not much else is happening, so a small lag might go unnoticed. */
 
	static const uint SPAWN_JOIN_TICK = 21; ///< Tick when jobs are spawned or joined every day.
 
	static LinkGraphSchedule instance;
 

	
 
	static LinkGraphSchedule *Instance();
 
	static void Run(void *j);
 
	static void Clear();
 

	
 
	void SpawnNext();
 
	void JoinNext();
 
	void SpawnAll();
src/linkgraph/mcf.cpp
Show inline comments
 
@@ -145,21 +145,20 @@ public:
 
	 * Setup the node to retrieve edges from.
 
	 * @param source Root of the current path tree.
 
	 * @param node Current node to be checked for outgoing flows.
 
	 */
 
	void SetNode(NodeID source, NodeID node)
 
	{
 
		static const FlowStat::SharesMap empty;
 
		const FlowStatMap &flows = this->job[node].Flows();
 
		FlowStatMap::const_iterator it = flows.find(this->job[source].Station());
 
		if (it != flows.end()) {
 
			this->it = it->second.GetShares()->begin();
 
			this->end = it->second.GetShares()->end();
 
		} else {
 
			this->it = empty.begin();
 
			this->end = empty.end();
 
			this->it = FlowStat::empty_sharesmap.begin();
 
			this->end = FlowStat::empty_sharesmap.end();
 
		}
 
	}
 

	
 
	/**
 
	 * Get the next node for which a flow exists.
 
	 * @return ID of next node with flow.
 
@@ -376,17 +375,16 @@ void MCF1stPass::EliminateCycle(PathVect
 
 * @param origin_id Origin of the paths to be checked.
 
 * @param next_id Next node to be checked.
 
 * @return If any cycles have been found and eliminated.
 
 */
 
bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next_id)
 
{
 
	static Path *invalid_path = new Path(INVALID_NODE, true);
 
	Path *at_next_pos = path[next_id];
 

	
 
	/* this node has already been searched */
 
	if (at_next_pos == invalid_path) return false;
 
	if (at_next_pos == Path::invalid_path) return false;
 

	
 
	if (at_next_pos == NULL) {
 
		/* Summarize paths; add up the paths with the same source and next hop
 
		 * in one path each. */
 
		PathList &paths = this->job[next_id].Paths();
 
		PathViaMap next_hops;
 
@@ -428,13 +426,13 @@ bool MCF1stPass::EliminateCycles(PathVec
 
		}
 
		/* All paths departing from this node have been searched. Mark as
 
		 * resolved if no cycles found. If cycles were found further cycles
 
		 * could be found in this branch, thus it has to be searched again next
 
		 * time we spot it.
 
		 */
 
		path[next_id] = found ? NULL : invalid_path;
 
		path[next_id] = found ? NULL : Path::invalid_path;
 
		return found;
 
	}
 

	
 
	/* This node has already been visited => we have a cycle.
 
	 * Backtrack to find the exact flow. */
 
	uint flow = this->FindCycleFlow(path, at_next_pos);
src/saveload/linkgraph_sl.cpp
Show inline comments
 
@@ -217,13 +217,13 @@ static void Load_LGRJ()
 

	
 
/**
 
 * Load the link graph schedule.
 
 */
 
static void Load_LGRS()
 
{
 
	SlObject(LinkGraphSchedule::Instance(), GetLinkGraphScheduleDesc());
 
	SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc());
 
}
 

	
 
/**
 
 * Spawn the threads for running link graph calculations.
 
 * Has to be done after loading as the cargo classes might have changed.
 
 */
 
@@ -243,13 +243,13 @@ void AfterLoadLinkGraphs()
 
			for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) {
 
				(*lg)[node_id].UpdateLocation(Station::Get((*lg)[node_id].Station())->xy);
 
			}
 
		}
 
	}
 

	
 
	LinkGraphSchedule::Instance()->SpawnAll();
 
	LinkGraphSchedule::instance.SpawnAll();
 
}
 

	
 
/**
 
 * Save all link graphs.
 
 */
 
static void Save_LGRP()
 
@@ -275,21 +275,21 @@ static void Save_LGRJ()
 

	
 
/**
 
 * Save the link graph schedule.
 
 */
 
static void Save_LGRS()
 
{
 
	SlObject(LinkGraphSchedule::Instance(), GetLinkGraphScheduleDesc());
 
	SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc());
 
}
 

	
 
/**
 
 * Substitute pointers in link graph schedule.
 
 */
 
static void Ptrs_LGRS()
 
{
 
	SlObject(LinkGraphSchedule::Instance(), GetLinkGraphScheduleDesc());
 
	SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc());
 
}
 

	
 
extern const ChunkHandler _linkgraph_chunk_handlers[] = {
 
	{ 'LGRP', Save_LGRP, Load_LGRP, NULL,      NULL, CH_ARRAY },
 
	{ 'LGRJ', Save_LGRJ, Load_LGRJ, NULL,      NULL, CH_ARRAY },
 
	{ 'LGRS', Save_LGRS, Load_LGRS, Ptrs_LGRS, NULL, CH_LAST  }
src/station.cpp
Show inline comments
 
@@ -103,13 +103,13 @@ Station::~Station()
 
				st->goods[c].flows.DeleteFlows(this->index);
 
				RerouteCargo(st, c, this->index, st->index);
 
			}
 
		}
 
		lg->RemoveNode(this->goods[c].node);
 
		if (lg->Size() == 0) {
 
			LinkGraphSchedule::Instance()->Unqueue(lg);
 
			LinkGraphSchedule::instance.Unqueue(lg);
 
			delete lg;
 
		}
 
	}
 

	
 
	Vehicle *v;
 
	FOR_ALL_VEHICLES(v) {
src/station_base.h
Show inline comments
 
@@ -34,12 +34,14 @@ static const byte INITIAL_STATION_RATING
 
 * mean anything by itself.
 
 */
 
class FlowStat {
 
public:
 
	typedef std::map<uint32, StationID> SharesMap;
 

	
 
	static const SharesMap empty_sharesmap;
 

	
 
	/**
 
	 * Invalid constructor. This can't be called as a FlowStat must not be
 
	 * empty. However, the constructor must be defined and reachable for
 
	 * FlwoStat to be used in a std::map.
 
	 */
 
	inline FlowStat() {NOT_REACHED();}
src/station_cmd.cpp
Show inline comments
 
@@ -56,12 +56,19 @@
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
/**
 
 * Static instance of FlowStat::SharesMap.
 
 * Note: This instance is created on task start.
 
 *       Lazy creation on first usage results in a data race between the CDist threads.
 
 */
 
/* static */ const FlowStat::SharesMap FlowStat::empty_sharesmap;
 

	
 
/**
 
 * Check whether the given tile is a hangar.
 
 * @param t the tile to of whether it is a hangar.
 
 * @pre IsTileType(t, MP_STATION)
 
 * @return true if and only if the tile is a hangar.
 
 */
 
bool IsHangar(TileIndex t)
 
@@ -3533,13 +3540,13 @@ void IncreaseStats(Station *st, CargoID 
 
	GoodsEntry &ge2 = st2->goods[cargo];
 
	LinkGraph *lg = NULL;
 
	if (ge1.link_graph == INVALID_LINK_GRAPH) {
 
		if (ge2.link_graph == INVALID_LINK_GRAPH) {
 
			if (LinkGraph::CanAllocateItem()) {
 
				lg = new LinkGraph(cargo);
 
				LinkGraphSchedule::Instance()->Queue(lg);
 
				LinkGraphSchedule::instance.Queue(lg);
 
				ge2.link_graph = lg->index;
 
				ge2.node = lg->AddNode(st2);
 
			} else {
 
				DEBUG(misc, 0, "Can't allocate link graph");
 
			}
 
		} else {
 
@@ -3555,17 +3562,17 @@ void IncreaseStats(Station *st, CargoID 
 
		ge2.node = lg->AddNode(st2);
 
	} else {
 
		lg = LinkGraph::Get(ge1.link_graph);
 
		if (ge1.link_graph != ge2.link_graph) {
 
			LinkGraph *lg2 = LinkGraph::Get(ge2.link_graph);
 
			if (lg->Size() < lg2->Size()) {
 
				LinkGraphSchedule::Instance()->Unqueue(lg);
 
				LinkGraphSchedule::instance.Unqueue(lg);
 
				lg2->Merge(lg); // Updates GoodsEntries of lg
 
				lg = lg2;
 
			} else {
 
				LinkGraphSchedule::Instance()->Unqueue(lg2);
 
				LinkGraphSchedule::instance.Unqueue(lg2);
 
				lg->Merge(lg2); // Updates GoodsEntries of lg2
 
			}
 
		}
 
	}
 
	if (lg != NULL) {
 
		(*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, mode);
 
@@ -3681,13 +3688,13 @@ static uint UpdateStationWaiting(Station
 
	StationID next = ge.GetVia(st->index);
 
	ge.cargo.Append(new CargoPacket(st->index, st->xy, amount, source_type, source_id), next);
 
	LinkGraph *lg = NULL;
 
	if (ge.link_graph == INVALID_LINK_GRAPH) {
 
		if (LinkGraph::CanAllocateItem()) {
 
			lg = new LinkGraph(type);
 
			LinkGraphSchedule::Instance()->Queue(lg);
 
			LinkGraphSchedule::instance.Queue(lg);
 
			ge.link_graph = lg->index;
 
			ge.node = lg->AddNode(st);
 
		} else {
 
			DEBUG(misc, 0, "Can't allocate link graph");
 
		}
 
	} else {
src/toolbar_gui.cpp
Show inline comments
 
@@ -1122,13 +1122,13 @@ void ToggleDirtyBlocks()
 
 */
 
void SetStartingYear(Year year)
 
{
 
	_settings_game.game_creation.starting_year = Clamp(year, MIN_YEAR, MAX_YEAR);
 
	Date new_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
 
	/* If you open a savegame as scenario there may already be link graphs.*/
 
	LinkGraphSchedule::Instance()->ShiftDates(new_date - _date);
 
	LinkGraphSchedule::instance.ShiftDates(new_date - _date);
 
	SetDate(new_date, 0);
 
}
 

	
 
/**
 
 * Choose the proper callback function for the main toolbar's help menu.
 
 * @param index The menu index which was selected.
0 comments (0 inline, 0 general)