git-subtree-dir: software/gecode_base git-subtree-split: bbefcea214fec798a0f5acc442581984555acd21
367 lines
8.7 KiB
C++
367 lines
8.7 KiB
C++
/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
/*
|
|
* Main authors:
|
|
* Christian Schulte <schulte@gecode.org>
|
|
*
|
|
* 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<class Tracer>
|
|
forceinline Engine<Tracer>&
|
|
Engine<Tracer>::Worker::engine(void) const {
|
|
return _engine;
|
|
}
|
|
template<class Tracer>
|
|
forceinline const Options&
|
|
Engine<Tracer>::opt(void) const {
|
|
return _opt;
|
|
}
|
|
template<class Tracer>
|
|
forceinline unsigned int
|
|
Engine<Tracer>::workers(void) const {
|
|
return static_cast<unsigned int>(opt().threads);
|
|
}
|
|
template<class Tracer>
|
|
forceinline bool
|
|
Engine<Tracer>::stopped(void) const {
|
|
return has_stopped;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Engine: command and wait handling
|
|
*/
|
|
template<class Tracer>
|
|
forceinline typename Engine<Tracer>::Cmd
|
|
Engine<Tracer>::cmd(void) const {
|
|
return _cmd;
|
|
}
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::block(void) {
|
|
_cmd = C_WAIT;
|
|
Support::Thread::acquireGlobalMutex(&_m_wait);
|
|
}
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::release(Cmd c) {
|
|
_cmd = c;
|
|
Support::Thread::releaseGlobalMutex(&_m_wait);
|
|
}
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::wait(void) {
|
|
_m_wait.acquire(); _m_wait.release();
|
|
}
|
|
|
|
|
|
/*
|
|
* Engine: initialization
|
|
*/
|
|
template<class Tracer>
|
|
forceinline
|
|
Engine<Tracer>::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<class Tracer>
|
|
forceinline
|
|
Engine<Tracer>::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<class Tracer>
|
|
forceinline Statistics
|
|
Engine<Tracer>::Worker::statistics(void) {
|
|
m.acquire();
|
|
Statistics s = *this;
|
|
m.release();
|
|
return s;
|
|
}
|
|
|
|
|
|
/*
|
|
* Engine: search control
|
|
*/
|
|
template<class Tracer>
|
|
forceinline bool
|
|
Engine<Tracer>::signal(void) const {
|
|
return solutions.empty() && (n_busy > 0) && !has_stopped;
|
|
}
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::idle(void) {
|
|
m_search.acquire();
|
|
bool bs = signal();
|
|
n_busy--;
|
|
if (bs && (n_busy == 0))
|
|
e_search.signal();
|
|
m_search.release();
|
|
}
|
|
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::busy(void) {
|
|
m_search.acquire();
|
|
assert(n_busy > 0);
|
|
n_busy++;
|
|
m_search.release();
|
|
}
|
|
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::stop(void) {
|
|
m_search.acquire();
|
|
bool bs = signal();
|
|
has_stopped = true;
|
|
if (bs)
|
|
e_search.signal();
|
|
m_search.release();
|
|
}
|
|
|
|
|
|
/*
|
|
* Engine: termination control
|
|
*/
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::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<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::ack_terminate(void) {
|
|
_m_term.acquire();
|
|
if (--_n_term_not_ack == 0)
|
|
_e_term_ack.signal();
|
|
_m_term.release();
|
|
}
|
|
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::wait_terminate(void) {
|
|
_m_wait_terminate.acquire();
|
|
_m_wait_terminate.release();
|
|
}
|
|
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::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<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::ack_reset_start(void) {
|
|
_m_reset.acquire();
|
|
if (--_n_reset_not_ack == 0)
|
|
e_reset_ack_start.signal();
|
|
_m_reset.release();
|
|
}
|
|
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::ack_reset_stop(void) {
|
|
_m_reset.acquire();
|
|
if (++_n_reset_not_ack == workers())
|
|
e_reset_ack_stop.signal();
|
|
_m_reset.release();
|
|
}
|
|
|
|
template<class Tracer>
|
|
forceinline void
|
|
Engine<Tracer>::wait_reset(void) {
|
|
m_wait_reset.acquire();
|
|
m_wait_reset.release();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Worker: finding and stealing working
|
|
*/
|
|
template<class Tracer>
|
|
forceinline Space*
|
|
Engine<Tracer>::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<class Tracer>
|
|
forceinline NoGoods&
|
|
Engine<Tracer>::Worker::nogoods(void) {
|
|
return path;
|
|
}
|
|
|
|
/*
|
|
* Engine: search control
|
|
*/
|
|
template<class Tracer>
|
|
Space*
|
|
Engine<Tracer>::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<class Tracer>
|
|
Support::Terminator*
|
|
Engine<Tracer>::Worker::terminator(void) const {
|
|
return &_engine;
|
|
}
|
|
|
|
/*
|
|
* Termination and deletion
|
|
*/
|
|
template<class Tracer>
|
|
Engine<Tracer>::Worker::~Worker(void) {
|
|
delete cur;
|
|
path.reset(0);
|
|
tracer.done();
|
|
}
|
|
|
|
}}}
|
|
|
|
// STATISTICS: search-par
|