text: #ifndef __THREAD_POOL_H
  #define __THREAD_POOL_H
  
  #include "RakMemoryOverride.h"
  #include "DS_Queue.h"
  #include "SimpleMutex.h"
  #include "Export.h"
  #include "RakThread.h"
  
  #ifdef _MSC_VER
  #pragma warning( push )
  #endif
  
  class ThreadDataInterface
  {
  public:
  	virtual void* PerThreadFactory(void *context)=0;
  	virtual void PerThreadDestructor(void* factoryResult, void *context)=0;
  };
  
  /// A simple class to create worker threads that processes a queue of functions with data.
  /// This class does not allocate or deallocate memory.  It is up to the user to handle memory management.
  /// InputType and OutputType are stored directly in a queue.  For large structures, if you plan to delete from the middle of the queue,
  /// you might wish to store pointers rather than the structures themselves so the array can shift efficiently.
  template <class InputType, class OutputType>
  struct RAK_DLL_EXPORT ThreadPool : public RakNet::RakMemoryOverride
  {
  	ThreadPool();
  	~ThreadPool();
  
  	/// Start the specified number of threads.
  	/// \param[in] numThreads The number of threads to start
  	/// \param[in] stackSize 0 for default (except on consoles).
  	/// \param[in] _perThreadDataFactory User callback to return data stored per thread.  Pass 0 if not needed.
  	/// \param[in] _perThreadDataDestructor User callback to destroy data stored per thread, created by _perThreadDataFactory.  Pass 0 if not needed.
  	/// \return True on success, false on failure.
  	bool StartThreads(int numThreads, int stackSize, void* (*_perThreadDataFactory)()=0, void (*_perThreadDataDestructor)(void*)=0);
  
  	// Alternate form of _perThreadDataFactory, _perThreadDataDestructor
  	void SetThreadDataInterface(ThreadDataInterface *tdi, void *context);
  
  	/// Stops all threads
  	void StopThreads(void);
  
  	/// Adds a function to a queue with data to pass to that function.  This function will be called from the thread
  	/// Memory management is your responsibility!  This class does not allocate or deallocate memory.
  	/// The best way to deallocate \a inputData is in userCallback.  If you call EndThreads such that callbacks were not called, you
  	/// can iterate through the inputQueue and deallocate all pending input data there
  	/// The best way to deallocate output is as it is returned to you from GetOutput.  Similarly, if you end the threads such that
  	/// not all output was returned, you can iterate through outputQueue and deallocate it there.
  	/// \param[in] workerThreadCallback The function to call from the thread
  	/// \param[in] inputData The parameter to pass to \a userCallback
  	void AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData);
  
  	/// Adds to the output queue
  	/// Use it if you want to inject output into the same queue that the system uses. Normally you would not use this. Consider it a convenience function.
  	/// \param[in] outputData The output to inject
  	void AddOutput(OutputType outputData);
  
  	/// Returns true if output from GetOutput is waiting.
  	/// \return true if output is waiting, false otherwise
  	bool HasOutput(void);
  
  	/// Inaccurate but fast version of HasOutput.  If this returns true, you should still check HasOutput for the real value.
  	/// \return true if output is probably waiting, false otherwise
  	bool HasOutputFast(void);
  
  	/// Returns true if input from GetInput is waiting.
  	/// \return true if input is waiting, false otherwise
  	bool HasInput(void);
  
  	/// Inaccurate but fast version of HasInput.  If this returns true, you should still check HasInput for the real value.
  	/// \return true if input is probably waiting, false otherwise
  	bool HasInputFast(void);
  
  	/// Gets the output of a call to \a userCallback
  	/// HasOutput must return true before you call this function.  Otherwise it will assert.
  	/// \return The output of \a userCallback.  If you have different output signatures, it is up to you to encode the data to indicate this
  	OutputType GetOutput(void);
  
  	/// Clears internal buffers
  	void Clear(void);
  
  	/// Lock the input buffer before calling the functions InputSize, InputAtIndex, and RemoveInputAtIndex
  	/// It is only necessary to lock the input or output while the threads are running
  	void LockInput(void);
  
  	/// Unlock the input buffer after you are done with the functions InputSize, GetInputAtIndex, and RemoveInputAtIndex
  	void UnlockInput(void);
  
  	/// Length of the input queue
  	unsigned InputSize(void);
  
  	/// Get the input at a specified index
  	InputType GetInputAtIndex(unsigned index);
  
  	/// Remove input from a specific index.  This does NOT do memory deallocation - it only removes the item from the queue
  	void RemoveInputAtIndex(unsigned index);
  
  	/// Lock the output buffer before calling the functions OutputSize, OutputAtIndex, and RemoveOutputAtIndex
  	/// It is only necessary to lock the input or output while the threads are running
  	void LockOutput(void);
  	
  	/// Unlock the output buffer after you are done with the functions OutputSize, GetOutputAtIndex, and RemoveOutputAtIndex
  	void UnlockOutput(void);
  
  	/// Length of the output queue
  	unsigned OutputSize(void);
  
  	/// Get the output at a specified index
  	OutputType GetOutputAtIndex(unsigned index);
  
  	/// Remove output from a specific index.  This does NOT do memory deallocation - it only removes the item from the queue
  	void RemoveOutputAtIndex(unsigned index);
  
  	/// Removes all items from the input queue
  	void ClearInput(void);
  
  	/// Removes all items from the output queue
  	void ClearOutput(void);
  
  	/// Are any of the threads working, or is input or output available?
  	bool IsWorking(void);
  
  	/// The number of currently active threads.
  	int NumThreadsWorking(void);
  
  	/// Have the threads been signaled to be stopped?
  	bool WasStopped(void) const;
  
  protected:
  	// It is valid to cancel input before it is processed.  To do so, lock the inputQueue with inputQueueMutex,
  	// Scan the list, and remove the item you don't want.
  	SimpleMutex inputQueueMutex, outputQueueMutex, workingThreadCountMutex, runThreadsMutex;
  
  	void* (*perThreadDataFactory)();
  	void (*perThreadDataDestructor)(void*);
  
  	// inputFunctionQueue & inputQueue are paired arrays so if you delete from one at a particular index you must delete from the other
  	// at the same index
  	DataStructures::Queue<OutputType (*)(InputType, bool *, void*)> inputFunctionQueue;
  	DataStructures::Queue<InputType> inputQueue;
  	DataStructures::Queue<OutputType> outputQueue;
  
  	ThreadDataInterface *threadDataInterface;
  	void *tdiContext;
  
  	
  	template <class ThreadInputType, class ThreadOutputType>
  	friend RAK_THREAD_DECLARATION(WorkerThread);
  
  	/*
  #ifdef _WIN32
  	friend unsigned __stdcall WorkerThread( LPVOID arguments );
  #else
  	friend void* WorkerThread( void* arguments );
  #endif
  	*/
  
  	/// \internal
  	bool runThreads;
  	/// \internal
  	int numThreadsRunning;
  	/// \internal
  	int numThreadsWorking;
  	/// \internal
  	SimpleMutex numThreadsRunningMutex;
  #ifdef _WIN32
  	/// \internal
  	HANDLE quitAndIncomingDataEvents[2];
  #endif
  };
  
  #include "ThreadPool.h"
  #include "RakSleep.h"
  #ifdef _WIN32
  #else
  #include <unistd.h>
  #endif
  
  #ifdef _MSC_VER
  #pragma warning(disable:4127)
  #pragma warning( disable : 4701 )  // potentially uninitialized local variable 'inputData' used
  #endif
  
  template <class ThreadInputType, class ThreadOutputType>
  RAK_THREAD_DECLARATION(WorkerThread)
  /*
  #ifdef _WIN32
  unsigned __stdcall WorkerThread( LPVOID arguments )
  #else
  void* WorkerThread( void* arguments )
  #endif
  */
  {
  	bool returnOutput;
  	ThreadPool<ThreadInputType, ThreadOutputType> *threadPool = (ThreadPool<ThreadInputType, ThreadOutputType>*) arguments;
  	ThreadOutputType (*userCallback)(ThreadInputType, bool *, void*);
  	ThreadInputType inputData;
  	ThreadOutputType callbackOutput;
  
  	userCallback=0;
  
  	void *perThreadData;
  	if (threadPool->perThreadDataFactory)
  		perThreadData=threadPool->perThreadDataFactory();
  	else if (threadPool->threadDataInterface)
  		perThreadData=threadPool->threadDataInterface->PerThreadFactory(threadPool->tdiContext);
  	else
  		perThreadData=0;
  
  	// Increase numThreadsRunning
  	threadPool->numThreadsRunningMutex.Lock();
  	++threadPool->numThreadsRunning;
  	threadPool->numThreadsRunningMutex.Unlock();
  
  	while (1)
  	{
  #ifdef _WIN32
  		if (userCallback==0)
  		{
  			// Wait for signaled event
  			WaitForMultipleObjects(
  				2,
  				threadPool->quitAndIncomingDataEvents,
  				false,
  				INFINITE);
  		}		
  #endif
  
  		threadPool->runThreadsMutex.Lock();
  		if (threadPool->runThreads==false)
  		{
  			threadPool->runThreadsMutex.Unlock();
  			break;
  		}
  		threadPool->runThreadsMutex.Unlock();
  
  		threadPool->workingThreadCountMutex.Lock();
  		++threadPool->numThreadsWorking;
  		threadPool->workingThreadCountMutex.Unlock();
  
  		// Read input data
  		userCallback=0;
  		threadPool->inputQueueMutex.Lock();
  		if (threadPool->inputFunctionQueue.Size())
  		{
  			userCallback=threadPool->inputFunctionQueue.Pop();
  			inputData=threadPool->inputQueue.Pop();
  		}
  		threadPool->inputQueueMutex.Unlock();
  
  		if (userCallback)
  		{
  			callbackOutput=userCallback(inputData, &returnOutput,perThreadData);
  			if (returnOutput)
  			{
  				threadPool->outputQueueMutex.Lock();
  				threadPool->outputQueue.Push(callbackOutput);
  				threadPool->outputQueueMutex.Unlock();
  			}			
  		}
  
  		threadPool->workingThreadCountMutex.Lock();
  		--threadPool->numThreadsWorking;
  		threadPool->workingThreadCountMutex.Unlock();
  
  #ifndef _WIN32
  		// If no input data available, and GCC, then sleep.
  		if (userCallback==0)
  			RakSleep(1000);
  #endif
  	}
  
  	// Decrease numThreadsRunning
  	threadPool->numThreadsRunningMutex.Lock();
  	--threadPool->numThreadsRunning;
  	threadPool->numThreadsRunningMutex.Unlock();
  
  	if (threadPool->perThreadDataDestructor)
  		threadPool->perThreadDataDestructor(perThreadData);
  	else if (threadPool->threadDataInterface)
  		threadPool->threadDataInterface->PerThreadDestructor(perThreadData, threadPool->tdiContext);
  
  	return 0;
  }
  template <class InputType, class OutputType>
  ThreadPool<InputType, OutputType>::ThreadPool()
  {
  	runThreads=false;
  	numThreadsRunning=0;
  	threadDataInterface=0;
  	tdiContext=0;
  
  }
  template <class InputType, class OutputType>
  ThreadPool<InputType, OutputType>::~ThreadPool()
  {
  	StopThreads();
  	Clear();
  }
  template <class InputType, class OutputType>
  bool ThreadPool<InputType, OutputType>::StartThreads(int numThreads, int stackSize, void* (*_perThreadDataFactory)(), void (*_perThreadDataDestructor)(void *))
  {
  	(void) stackSize;
  
  	runThreadsMutex.Lock();
  	if (runThreads==true)
  	{
  		runThreadsMutex.Unlock();
  		return false;
  	}
  	runThreadsMutex.Unlock();
  
  #ifdef _WIN32
  	quitAndIncomingDataEvents[0]=CreateEvent(0, true, false, 0);
  	quitAndIncomingDataEvents[1]=CreateEvent(0, false, false, 0);
  #endif
  
  	perThreadDataFactory=_perThreadDataFactory;
  	perThreadDataDestructor=_perThreadDataDestructor;
  
  	runThreadsMutex.Lock();
  	runThreads=true;
  	runThreadsMutex.Unlock();
  
  	numThreadsWorking=0;
  	unsigned threadId = 0;
  	(void) threadId;
  	int i;
  	for (i=0; i < numThreads; i++)
  	{
  		int errorCode = RakNet::RakThread::Create(WorkerThread<InputType, OutputType>, this);
  		if (errorCode!=0)
  		{
  			StopThreads();
  			return false;
  		}
  	}
  	// Wait for number of threads running to increase to numThreads
  	bool done=false;
  	while (done==false)
  	{
  		RakSleep(50);
  		numThreadsRunningMutex.Lock();
  		if (numThreadsRunning==numThreads)
  			done=true;
  		numThreadsRunningMutex.Unlock();
  	}
  
  	return true;
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::SetThreadDataInterface(ThreadDataInterface *tdi, void *context)
  {
  	threadDataInterface=tdi;
  	tdiContext=context;
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::StopThreads(void)
  {
  	runThreadsMutex.Lock();
  	if (runThreads==false)
  	{
  		runThreadsMutex.Unlock();
  		return;
  	}
  
  	runThreads=false;
  	runThreadsMutex.Unlock();
  
  #ifdef _WIN32
  	// Quit event
  	SetEvent(quitAndIncomingDataEvents[0]);
  #endif
  
  	// Wait for number of threads running to decrease to 0
  	bool done=false;
  	while (done==false)
  	{
  		RakSleep(50);
  		numThreadsRunningMutex.Lock();
  		if (numThreadsRunning==0)
  			done=true;
  		numThreadsRunningMutex.Unlock();
  	}
  
  #ifdef _WIN32
  	CloseHandle(quitAndIncomingDataEvents[0]);
  	CloseHandle(quitAndIncomingDataEvents[1]);
  #endif
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData)
  {
  	inputQueueMutex.Lock();
  	inputQueue.Push(inputData);
  	inputFunctionQueue.Push(workerThreadCallback);
  	inputQueueMutex.Unlock();
  
  #ifdef _WIN32
  	// Input data event
  	SetEvent(quitAndIncomingDataEvents[1]);
  #endif
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::AddOutput(OutputType outputData)
  {
  	outputQueueMutex.Lock();
  	outputQueue.Push(outputData);
  	outputQueueMutex.Unlock();
  }
  template <class InputType, class OutputType>
  bool ThreadPool<InputType, OutputType>::HasOutputFast(void)
  {
  	return outputQueue.IsEmpty()==false;
  }
  template <class InputType, class OutputType>
  bool ThreadPool<InputType, OutputType>::HasOutput(void)
  {
  	bool res;
  	outputQueueMutex.Lock();
  	res=outputQueue.IsEmpty()==false;
  	outputQueueMutex.Unlock();
  	return res;
  }
  template <class InputType, class OutputType>
  bool ThreadPool<InputType, OutputType>::HasInputFast(void)
  {
  	return inputQueue.IsEmpty()==false;
  }
  template <class InputType, class OutputType>
  bool ThreadPool<InputType, OutputType>::HasInput(void)
  {
  	bool res;
  	inputQueueMutex.Lock();
  	res=inputQueue.IsEmpty()==false;
  	inputQueueMutex.Unlock();
  	return res;
  }
  template <class InputType, class OutputType>
  OutputType ThreadPool<InputType, OutputType>::GetOutput(void)
  {
  	// Real output check
  	OutputType output;
  	outputQueueMutex.Lock();
  	output=outputQueue.Pop();
  	outputQueueMutex.Unlock();
  	return output;
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::Clear(void)
  {
  	runThreadsMutex.Lock();
  	if (runThreads)
  	{
  		runThreadsMutex.Unlock();
  		inputQueueMutex.Lock();
  		inputFunctionQueue.Clear();
  		inputQueue.Clear();
  		inputQueueMutex.Unlock();
  
  		outputQueueMutex.Lock();
  		outputQueue.Clear();
  		outputQueueMutex.Unlock();
  	}
  	else
  	{
  		inputFunctionQueue.Clear();
  		inputQueue.Clear();
  		outputQueue.Clear();
  	}
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::LockInput(void)
  {
  	inputQueueMutex.Lock();
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::UnlockInput(void)
  {
  	inputQueueMutex.Unlock();
  }
  template <class InputType, class OutputType>
  unsigned ThreadPool<InputType, OutputType>::InputSize(void)
  {
  	return inputQueue.Size();
  }
  template <class InputType, class OutputType>
  InputType ThreadPool<InputType, OutputType>::GetInputAtIndex(unsigned index)
  {
  	return inputQueue[index];
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::RemoveInputAtIndex(unsigned index)
  {
  	inputQueue.RemoveAtIndex(index);
  	inputFunctionQueue.RemoveAtIndex(index);
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::LockOutput(void)
  {
  	outputQueueMutex.Lock();
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::UnlockOutput(void)
  {
  	outputQueueMutex.Unlock();
  }
  template <class InputType, class OutputType>
  unsigned ThreadPool<InputType, OutputType>::OutputSize(void)
  {
  	return outputQueue.Size();
  }
  template <class InputType, class OutputType>
  OutputType ThreadPool<InputType, OutputType>::GetOutputAtIndex(unsigned index)
  {
  	return outputQueue[index];
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::RemoveOutputAtIndex(unsigned index)
  {
  	outputQueue.RemoveAtIndex(index);
  }
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::ClearInput(void)
  {
  	inputQueue.Clear();
  	inputFunctionQueue.Clear();
  }
  
  template <class InputType, class OutputType>
  void ThreadPool<InputType, OutputType>::ClearOutput(void)
  {
  	outputQueue.Clear();
  }
  template <class InputType, class OutputType>
  bool ThreadPool<InputType, OutputType>::IsWorking(void)
  {
  	bool isWorking;
  //	workingThreadCountMutex.Lock();
  //	isWorking=numThreadsWorking!=0;
  //	workingThreadCountMutex.Unlock();
  
  //	if (isWorking)
  //		return true;
  
  	// Bug fix: Originally the order of these two was reversed.
  	// It's possible with the thread timing that working could have been false, then it picks up the data in the other thread, then it checks
  	// here and sees there is no data.  So it thinks the thread is not working when it was.
  	if (HasOutputFast() && HasOutput())
  		return true;
  
  	if (HasInputFast() && HasInput())
  		return true;
  
  	// Need to check is working again, in case the thread was between the first and second checks
  	workingThreadCountMutex.Lock();
  	isWorking=numThreadsWorking!=0;
  	workingThreadCountMutex.Unlock();
  
  	return isWorking;
  }
  
  template <class InputType, class OutputType>
  int ThreadPool<InputType, OutputType>::NumThreadsWorking(void)
  {
  	return numThreadsWorking;
  }
  
  template <class InputType, class OutputType>
  bool ThreadPool<InputType, OutputType>::WasStopped(void) const
  {
  	bool b;
  	runThreadsMutex.Lock();
  	b = runThreads;
  	runThreadsMutex.Unlock();
  	return b;
  }
  
  #ifdef _MSC_VER
  #pragma warning( pop )
  #endif
  
  #endif
  
  