1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker 981be2067e Squashed 'software/gecode_on_replay/' content from commit 8051d92b9
git-subtree-dir: software/gecode_on_replay
git-subtree-split: 8051d92b9c89e49cccfbd1c201371580d7703ab4
2021-06-16 14:04:29 +10:00

370 lines
9.7 KiB
C++

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Christian Schulte <schulte@gecode.org>
*
* Copyright:
* Christian Schulte, 2017
*
* 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 Support {
/// Baseclass for jobs with return type \a RetType
template<class RetType>
class Job {
public:
/// Run a job with iterator index \a i
virtual RetType run(int i) = 0;
/// Destructor
virtual ~Job(void) {}
};
/// Class to throw an exception to stop new jobs from being started
template<class RetType>
class JobStop {
protected:
/// The result stored
RetType r;
public:
/// Constructor
JobStop(RetType r0);
/// Return the passed result
RetType result(void) const;
};
template<class Jobs, class RetType>
/**
* \brief Parallel iterator that runs jobs with a given number of threads
*
* It takes an iterator over jobs as input and acts as an iterator
* over the jobs' results. The order of iteration is not kept. The
* iterator runs several jobs as defined by the input iterator in
* parallel using a maximal number of threads.
*
* The iterator can be stopped creating new jobs if one of the running
* jobs throws an exception of type \a JobStop. In that case the result
* of the stopped job is defined by the default constructor of the
* return type \a RetType. Already running jobs are not stopped and
* deliver their results as usual.
*
*/
class RunJobs {
protected:
class Master;
/// The actual worker using a thread to run a job
class Worker : public Runnable {
protected:
/// The job to run
Job<RetType>* job;
/// The master to communicate with
Master* master;
/// Original iterator index of job
int idx;
public:
/// Initialize worker
Worker(Job<RetType>* j, Master* m, int i);
/// Run jobs
virtual void run(void);
/// Nothing to delete (done in run)
virtual ~Worker(void);
};
class Master {
protected:
/// Mutex for synchronizing access
Mutex m;
/// Event is triggered if a the first job is added to queue
Event e;
/// Number of threads currently not in use
unsigned int n_threads;
/// Input jobs
Jobs& jobs;
/// Index of next job to be created
int idx;
/// Result from a the first stopped job (passed in exception)
RetType sres;
/// Index of the first stop that has been stopped (-1 if none)
int sidx;
/// Queue of not yet requested results
DynamicQueue<RetType,Heap> rs;
public:
/// Get next job witth index \a i, if possible
Job<RetType>* next(int& i);
/// Report result \a r by a worker
void report(RetType r);
/// Report that a job with index \a i has been stopped
void stop(RetType r, int i);
/// Test whether all jobs are done
bool done(void) const;
/// Initialize with job iterator \a j and maximal number of threads \a m
Master(Jobs& j, unsigned int m);
/// Run next job and return true if succesful and assign \a r to its result
bool run(RetType& r);
/// Whether a job has thrown a \a JobStop exception
bool stopped(void) const;
/// Return index of first job that has thrown a \a JobStop exception (-1 if none) with its result
int stoppedjob(RetType& r) const;
/// Whether a new thread is needed for deletion
bool needthread(void);
/// Destructor
~Master(void);
};
/// A class to delete the master (running in parallel)
class Deleter : public Runnable {
protected:
/// The master to be deleted
Master* master;
public:
/// Initialize with master \a m
Deleter(Master* m);
/// Perform deletion
virtual void run(void);
};
/// The actual master
Master* master;
public:
/// Initialize with job iterator \a j and maximal number of threads \a m
RunJobs(Jobs& j, unsigned int m);
/// Run next job and return true if succesful and assign \a r to its result
bool run(RetType& r);
/// Whether a job has thrown a \a JobStop exception with index \a i and result \a r
bool stopped(int& i, RetType& r) const;
/// Destructor
~RunJobs(void);
};
template<class RetType>
forceinline
JobStop<RetType>::JobStop(RetType r0) : r(r0) {}
template<class RetType>
forceinline RetType
JobStop<RetType>::result(void) const {
return r;
}
template<class Jobs, class RetType>
forceinline
RunJobs<Jobs,RetType>::Worker::Worker(Job<RetType>* j,
Master* m,
int i)
: Runnable(true), job(j), master(m), idx(i) {}
template<class Jobs, class RetType>
RunJobs<Jobs,RetType>::Worker::~Worker(void) {}
template<class Jobs, class RetType>
inline int
RunJobs<Jobs,RetType>::Master::stoppedjob(RetType& r) const {
r = sres;
return sidx;
}
template<class Jobs, class RetType>
inline bool
RunJobs<Jobs,RetType>::Master::stopped(void) const {
return sidx >= 0;
}
template<class Jobs, class RetType>
forceinline Job<RetType>*
RunJobs<Jobs,RetType>::Master::next(int& i) {
m.acquire();
Job<RetType>* j;
if (jobs() && !stopped()) {
j = jobs.job(); i=idx++;
} else {
j = nullptr;
n_threads--;
e.signal();
}
m.release();
return j;
}
template<class Jobs, class RetType>
forceinline void
RunJobs<Jobs,RetType>::Master::report(RetType r) {
m.acquire();
rs.push(r);
e.signal();
m.release();
}
template<class Jobs, class RetType>
forceinline void
RunJobs<Jobs,RetType>::Master::stop(RetType r, int i) {
m.acquire();
if (!stopped()) {
sres=r; sidx = i;
}
rs.push(r);
e.signal();
m.release();
}
template<class Jobs, class RetType>
void
RunJobs<Jobs,RetType>::Worker::run(void) {
do {
try {
RetType r = job->run(idx);
master->report(r);
} catch (JobStop<RetType>& js) {
master->stop(js.result(),idx);
}
delete job;
job = master->next(idx);
} while (job != nullptr);
}
template<class Jobs, class RetType>
forceinline bool
RunJobs<Jobs,RetType>::Master::done(void) const {
return (n_threads == 0) && (!jobs() || stopped()) && rs.empty();
}
template<class Jobs, class RetType>
inline
RunJobs<Jobs,RetType>::Master::Master(Jobs& j, unsigned int m_threads)
: n_threads(0), jobs(j), idx(0), sidx(-1), rs(heap) {
m.acquire();
while ((n_threads < m_threads) && jobs()) {
if (stopped())
break;
Thread::run(new Worker(jobs.job(),this,idx));
n_threads++; idx++;
}
m.release();
}
template<class Jobs, class RetType>
inline bool
RunJobs<Jobs,RetType>::Master::run(RetType& r) {
m.acquire();
if (done()) {
m.release();
return false;
}
if (!rs.empty()) {
r = rs.pop();
m.release();
return true;
}
m.release();
while (true) {
e.wait();
m.acquire();
if (done()) {
m.release();
return false;
}
if (!rs.empty()) {
r = rs.pop();
m.release();
return true;
}
m.release();
}
GECODE_NEVER;
}
template<class Jobs, class RetType>
inline bool
RunJobs<Jobs,RetType>::Master::needthread(void) {
bool n;
m.acquire();
while (!rs.empty())
rs.pop().~RetType();
sidx = 0;
n = !done();
m.release();
return n;
}
template<class Jobs, class RetType>
inline
RunJobs<Jobs,RetType>::Master::~Master(void) {
sidx = 0;
RetType r;
while (run(r))
r.~RetType();
}
template<class Jobs, class RetType>
forceinline
RunJobs<Jobs,RetType>::Deleter::Deleter(Master* m)
: Runnable(true), master(m) {}
template<class Jobs, class RetType>
void
RunJobs<Jobs,RetType>::Deleter::run(void) {
delete master;
}
template<class Jobs, class RetType>
inline bool
RunJobs<Jobs,RetType>::stopped(int& i, RetType& r) const {
i = master->stoppedjob(r);
return i >= 0;
}
template<class Jobs, class RetType>
inline
RunJobs<Jobs,RetType>::RunJobs(Jobs& j, unsigned int m)
: master(new Master(j,m)) {}
template<class Jobs, class RetType>
inline bool
RunJobs<Jobs,RetType>::run(RetType& r) {
return master->run(r);
}
template<class Jobs, class RetType>
inline
RunJobs<Jobs,RetType>::~RunJobs(void) {
if (!master->needthread())
delete master;
else
Thread::run(new Deleter(master));
}
}}
// STATISTICS: support-any