00001 /* $Id$ */ 00002 00003 /* 00004 * This file is part of OpenTTD. 00005 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. 00006 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00007 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. 00008 */ 00009 00012 #include "../stdafx.h" 00013 #include "linkgraphschedule.h" 00014 #include "init.h" 00015 #include "demands.h" 00016 #include "mcf.h" 00017 #include "flowmapper.h" 00018 00024 void LinkGraphSchedule::SpawnThread(LinkGraphJob *job) 00025 { 00026 if (!ThreadObject::New(&(LinkGraphSchedule::Run), job, &job->thread)) { 00027 job->thread = NULL; 00028 /* Of course this will hang a bit. 00029 * On the other hand, if you want to play games which make this hang noticably 00030 * on a platform without threads then you'll probably get other problems first. 00031 * OK: 00032 * If someone comes and tells me that this hangs for him/her, I'll implement a 00033 * smaller grained "Step" method for all handlers and add some more ticks where 00034 * "Step" is called. No problem in principle. 00035 */ 00036 LinkGraphSchedule::Run(job); 00037 } 00038 } 00039 00044 void LinkGraphSchedule::JoinThread(LinkGraphJob *job) 00045 { 00046 if (job->thread != NULL) { 00047 job->thread->Join(); 00048 delete job->thread; 00049 job->thread = NULL; 00050 } 00051 } 00052 00056 void LinkGraphSchedule::SpawnNext() 00057 { 00058 if (this->schedule.empty()) return; 00059 LinkGraph *next = this->schedule.front(); 00060 assert(next == LinkGraph::Get(next->index)); 00061 this->schedule.pop_front(); 00062 if (LinkGraphJob::CanAllocateItem()) { 00063 LinkGraphJob *job = new LinkGraphJob(*next); 00064 this->SpawnThread(job); 00065 this->running.push_back(job); 00066 } else { 00067 NOT_REACHED(); 00068 } 00069 } 00070 00074 void LinkGraphSchedule::JoinNext() 00075 { 00076 if (this->running.empty()) return; 00077 LinkGraphJob *next = this->running.front(); 00078 if (!next->IsFinished()) return; 00079 this->running.pop_front(); 00080 LinkGraphID id = next->LinkGraphIndex(); 00081 this->JoinThread(next); 00082 delete next; 00083 if (LinkGraph::IsValidID(id)) { 00084 LinkGraph *lg = LinkGraph::Get(id); 00085 this->Unqueue(lg); // Unqueue to avoid double-queueing recycled IDs. 00086 this->Queue(lg); 00087 } 00088 } 00089 00095 /* static */ void LinkGraphSchedule::Run(void *j) 00096 { 00097 LinkGraphJob *job = (LinkGraphJob *)j; 00098 LinkGraphSchedule *schedule = LinkGraphSchedule::Instance(); 00099 for (uint i = 0; i < lengthof(schedule->handlers); ++i) { 00100 schedule->handlers[i]->Run(*job); 00101 } 00102 } 00103 00108 void LinkGraphSchedule::SpawnAll() 00109 { 00110 for (JobList::iterator i = this->running.begin(); i != this->running.end(); ++i) { 00111 this->SpawnThread(*i); 00112 } 00113 } 00114 00118 /* static */ void LinkGraphSchedule::Clear() 00119 { 00120 LinkGraphSchedule *inst = LinkGraphSchedule::Instance(); 00121 for (JobList::iterator i(inst->running.begin()); i != inst->running.end(); ++i) { 00122 inst->JoinThread(*i); 00123 } 00124 inst->running.clear(); 00125 inst->schedule.clear(); 00126 } 00127 00131 LinkGraphSchedule::LinkGraphSchedule() 00132 { 00133 this->handlers[0] = new InitHandler; 00134 this->handlers[1] = new DemandHandler; 00135 this->handlers[2] = new MCFHandler<MCF1stPass>; 00136 this->handlers[3] = new FlowMapper; 00137 this->handlers[4] = new MCFHandler<MCF2ndPass>; 00138 this->handlers[5] = new FlowMapper; 00139 } 00140 00144 LinkGraphSchedule::~LinkGraphSchedule() 00145 { 00146 this->Clear(); 00147 for (uint i = 0; i < lengthof(this->handlers); ++i) { 00148 delete this->handlers[i]; 00149 } 00150 } 00151 00155 /* static */ LinkGraphSchedule *LinkGraphSchedule::Instance() 00156 { 00157 static LinkGraphSchedule inst; 00158 return &inst; 00159 } 00160 00165 void OnTick_LinkGraph() 00166 { 00167 if (_date_fract == LinkGraphSchedule::SPAWN_JOIN_TICK) { 00168 Date offset = _date % _settings_game.linkgraph.recalc_interval; 00169 if (offset == 0) { 00170 LinkGraphSchedule::Instance()->SpawnNext(); 00171 } else if (offset == _settings_game.linkgraph.recalc_interval / 2) { 00172 LinkGraphSchedule::Instance()->JoinNext(); 00173 } 00174 } else if (_date_fract == LinkGraph::COMPRESSION_TICK) { 00175 LinkGraph *lg; 00176 /* Compress graphs after 256 to 512 days; approximately once a year. */ 00177 int cutoff = RandomRange(256) + 256; 00178 FOR_ALL_LINK_GRAPHS(lg) { 00179 if (_date - lg->LastCompression() > cutoff) lg->Compress(); 00180 } 00181 } 00182 } 00183 00184