/*
 * Licensed under 7DRL license.  See LICENCE.TXT  
 *
 * Produced by:	Jeff Lait
 *
 *      	7DRL Development
 *
 * NAME:        threadpool.h ( Pointless, C++ )
 *
 * COMMENTS:
 *	Attempt at a threading pool.
 */

#ifndef __threadpool_h__
#define __threadpool_h__

#include "thread.h"
#include "queue.h"
#include "rand.h"

#define THREADPOOL_DOTHREAD THREADPOOL::getPool()->runAndCompleteTasks

class THREADPOOL_TASK_BASE
{
public:
    THREADPOOL_TASK_BASE(int task)
	: myTaskId(task)
    {}

    virtual     ~THREADPOOL_TASK_BASE() {}

    virtual	void	doWork() = 0;
    int			taskId() const { return myTaskId; }
protected:
    int		myTaskId;
};

template <typename OP>
class THREADPOOL_TASK : public THREADPOOL_TASK_BASE
{
public:
    THREADPOOL_TASK(int task, const OP &op) 
	: THREADPOOL_TASK_BASE(task)
	, myOp(op)
    {}

    void		doWork() override
    {
	return myOp(myTaskId);
    }

protected:
    const OP &myOp;
};

class THREADPOOL_LIST
{
public:
    template <typename OP>
    void				enqueueTasks(int ntask, const OP &op)
    {
	for (int i = 0; i < ntask; i++)
	{
	    myQueue.append(new THREADPOOL_TASK<OP>(i, op));
	}
    }

    // Run continuously on a pool thread to apply tasks when needed.
    void				threadLoop()
    {
	// Ensure this thread has a good seed!
	rand_truerandomseed();
	for (int i = 0; i < 10000; i++)
	    rand_choice(2);

	while (1)
	{
	    THREADPOOL_TASK_BASE *task = myQueue.waitAndRemove();
	    int taskid = task->taskId();
	    task->doWork();
	    delete task;
	    myComplete.append(taskid);
	}
    }

    void awaitTasks(int ntask)
    {
	for (int i = 0; i < ntask; i++)
	{
	    myComplete.waitAndRemove();
	}
    }
protected:
    QUEUE<THREADPOOL_TASK_BASE *>	myQueue;
    // Task number that completed.
    QUEUE<int>	myComplete;
};

class THREADPOOL
{
public:
    static THREADPOOL *getPool();
    THREADPOOL();

    template <typename OP>
    void	runAndCompleteTasks(int ntask, const OP &op)
    {
	myTaskList.enqueueTasks(ntask, op);

	// Block until the corresponding tasks are freed.
	myTaskList.awaitTasks(ntask);
    }

protected:
    THREADPOOL_LIST	myTaskList;
};

#endif
