/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__JOB_SYSTEM__
#define	__JOB_SYSTEM__


		#define	__USE_LOCK_FREE_JOB_QUEUE__ 1

		#include "thread/thread_event.h"
		#include "thread/thread.h"
		#include "thread/mutex.h"
		#include "container/nlist.h"
	#if	__USE_LOCK_FREE_JOB_QUEUE__
		#include "container/mpmc_bounded_queue.h"
	#else
		#include "container/nstack.h"
	#endif
		#include "container/narray.h"
		#include "memory/nauto_ptr.h"
		#include "nstring/nstring.h"
		#include "time/ntime.h"


namespace GS {
	namespace ASync {
		class JobManager;

//
class	JobWorkerThread : public Threading::Thread
{
protected:

		JobManager	&manager;

		Threading::Atomic32	running;
		uint		worker_id;

public:

		/// Get worker id.
		int			GetWorkerId() const { return worker_id; }
		/// Worker loop.
virtual	void		Execute();

		/// Stop worker thread.
		void		Stop();
		/// Is the worker thread running.
		bool		IsRunning() const;

		JobWorkerThread(JobManager &m, uint id) : manager(m), worker_id(id) {}
};

/// Parallel job.
struct	Job
{
		String		name;
		Time		time_start, time_end;

		Threading::Atomic32	done;

		/// Execute job.
virtual	void		Execute(uint worker_id) = 0;

		Job(const char *_name) : name(_name), done(1) {}
virtual	~Job() {}
};

/// Job group.
class	JobGroup
{
friend	class	JobManager;

		AutoPtr <Threading::Mutex> job_list_mutex;
		List <Job *> job_list;

public:

		JobGroup();
};

//
class	JobManager
{
friend	class	JobWorkerThread;

protected:

		Array <JobWorkerThread *> pool;

	#if	__USE_LOCK_FREE_JOB_QUEUE__
		mpmc_bounded_queue <Job *> pending_queue;
	#else
		AutoPtr <Mutex> pending_queue_mutex;
		Stack <Job *> pending_queue;
	#endif

public:

		Threading::Event job_queued_event;

		/// Wait for a job to complete.
		bool		JoinJob(Job *, bool blocking = true);
		/// Wait for a job group to complete.
		bool		JoinGroup(JobGroup *, bool blocking = true);

		/// Execute a pending job on the caller thread.
		bool		ExecutePendingJob(uint worker_id);

		bool		EnqueueJob(Job * = 0, JobGroup * = 0);

		/// Get the number of worker.
		uint		GetWorkerPoolSize() const;

		/// Create the job worker thread pool.
		bool		CreateJobThreadPool(uint count = 0);
		/// Free the job worker thread pool.
		void		FreeJobThreadPool();

		JobManager();
		~JobManager();
};

	} // ASync
} // GS


#endif	// __JOB_SYSTEM__
