this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2/* 3 * Main authors: 4 * Christian Schulte <schulte@gecode.org> 5 * 6 * Copyright: 7 * Christian Schulte, 2009 8 * 9 * This file is part of Gecode, the generic constraint 10 * development environment: 11 * http://www.gecode.org 12 * 13 * Permission is hereby granted, free of charge, to any person obtaining 14 * a copy of this software and associated documentation files (the 15 * "Software"), to deal in the Software without restriction, including 16 * without limitation the rights to use, copy, modify, merge, publish, 17 * distribute, sublicense, and/or sell copies of the Software, and to 18 * permit persons to whom the Software is furnished to do so, subject to 19 * the following conditions: 20 * 21 * The above copyright notice and this permission notice shall be 22 * included in all copies or substantial portions of the Software. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 * 32 */ 33 34namespace Gecode { namespace Search { namespace Par { 35 36 37 /* 38 * Basic access routines 39 */ 40 template<class Tracer> 41 forceinline Engine<Tracer>& 42 Engine<Tracer>::Worker::engine(void) const { 43 return _engine; 44 } 45 template<class Tracer> 46 forceinline const Options& 47 Engine<Tracer>::opt(void) const { 48 return _opt; 49 } 50 template<class Tracer> 51 forceinline unsigned int 52 Engine<Tracer>::workers(void) const { 53 return static_cast<unsigned int>(opt().threads); 54 } 55 template<class Tracer> 56 forceinline bool 57 Engine<Tracer>::stopped(void) const { 58 return has_stopped; 59 } 60 61 62 63 /* 64 * Engine: command and wait handling 65 */ 66 template<class Tracer> 67 forceinline typename Engine<Tracer>::Cmd 68 Engine<Tracer>::cmd(void) const { 69 return _cmd; 70 } 71 template<class Tracer> 72 forceinline void 73 Engine<Tracer>::block(void) { 74 _cmd = C_WAIT; 75 Support::Thread::acquireGlobalMutex(&_m_wait); 76 } 77 template<class Tracer> 78 forceinline void 79 Engine<Tracer>::release(Cmd c) { 80 _cmd = c; 81 Support::Thread::releaseGlobalMutex(&_m_wait); 82 } 83 template<class Tracer> 84 forceinline void 85 Engine<Tracer>::wait(void) { 86 _m_wait.acquire(); _m_wait.release(); 87 } 88 89 90 /* 91 * Engine: initialization 92 */ 93 template<class Tracer> 94 forceinline 95 Engine<Tracer>::Worker::Worker(Space* s, Engine& e) 96 : tracer(e.opt().tracer), _engine(e), 97 path(s == nullptr ? 0 : e.opt().nogoods_limit), d(0), 98 idle(false) { 99 tracer.worker(); 100 if (s != nullptr) { 101 if (s->status(*this) == SS_FAILED) { 102 fail++; 103 cur = nullptr; 104 if (!engine().opt().clone) 105 delete s; 106 } else { 107 cur = snapshot(s,engine().opt()); 108 } 109 } else { 110 cur = nullptr; 111 } 112 } 113 114 template<class Tracer> 115 forceinline 116 Engine<Tracer>::Engine(const Options& o) 117 : _opt(o), solutions(heap) { 118 // Initialize termination information 119 _n_term_not_ack = workers(); 120 _n_not_terminated = workers(); 121 // Initialize search information 122 n_busy = workers(); 123 has_stopped = false; 124 // Initialize reset information 125 _n_reset_not_ack = workers(); 126 } 127 128 129 /* 130 * Statistics 131 */ 132 template<class Tracer> 133 forceinline Statistics 134 Engine<Tracer>::Worker::statistics(void) { 135 m.acquire(); 136 Statistics s = *this; 137 m.release(); 138 return s; 139 } 140 141 142 /* 143 * Engine: search control 144 */ 145 template<class Tracer> 146 forceinline bool 147 Engine<Tracer>::signal(void) const { 148 return solutions.empty() && (n_busy > 0) && !has_stopped; 149 } 150 template<class Tracer> 151 forceinline void 152 Engine<Tracer>::idle(void) { 153 m_search.acquire(); 154 bool bs = signal(); 155 n_busy--; 156 if (bs && (n_busy == 0)) 157 e_search.signal(); 158 m_search.release(); 159 } 160 161 template<class Tracer> 162 forceinline void 163 Engine<Tracer>::busy(void) { 164 m_search.acquire(); 165 assert(n_busy > 0); 166 n_busy++; 167 m_search.release(); 168 } 169 170 template<class Tracer> 171 forceinline void 172 Engine<Tracer>::stop(void) { 173 m_search.acquire(); 174 bool bs = signal(); 175 has_stopped = true; 176 if (bs) 177 e_search.signal(); 178 m_search.release(); 179 } 180 181 182 /* 183 * Engine: termination control 184 */ 185 template<class Tracer> 186 forceinline void 187 Engine<Tracer>::terminated(void) { 188 unsigned int n; 189 _m_term.acquire(); 190 n = --_n_not_terminated; 191 _m_term.release(); 192 // The signal must be outside of the look, otherwise a thread might be 193 // terminated that still holds a mutex. 194 if (n == 0) 195 _e_terminate.signal(); 196 } 197 198 template<class Tracer> 199 forceinline void 200 Engine<Tracer>::ack_terminate(void) { 201 _m_term.acquire(); 202 if (--_n_term_not_ack == 0) 203 _e_term_ack.signal(); 204 _m_term.release(); 205 } 206 207 template<class Tracer> 208 forceinline void 209 Engine<Tracer>::wait_terminate(void) { 210 _m_wait_terminate.acquire(); 211 _m_wait_terminate.release(); 212 } 213 214 template<class Tracer> 215 forceinline void 216 Engine<Tracer>::terminate(void) { 217 // Grab the wait mutex for termination 218 _m_wait_terminate.acquire(); 219 // Release all threads 220 release(C_TERMINATE); 221 // Wait until all threads have acknowledged termination request 222 _e_term_ack.wait(); 223 // Release waiting threads 224 _m_wait_terminate.release(); 225 // Wait until all threads have in fact terminated 226 _e_terminate.wait(); 227 // Now all threads are terminated! 228 } 229 230 /* 231 * Engine: reset control 232 */ 233 template<class Tracer> 234 forceinline void 235 Engine<Tracer>::ack_reset_start(void) { 236 _m_reset.acquire(); 237 if (--_n_reset_not_ack == 0) 238 e_reset_ack_start.signal(); 239 _m_reset.release(); 240 } 241 242 template<class Tracer> 243 forceinline void 244 Engine<Tracer>::ack_reset_stop(void) { 245 _m_reset.acquire(); 246 if (++_n_reset_not_ack == workers()) 247 e_reset_ack_stop.signal(); 248 _m_reset.release(); 249 } 250 251 template<class Tracer> 252 forceinline void 253 Engine<Tracer>::wait_reset(void) { 254 m_wait_reset.acquire(); 255 m_wait_reset.release(); 256 } 257 258 259 260 /* 261 * Worker: finding and stealing working 262 */ 263 template<class Tracer> 264 forceinline Space* 265 Engine<Tracer>::Worker::steal(unsigned long int& d, 266 Tracer& myt, Tracer& ot) { 267 /* 268 * Make a quick check whether the worker might have work 269 * 270 * If that is not true any longer, the worker will be asked 271 * again eventually. 272 */ 273 if (!path.steal()) 274 return nullptr; 275 m.acquire(); 276 Space* s = path.steal(*this,d,myt,ot); 277 m.release(); 278 // Tell that there will be one more busy worker 279 if (s != nullptr) 280 engine().busy(); 281 return s; 282 } 283 284 /* 285 * Return No-Goods 286 */ 287 template<class Tracer> 288 forceinline NoGoods& 289 Engine<Tracer>::Worker::nogoods(void) { 290 return path; 291 } 292 293 /* 294 * Engine: search control 295 */ 296 template<class Tracer> 297 Space* 298 Engine<Tracer>::next(void) { 299 // Invariant: the worker holds the wait mutex 300 m_search.acquire(); 301 if (!solutions.empty()) { 302 // No search needs to be done, take leftover solution 303 Space* s = solutions.pop(); 304 m_search.release(); 305 return s; 306 } 307 // We ignore stopped (it will be reported again if needed) 308 has_stopped = false; 309 // No more solutions? 310 if (n_busy == 0) { 311 m_search.release(); 312 return nullptr; 313 } 314 m_search.release(); 315 // Okay, now search has to continue, make the guys work 316 release(C_WORK); 317 318 /* 319 * Wait until a search related event has happened. It might be that 320 * the event has already been signalled in the last run, but the 321 * solution has been removed. So we have to try until there has 322 * something new happened. 323 */ 324 while (true) { 325 e_search.wait(); 326 m_search.acquire(); 327 if (!solutions.empty()) { 328 // Report solution 329 Space* s = solutions.pop(); 330 m_search.release(); 331 // Make workers wait again 332 block(); 333 return s; 334 } 335 // No more solutions or stopped? 336 if ((n_busy == 0) || has_stopped) { 337 m_search.release(); 338 // Make workers wait again 339 block(); 340 return nullptr; 341 } 342 m_search.release(); 343 } 344 GECODE_NEVER; 345 return nullptr; 346 } 347 348 template<class Tracer> 349 Support::Terminator* 350 Engine<Tracer>::Worker::terminator(void) const { 351 return &_engine; 352 } 353 354 /* 355 * Termination and deletion 356 */ 357 template<class Tracer> 358 Engine<Tracer>::Worker::~Worker(void) { 359 delete cur; 360 path.reset(0); 361 tracer.done(); 362 } 363 364}}} 365 366// STATISTICS: search-par