/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Christian Schulte * * Copyright: * Christian Schulte, 2009 * * This file is part of Gecode, the generic constraint * development environment: * http://www.gecode.org * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ namespace Gecode { namespace Search { namespace Par { /* * Basic access routines */ template forceinline Engine& Engine::Worker::engine(void) const { return _engine; } template forceinline const Options& Engine::opt(void) const { return _opt; } template forceinline unsigned int Engine::workers(void) const { return static_cast(opt().threads); } template forceinline bool Engine::stopped(void) const { return has_stopped; } /* * Engine: command and wait handling */ template forceinline typename Engine::Cmd Engine::cmd(void) const { return _cmd; } template forceinline void Engine::block(void) { _cmd = C_WAIT; Support::Thread::acquireGlobalMutex(&_m_wait); } template forceinline void Engine::release(Cmd c) { _cmd = c; Support::Thread::releaseGlobalMutex(&_m_wait); } template forceinline void Engine::wait(void) { _m_wait.acquire(); _m_wait.release(); } /* * Engine: initialization */ template forceinline Engine::Worker::Worker(Space* s, Engine& e) : tracer(e.opt().tracer), _engine(e), path(s == nullptr ? 0 : e.opt().nogoods_limit), d(0), idle(false) { tracer.worker(); if (s != nullptr) { if (s->status(*this) == SS_FAILED) { fail++; cur = nullptr; if (!engine().opt().clone) delete s; } else { cur = snapshot(s,engine().opt()); } } else { cur = nullptr; } } template forceinline Engine::Engine(const Options& o) : _opt(o), solutions(heap) { // Initialize termination information _n_term_not_ack = workers(); _n_not_terminated = workers(); // Initialize search information n_busy = workers(); has_stopped = false; // Initialize reset information _n_reset_not_ack = workers(); } /* * Statistics */ template forceinline Statistics Engine::Worker::statistics(void) { m.acquire(); Statistics s = *this; m.release(); return s; } /* * Engine: search control */ template forceinline bool Engine::signal(void) const { return solutions.empty() && (n_busy > 0) && !has_stopped; } template forceinline void Engine::idle(void) { m_search.acquire(); bool bs = signal(); n_busy--; if (bs && (n_busy == 0)) e_search.signal(); m_search.release(); } template forceinline void Engine::busy(void) { m_search.acquire(); assert(n_busy > 0); n_busy++; m_search.release(); } template forceinline void Engine::stop(void) { m_search.acquire(); bool bs = signal(); has_stopped = true; if (bs) e_search.signal(); m_search.release(); } /* * Engine: termination control */ template forceinline void Engine::terminated(void) { unsigned int n; _m_term.acquire(); n = --_n_not_terminated; _m_term.release(); // The signal must be outside of the look, otherwise a thread might be // terminated that still holds a mutex. if (n == 0) _e_terminate.signal(); } template forceinline void Engine::ack_terminate(void) { _m_term.acquire(); if (--_n_term_not_ack == 0) _e_term_ack.signal(); _m_term.release(); } template forceinline void Engine::wait_terminate(void) { _m_wait_terminate.acquire(); _m_wait_terminate.release(); } template forceinline void Engine::terminate(void) { // Grab the wait mutex for termination _m_wait_terminate.acquire(); // Release all threads release(C_TERMINATE); // Wait until all threads have acknowledged termination request _e_term_ack.wait(); // Release waiting threads _m_wait_terminate.release(); // Wait until all threads have in fact terminated _e_terminate.wait(); // Now all threads are terminated! } /* * Engine: reset control */ template forceinline void Engine::ack_reset_start(void) { _m_reset.acquire(); if (--_n_reset_not_ack == 0) e_reset_ack_start.signal(); _m_reset.release(); } template forceinline void Engine::ack_reset_stop(void) { _m_reset.acquire(); if (++_n_reset_not_ack == workers()) e_reset_ack_stop.signal(); _m_reset.release(); } template forceinline void Engine::wait_reset(void) { m_wait_reset.acquire(); m_wait_reset.release(); } /* * Worker: finding and stealing working */ template forceinline Space* Engine::Worker::steal(unsigned long int& d, Tracer& myt, Tracer& ot) { /* * Make a quick check whether the worker might have work * * If that is not true any longer, the worker will be asked * again eventually. */ if (!path.steal()) return nullptr; m.acquire(); Space* s = path.steal(*this,d,myt,ot); m.release(); // Tell that there will be one more busy worker if (s != nullptr) engine().busy(); return s; } /* * Return No-Goods */ template forceinline NoGoods& Engine::Worker::nogoods(void) { return path; } /* * Engine: search control */ template Space* Engine::next(void) { // Invariant: the worker holds the wait mutex m_search.acquire(); if (!solutions.empty()) { // No search needs to be done, take leftover solution Space* s = solutions.pop(); m_search.release(); return s; } // We ignore stopped (it will be reported again if needed) has_stopped = false; // No more solutions? if (n_busy == 0) { m_search.release(); return nullptr; } m_search.release(); // Okay, now search has to continue, make the guys work release(C_WORK); /* * Wait until a search related event has happened. It might be that * the event has already been signalled in the last run, but the * solution has been removed. So we have to try until there has * something new happened. */ while (true) { e_search.wait(); m_search.acquire(); if (!solutions.empty()) { // Report solution Space* s = solutions.pop(); m_search.release(); // Make workers wait again block(); return s; } // No more solutions or stopped? if ((n_busy == 0) || has_stopped) { m_search.release(); // Make workers wait again block(); return nullptr; } m_search.release(); } GECODE_NEVER; return nullptr; } template Support::Terminator* Engine::Worker::terminator(void) const { return &_engine; } /* * Termination and deletion */ template Engine::Worker::~Worker(void) { delete cur; path.reset(0); tracer.done(); } }}} // STATISTICS: search-par