00001 /* $Id: linkgraphschedule.cpp 26276 2014-01-24 21:39:07Z fonsinchen $ */ 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 LinkGraph *first = next; 00061 while (next->Size() < 2) { 00062 this->schedule.splice(this->schedule.end(), this->schedule, this->schedule.begin()); 00063 next = this->schedule.front(); 00064 if (next == first) return; 00065 } 00066 assert(next == LinkGraph::Get(next->index)); 00067 this->schedule.pop_front(); 00068 if (LinkGraphJob::CanAllocateItem()) { 00069 LinkGraphJob *job = new LinkGraphJob(*next); 00070 this->SpawnThread(job); 00071 this->running.push_back(job); 00072 } else { 00073 NOT_REACHED(); 00074 } 00075 } 00076 00080 void LinkGraphSchedule::JoinNext() 00081 { 00082 if (this->running.empty()) return; 00083 LinkGraphJob *next = this->running.front(); 00084 if (!next->IsFinished()) return; 00085 this->running.pop_front(); 00086 LinkGraphID id = next->LinkGraphIndex(); 00087 this->JoinThread(next); 00088 delete next; 00089 if (LinkGraph::IsValidID(id)) { 00090 LinkGraph *lg = LinkGraph::Get(id); 00091 this->Unqueue(lg); // Unqueue to avoid double-queueing recycled IDs. 00092 this->Queue(lg); 00093 } 00094 } 00095 00101 /* static */ void LinkGraphSchedule::Run(void *j) 00102 { 00103 LinkGraphJob *job = (LinkGraphJob *)j; 00104 LinkGraphSchedule *schedule = LinkGraphSchedule::Instance(); 00105 for (uint i = 0; i < lengthof(schedule->handlers); ++i) { 00106 schedule->handlers[i]->Run(*job); 00107 } 00108 } 00109 00114 void LinkGraphSchedule::SpawnAll() 00115 { 00116 for (JobList::iterator i = this->running.begin(); i != this->running.end(); ++i) { 00117 this->SpawnThread(*i); 00118 } 00119 } 00120 00124 /* static */ void LinkGraphSchedule::Clear() 00125 { 00126 LinkGraphSchedule *inst = LinkGraphSchedule::Instance(); 00127 for (JobList::iterator i(inst->running.begin()); i != inst->running.end(); ++i) { 00128 inst->JoinThread(*i); 00129 } 00130 inst->running.clear(); 00131 inst->schedule.clear(); 00132 } 00133 00139 void LinkGraphSchedule::ShiftDates(int interval) 00140 { 00141 LinkGraph *lg; 00142 FOR_ALL_LINK_GRAPHS(lg) lg->ShiftDates(interval); 00143 LinkGraphJob *lgj; 00144 FOR_ALL_LINK_GRAPH_JOBS(lgj) lgj->ShiftJoinDate(interval); 00145 } 00146 00150 LinkGraphSchedule::LinkGraphSchedule() 00151 { 00152 this->handlers[0] = new InitHandler; 00153 this->handlers[1] = new DemandHandler; 00154 this->handlers[2] = new MCFHandler<MCF1stPass>; 00155 this->handlers[3] = new FlowMapper(false); 00156 this->handlers[4] = new MCFHandler<MCF2ndPass>; 00157 this->handlers[5] = new FlowMapper(true); 00158 } 00159 00163 LinkGraphSchedule::~LinkGraphSchedule() 00164 { 00165 this->Clear(); 00166 for (uint i = 0; i < lengthof(this->handlers); ++i) { 00167 delete this->handlers[i]; 00168 } 00169 } 00170 00174 /* static */ LinkGraphSchedule *LinkGraphSchedule::Instance() 00175 { 00176 static LinkGraphSchedule inst; 00177 return &inst; 00178 } 00179 00184 void OnTick_LinkGraph() 00185 { 00186 if (_date_fract != LinkGraphSchedule::SPAWN_JOIN_TICK) return; 00187 Date offset = _date % _settings_game.linkgraph.recalc_interval; 00188 if (offset == 0) { 00189 LinkGraphSchedule::Instance()->SpawnNext(); 00190 } else if (offset == _settings_game.linkgraph.recalc_interval / 2) { 00191 LinkGraphSchedule::Instance()->JoinNext(); 00192 } 00193 } 00194 00195