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, 2017 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 34 35namespace Gecode { namespace Support { 36 37 /// Baseclass for jobs with return type \a RetType 38 template<class RetType> 39 class Job { 40 public: 41 /// Run a job with iterator index \a i 42 virtual RetType run(int i) = 0; 43 /// Destructor 44 virtual ~Job(void) {} 45 }; 46 47 /// Class to throw an exception to stop new jobs from being started 48 template<class RetType> 49 class JobStop { 50 protected: 51 /// The result stored 52 RetType r; 53 public: 54 /// Constructor 55 JobStop(RetType r0); 56 /// Return the passed result 57 RetType result(void) const; 58 }; 59 60 template<class Jobs, class RetType> 61 /** 62 * \brief Parallel iterator that runs jobs with a given number of threads 63 * 64 * It takes an iterator over jobs as input and acts as an iterator 65 * over the jobs' results. The order of iteration is not kept. The 66 * iterator runs several jobs as defined by the input iterator in 67 * parallel using a maximal number of threads. 68 * 69 * The iterator can be stopped creating new jobs if one of the running 70 * jobs throws an exception of type \a JobStop. In that case the result 71 * of the stopped job is defined by the default constructor of the 72 * return type \a RetType. Already running jobs are not stopped and 73 * deliver their results as usual. 74 * 75 */ 76 class RunJobs { 77 protected: 78 class Master; 79 /// The actual worker using a thread to run a job 80 class Worker : public Runnable { 81 protected: 82 /// The job to run 83 Job<RetType>* job; 84 /// The master to communicate with 85 Master* master; 86 /// Original iterator index of job 87 int idx; 88 public: 89 /// Initialize worker 90 Worker(Job<RetType>* j, Master* m, int i); 91 /// Run jobs 92 virtual void run(void); 93 /// Nothing to delete (done in run) 94 virtual ~Worker(void); 95 }; 96 class Master { 97 protected: 98 /// Mutex for synchronizing access 99 Mutex m; 100 /// Event is triggered if a the first job is added to queue 101 Event e; 102 /// Number of threads currently not in use 103 unsigned int n_threads; 104 /// Input jobs 105 Jobs& jobs; 106 /// Index of next job to be created 107 int idx; 108 /// Result from a the first stopped job (passed in exception) 109 RetType sres; 110 /// Index of the first stop that has been stopped (-1 if none) 111 int sidx; 112 /// Queue of not yet requested results 113 DynamicQueue<RetType,Heap> rs; 114 public: 115 /// Get next job witth index \a i, if possible 116 Job<RetType>* next(int& i); 117 /// Report result \a r by a worker 118 void report(RetType r); 119 /// Report that a job with index \a i has been stopped 120 void stop(RetType r, int i); 121 /// Test whether all jobs are done 122 bool done(void) const; 123 /// Initialize with job iterator \a j and maximal number of threads \a m 124 Master(Jobs& j, unsigned int m); 125 /// Run next job and return true if succesful and assign \a r to its result 126 bool run(RetType& r); 127 /// Whether a job has thrown a \a JobStop exception 128 bool stopped(void) const; 129 /// Return index of first job that has thrown a \a JobStop exception (-1 if none) with its result 130 int stoppedjob(RetType& r) const; 131 /// Whether a new thread is needed for deletion 132 bool needthread(void); 133 /// Destructor 134 ~Master(void); 135 }; 136 /// A class to delete the master (running in parallel) 137 class Deleter : public Runnable { 138 protected: 139 /// The master to be deleted 140 Master* master; 141 public: 142 /// Initialize with master \a m 143 Deleter(Master* m); 144 /// Perform deletion 145 virtual void run(void); 146 }; 147 /// The actual master 148 Master* master; 149 public: 150 /// Initialize with job iterator \a j and maximal number of threads \a m 151 RunJobs(Jobs& j, unsigned int m); 152 /// Run next job and return true if succesful and assign \a r to its result 153 bool run(RetType& r); 154 /// Whether a job has thrown a \a JobStop exception with index \a i and result \a r 155 bool stopped(int& i, RetType& r) const; 156 /// Destructor 157 ~RunJobs(void); 158 }; 159 160 161 162 template<class RetType> 163 forceinline 164 JobStop<RetType>::JobStop(RetType r0) : r(r0) {} 165 166 template<class RetType> 167 forceinline RetType 168 JobStop<RetType>::result(void) const { 169 return r; 170 } 171 172 173 174 template<class Jobs, class RetType> 175 forceinline 176 RunJobs<Jobs,RetType>::Worker::Worker(Job<RetType>* j, 177 Master* m, 178 int i) 179 : Runnable(true), job(j), master(m), idx(i) {} 180 181 template<class Jobs, class RetType> 182 RunJobs<Jobs,RetType>::Worker::~Worker(void) {} 183 184 185 template<class Jobs, class RetType> 186 inline int 187 RunJobs<Jobs,RetType>::Master::stoppedjob(RetType& r) const { 188 r = sres; 189 return sidx; 190 } 191 192 template<class Jobs, class RetType> 193 inline bool 194 RunJobs<Jobs,RetType>::Master::stopped(void) const { 195 return sidx >= 0; 196 } 197 198 template<class Jobs, class RetType> 199 forceinline Job<RetType>* 200 RunJobs<Jobs,RetType>::Master::next(int& i) { 201 m.acquire(); 202 Job<RetType>* j; 203 if (jobs() && !stopped()) { 204 j = jobs.job(); i=idx++; 205 } else { 206 j = nullptr; 207 n_threads--; 208 e.signal(); 209 } 210 m.release(); 211 return j; 212 } 213 214 template<class Jobs, class RetType> 215 forceinline void 216 RunJobs<Jobs,RetType>::Master::report(RetType r) { 217 m.acquire(); 218 rs.push(r); 219 e.signal(); 220 m.release(); 221 } 222 223 template<class Jobs, class RetType> 224 forceinline void 225 RunJobs<Jobs,RetType>::Master::stop(RetType r, int i) { 226 m.acquire(); 227 if (!stopped()) { 228 sres=r; sidx = i; 229 } 230 rs.push(r); 231 e.signal(); 232 m.release(); 233 } 234 235 template<class Jobs, class RetType> 236 void 237 RunJobs<Jobs,RetType>::Worker::run(void) { 238 do { 239 try { 240 RetType r = job->run(idx); 241 master->report(r); 242 } catch (JobStop<RetType>& js) { 243 master->stop(js.result(),idx); 244 } 245 delete job; 246 job = master->next(idx); 247 } while (job != nullptr); 248 } 249 250 template<class Jobs, class RetType> 251 forceinline bool 252 RunJobs<Jobs,RetType>::Master::done(void) const { 253 return (n_threads == 0) && (!jobs() || stopped()) && rs.empty(); 254 } 255 256 template<class Jobs, class RetType> 257 inline 258 RunJobs<Jobs,RetType>::Master::Master(Jobs& j, unsigned int m_threads) 259 : n_threads(0), jobs(j), idx(0), sidx(-1), rs(heap) { 260 m.acquire(); 261 while ((n_threads < m_threads) && jobs()) { 262 if (stopped()) 263 break; 264 Thread::run(new Worker(jobs.job(),this,idx)); 265 n_threads++; idx++; 266 } 267 m.release(); 268 } 269 270 template<class Jobs, class RetType> 271 inline bool 272 RunJobs<Jobs,RetType>::Master::run(RetType& r) { 273 m.acquire(); 274 if (done()) { 275 m.release(); 276 return false; 277 } 278 if (!rs.empty()) { 279 r = rs.pop(); 280 m.release(); 281 return true; 282 } 283 m.release(); 284 while (true) { 285 e.wait(); 286 m.acquire(); 287 if (done()) { 288 m.release(); 289 return false; 290 } 291 if (!rs.empty()) { 292 r = rs.pop(); 293 m.release(); 294 return true; 295 } 296 m.release(); 297 } 298 GECODE_NEVER; 299 } 300 301 template<class Jobs, class RetType> 302 inline bool 303 RunJobs<Jobs,RetType>::Master::needthread(void) { 304 bool n; 305 m.acquire(); 306 while (!rs.empty()) 307 rs.pop().~RetType(); 308 sidx = 0; 309 n = !done(); 310 m.release(); 311 return n; 312 } 313 314 template<class Jobs, class RetType> 315 inline 316 RunJobs<Jobs,RetType>::Master::~Master(void) { 317 sidx = 0; 318 RetType r; 319 while (run(r)) 320 r.~RetType(); 321 } 322 323 template<class Jobs, class RetType> 324 forceinline 325 RunJobs<Jobs,RetType>::Deleter::Deleter(Master* m) 326 : Runnable(true), master(m) {} 327 328 template<class Jobs, class RetType> 329 void 330 RunJobs<Jobs,RetType>::Deleter::run(void) { 331 delete master; 332 } 333 334 335 336 337 338 template<class Jobs, class RetType> 339 inline bool 340 RunJobs<Jobs,RetType>::stopped(int& i, RetType& r) const { 341 i = master->stoppedjob(r); 342 return i >= 0; 343 } 344 345 template<class Jobs, class RetType> 346 inline 347 RunJobs<Jobs,RetType>::RunJobs(Jobs& j, unsigned int m) 348 : master(new Master(j,m)) {} 349 350 template<class Jobs, class RetType> 351 inline bool 352 RunJobs<Jobs,RetType>::run(RetType& r) { 353 return master->run(r); 354 } 355 356 template<class Jobs, class RetType> 357 inline 358 RunJobs<Jobs,RetType>::~RunJobs(void) { 359 if (!master->needthread()) 360 delete master; 361 else 362 Thread::run(new Deleter(master)); 363 } 364 365 366}} 367 368// STATISTICS: support-any 369