2013-12-09 10:59:57 +00:00
|
|
|
#include "ParallelWork.h"
|
|
|
|
|
|
|
|
#include "Thread.h"
|
|
|
|
#include "System.h"
|
2014-08-18 13:20:04 +00:00
|
|
|
#include "Semaphore.h"
|
|
|
|
#include "Mutex.h"
|
2014-08-18 10:17:16 +00:00
|
|
|
#include "ParallelWorker.h"
|
2013-12-09 10:59:57 +00:00
|
|
|
#include <cassert>
|
|
|
|
|
2014-08-18 10:17:16 +00:00
|
|
|
/**
|
|
|
|
* Compatibility class for code that uses ParallelUnitFunction.
|
|
|
|
*/
|
|
|
|
class ParallelWorkerCompat:public ParallelWorker
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ParallelWorkerCompat(ParallelWork *work, ParallelWork::ParallelUnitFunction func, void* data):
|
|
|
|
ParallelWorker(), work(work), func(func), data(data)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual int processParallelUnit(int unit)
|
|
|
|
{
|
|
|
|
return func(work, unit, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ParallelWork* work;
|
|
|
|
ParallelWork::ParallelUnitFunction func;
|
|
|
|
void* data;
|
|
|
|
};
|
|
|
|
|
2014-08-18 13:20:04 +00:00
|
|
|
/**
|
|
|
|
* Thread sub-class to perform units of work
|
|
|
|
*/
|
|
|
|
class ParallelWork::ParallelThread:public Thread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ParallelThread(ParallelWork *work):
|
|
|
|
Thread(), work(work), sem(0)
|
|
|
|
{
|
|
|
|
interrupted = false;
|
|
|
|
unit = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void feedUnit(int unit)
|
|
|
|
{
|
|
|
|
this->unit = unit;
|
|
|
|
sem.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
void interrupt()
|
|
|
|
{
|
|
|
|
interrupted = true;
|
|
|
|
sem.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void run() override
|
|
|
|
{
|
|
|
|
while (unit >= 0 or not interrupted)
|
|
|
|
{
|
|
|
|
// Wait for a unit (or interrupt)
|
|
|
|
sem.acquire();
|
|
|
|
|
|
|
|
// Process the unit
|
|
|
|
if (unit >= 0)
|
|
|
|
{
|
|
|
|
work->worker->processParallelUnit(unit);
|
|
|
|
work->returnThread(this);
|
|
|
|
unit = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ParallelWork* work;
|
|
|
|
Semaphore sem;
|
|
|
|
int unit;
|
|
|
|
bool interrupted;
|
|
|
|
};
|
|
|
|
|
2014-08-18 10:17:16 +00:00
|
|
|
ParallelWork::ParallelWork(ParallelWorker *worker, int units)
|
|
|
|
{
|
|
|
|
this->units = units;
|
|
|
|
this->running = 0;
|
|
|
|
this->worker = worker;
|
|
|
|
this->worker_compat = false;
|
|
|
|
}
|
|
|
|
|
2013-12-09 10:59:57 +00:00
|
|
|
ParallelWork::ParallelWork(ParallelUnitFunction func, int units, void* data)
|
|
|
|
{
|
|
|
|
this->units = units;
|
|
|
|
this->running = 0;
|
2014-08-18 10:17:16 +00:00
|
|
|
this->worker = new ParallelWorkerCompat(this, func, data);
|
|
|
|
this->worker_compat = true;
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ParallelWork::~ParallelWork()
|
|
|
|
{
|
|
|
|
assert(not running);
|
2014-08-18 10:17:16 +00:00
|
|
|
if (worker_compat)
|
|
|
|
{
|
|
|
|
delete worker;
|
|
|
|
}
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|
|
|
|
|
2014-08-18 10:17:16 +00:00
|
|
|
int ParallelWork::perform(int thread_count)
|
2013-12-09 10:59:57 +00:00
|
|
|
{
|
2014-08-18 13:20:04 +00:00
|
|
|
int i, done;
|
2013-12-09 10:59:57 +00:00
|
|
|
assert(not running);
|
|
|
|
|
2014-08-18 13:20:04 +00:00
|
|
|
// Get thread count
|
2014-08-18 10:17:16 +00:00
|
|
|
if (thread_count <= 0)
|
2013-12-09 10:59:57 +00:00
|
|
|
{
|
2014-08-18 10:17:16 +00:00
|
|
|
thread_count = System::getCoreCount();
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|
2014-08-18 10:17:16 +00:00
|
|
|
if (thread_count > PARALLEL_MAX_THREADS)
|
2013-12-09 10:59:57 +00:00
|
|
|
{
|
2014-08-18 10:17:16 +00:00
|
|
|
thread_count = PARALLEL_MAX_THREADS;
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|
2014-08-18 13:20:04 +00:00
|
|
|
this->thread_count = thread_count;
|
2013-12-09 10:59:57 +00:00
|
|
|
running = 1;
|
|
|
|
|
2014-08-18 13:20:04 +00:00
|
|
|
// Init threads
|
|
|
|
semaphore = new Semaphore(thread_count);
|
|
|
|
mutex = new Mutex();
|
|
|
|
threads = new ParallelThread*[thread_count];
|
|
|
|
available = new ParallelThread*[thread_count];
|
|
|
|
available_offset = 0;
|
|
|
|
available_length = thread_count;
|
2014-08-18 10:17:16 +00:00
|
|
|
for (i = 0; i < thread_count; i++)
|
2013-12-09 10:59:57 +00:00
|
|
|
{
|
2014-08-18 13:20:04 +00:00
|
|
|
threads[i] = available[i] = new ParallelThread(this);
|
|
|
|
threads[i]->start();
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|
|
|
|
|
2014-08-18 13:20:04 +00:00
|
|
|
// Perform all units
|
2013-12-09 10:59:57 +00:00
|
|
|
for (done = 0; done < units; done++)
|
|
|
|
{
|
2014-08-18 13:20:04 +00:00
|
|
|
// Take first available thread
|
|
|
|
semaphore->acquire();
|
|
|
|
mutex->acquire();
|
|
|
|
ParallelThread *thread = available[available_offset];
|
|
|
|
available_offset++;
|
|
|
|
if (available_offset >= thread_count)
|
2013-12-09 10:59:57 +00:00
|
|
|
{
|
2014-08-18 13:20:04 +00:00
|
|
|
available_offset = 0;
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|
2014-08-18 13:20:04 +00:00
|
|
|
available_length--;
|
|
|
|
mutex->release();
|
|
|
|
|
|
|
|
// Feed the unit to it
|
|
|
|
thread->feedUnit(done);
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|
|
|
|
|
2014-08-18 13:20:04 +00:00
|
|
|
// Wait for all threads to end, then cleanup
|
2014-08-18 10:17:16 +00:00
|
|
|
for (i = 0; i < thread_count; i++)
|
2013-12-09 10:59:57 +00:00
|
|
|
{
|
2014-08-18 13:20:04 +00:00
|
|
|
threads[i]->interrupt();
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|
2014-08-18 13:20:04 +00:00
|
|
|
for (i = 0; i < thread_count; i++)
|
|
|
|
{
|
|
|
|
threads[i]->join();
|
|
|
|
delete threads[i];
|
|
|
|
}
|
|
|
|
delete[] threads;
|
|
|
|
delete[] available;
|
|
|
|
delete semaphore;
|
|
|
|
delete mutex;
|
2013-12-09 10:59:57 +00:00
|
|
|
|
|
|
|
running = 0;
|
2014-08-18 13:20:04 +00:00
|
|
|
return done;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ParallelWork::returnThread(ParallelWork::ParallelThread *thread)
|
|
|
|
{
|
|
|
|
mutex->acquire();
|
|
|
|
available[(available_offset + available_length) % thread_count] = thread;
|
|
|
|
available_length++;
|
|
|
|
semaphore->release();
|
|
|
|
mutex->release();
|
2013-12-09 10:59:57 +00:00
|
|
|
}
|