/*
* This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
* http://www.gnu.org/licenses/lgpl-3.0.html
*/
#ifndef BACKGROUNDTHREAD_H
#define BACKGROUNDTHREAD_H
#include "safedelete.h"
#undef new
#include <deque>
#include <list>
#include <algorithm>
#include <wx/timer.h> // wxMilliSleep
#include "wx/thread.h"
#include "manager.h"
/*
* BackgroundThread is a lightweight single background worker thread implementation for situations in which
* you simply want to do one or several things in another thread, and using a thread pool is overkill.
* Also, several BackgroundThreads can be used in situations where a thread pool is unsuitable by design. For example,
* you can use two BackgroundThreads to asynchronously read a list of files from disk one at at time and download another list
* of files from the internet without hogging either the hard disk or the network layer with more than one concurrent access.
*
* BackgroundThread can be configured to own the job objects (will delete them after running) or not. It can also own
* the semaphore and queue, or use a shared context.
*
* BackgroundThreadPool is a low overhead thread pool implementation around BackgroundThread.
*/
class AbstractJob
{
public:
AbstractJob(){};
virtual ~AbstractJob(){};
virtual void operator()() = 0;
};
class JobQueue : public std::deque<AbstractJob*>
{
wxCriticalSection c;
public:
void Push(AbstractJob *j)
{
wxCriticalSectionLocker l(c);
push_back(j);
};
AbstractJob* Pop()
{
wxCriticalSectionLocker l(c);
AbstractJob* j = front();
pop_front();
return j;
};
};
class BackgroundThread : public wxThread
{
JobQueue *queue;
wxSemaphore *semaphore;
bool die;
const bool ownsQueue;
const bool ownsSemaphore;
const bool ownsJobs;
public:
BackgroundThread(JobQueue *q, wxSemaphore *s, const bool owns_jobs = true)
: queue(q), semaphore(s), die(false), ownsQueue(false), ownsSemaphore(false), ownsJobs(owns_jobs)
{
Create();
Run();
};
BackgroundThread(wxSemaphore *s, const bool owns_jobs = true)
: queue(new JobQueue), semaphore(s), die(false), ownsQueue(true), ownsSemaphore(false), ownsJobs(owns_jobs)
{
Create();
Run();
};
BackgroundThread(JobQueue *q, const bool owns_jobs = true)
: queue(q), semaphore(new wxSemaphore), die(false), ownsQueue(false), ownsSemaphore(true), ownsJobs(owns_jobs)
{
Create();
Run();
};
BackgroundThread(const bool owns_jobs = true)
: queue(new JobQueue), semaphore(new wxSemaphore), die(false), ownsQueue(true), ownsSemaphore(true), ownsJobs(owns_jobs)
{
Create();
Run();
};
~BackgroundThread()
{
if(ownsSemaphore)
::Delete(semaphore);
if(ownsQueue)
::Delete(queue);
};
void Queue(AbstractJob* j)
{
queue->Push(j);
semaphore->Post();
};
void Die()
{
die = true;
semaphore->Post();
wxMilliSleep(0);
};
void MarkDying() // Need this for threadpool. Die() alone does not work in shared context (for obvious reason).
{
die = true;
};
/*
* This function is inherently unsafe!
* Also, if used on a thread belonging to a pool, it will not do what you think it does!
* Do not use this function unless you are sure you really know what you are doing.
*/
void MurderDeathKill()
{
Die();
wxMilliSleep(0);
wxMilliSleep(0);
if(this && IsRunning())
Kill();
};
ExitCode Entry()
{
AbstractJob* job;
for(;;)
{
semaphore->Wait();
if(die)
break;
job = queue->Pop();
if ( job )
(*job)();
if(ownsJobs)
delete job;
}
return 0;
};
};
struct Agony { inline void operator()(BackgroundThread* t){t->MarkDying();}; };
struct Death { inline void operator()(BackgroundThread* t){t->Die();}; };
class BackgroundThreadPool
{
typedef std::list<BackgroundThread*> ThreadList;
JobQueue queue;
wxSemaphore semaphore;
ThreadList threadList;
public:
BackgroundThreadPool(size_t num_threads = 4)
{
for(unsigned int i = 0; i < num_threads; ++i)
AddThread(new BackgroundThread(&queue, &semaphore));
};
~BackgroundThreadPool()
{
for_each(threadList.begin(), threadList.end(), Agony());
for_each(threadList.begin(), threadList.end(), Death());
Delete(queue);
wxMilliSleep(0);
};
void AddThread(BackgroundThread * t)
{
threadList.push_back(t);
};
void Queue(AbstractJob* j)
{
queue.Push(j);
semaphore.Post();
};
};
#endif