#include "ethread.h"
#include "evar.h"

#ifndef _WIN32
#include <unistd.h>
#endif



// Find out number of processors: sysconf(_SC_NPROCESSORS_ONLN); 

#ifdef _WIN32
emutex::emutex(int type)
{
//  _mutex = CreateMutex(NULL,FALSE,NULL);
  InitializeSRWLock((PSRWLOCK)&_mutex);
}

emutex::emutex()
{
//  _mutex = CreateMutex(NULL, FALSE, NULL);
	InitializeSRWLock((PSRWLOCK)&_mutex);
}

emutex::~emutex()
{
//  CloseHandle((HANDLE)_mutex);
}

void emutex::lock() const
{
  // Was commented before don't know why
  AcquireSRWLockExclusive((PSRWLOCK)&_mutex);

//  DWORD dwWaitResult = WaitForSingleObject((HANDLE)_mutex,INFINITE);
//  if (dwWaitResult != WAIT_OBJECT_0)
//    ldie("error locking mutex");
}

void emutex::unlock() const
{
  // Was commented before don't know why
  ReleaseSRWLockExclusive((PSRWLOCK)&_mutex);
//  if (!ReleaseMutex((HANDLE)_mutex))
//    ldie("error releasing mutex");
}

bool emutex::trylock() const
{
/*
  DWORD dwWaitResult = WaitForSingleObject((HANDLE)_mutex, 0.0);
  if (dwWaitResult == WAIT_OBJECT_0)
	return(true);
  else if (dwWaitResult== WAIT_TIMEOUT)
    return(false);
  ldie("error on trylock");
  return(false);
*/
  return(TryAcquireSRWLockExclusive((PSRWLOCK)&_mutex));
}
#else
emutex::emutex(int type)
{

	pthread_mutexattr_t attr;

	pthread_mutexattr_init(&attr);
	if (type != EMUTEX_RECURSIVE)
		ldie("unknown type: " + estr(type));

	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

	pthread_mutex_init(&_mutex, &attr);
	pthread_mutexattr_destroy(&attr);
}

emutex::emutex()
{
	pthread_mutex_init(&_mutex, NULL);
}

emutex::~emutex()
{
	pthread_mutex_destroy(&_mutex);
}

void emutex::lock() const
{
	pthread_mutex_lock((pthread_mutex_t*)&_mutex);
}

void emutex::unlock() const
{
	pthread_mutex_unlock((pthread_mutex_t*)&_mutex);
}

bool emutex::trylock() const
{
	if (pthread_mutex_trylock((pthread_mutex_t*)&_mutex) == 0) return(true);
	return(false);
}
#endif


#ifdef _WIN32
econdsig::econdsig()
{
	InitializeConditionVariable(&_cond);
//	pthread_cond_init(&_cond,NULL);
}

econdsig::~econdsig()
{
}

void econdsig::wait(emutex& mutex)
{
  SleepConditionVariableSRW(&_cond, &mutex._mutex, INFINITE, 0);
}

void econdsig::signal()
{
  WakeConditionVariable(&_cond);
}

void econdsig::broadcast()
{
  WakeAllConditionVariable(&_cond);
}
#else
econdsig::econdsig()
{
	pthread_cond_init(&_cond, NULL);
}

econdsig::~econdsig()
{
	pthread_cond_destroy(&_cond);
}

void econdsig::wait(emutex& mutex)
{
	pthread_cond_wait(&_cond, &mutex._mutex);
}

void econdsig::signal()
{
	pthread_cond_signal(&_cond);
}

void econdsig::broadcast()
{
	pthread_cond_broadcast(&_cond);
}
#endif

/*
class ethread_info
{
 public:
  efunc func;
  evararray args;
};

pthread_t ethread_create(const efunc& func,const evararray& args)
{
  pthread_t pthread;
  ethread_info *tinfo=new ethread_info;
  tinfo->func=func;
  tinfo->args=args;
  pthread_create(&pthread,NULL,ethread_run,tinfo);
  return(pthread);
}

void* ethread_run(void *ptinfo)
{
  ethread_info *tinfo=static_cast<ethread_info*>(ptinfo);
  tinfo->func.call(tinfo->args);
  delete tinfo;
  return(0x00);
}
*/

void ethreads::setThreads(int nthreads)
{
//  cout << "ethreads: creating threads" << endl;
  while (threads.size()<nthreads)
    threads.add(new ethreadFunc);
}

void ethreads::run(const efunc& func,const evararray& args,int nthreads)
{
  int i;
//  cout << "ethreads: calling wait" << endl;
  wait();

  setThreads(nthreads);

//  cout << "ethreads: running threads" << endl;
  for (i=0; i<threads.size(); ++i)
    threads[i]->run(func,args);
}

void ethreads::wait()
{
//  cout << "ethreads: waiting" << endl;
  for (int i=0; i<threads.size(); ++i)
    threads[i]->wait();
//  cout << "ethreads: done waiting" << endl;
}

void ethreads::stop()
{
  int i;
  for (i=0; i<threads.size(); ++i)
    delete threads[i]; // stops and destroys ethread
  threads.clear();
}

ethreads::~ethreads()
{
  stop();
}

#ifdef _WIN32
ethread::ethread() : _stopThread(false), _pausedThread(true), _pthread(0x00)
{
}

ethread::~ethread()
{
	mutex.lock();
	pthread_t tmpthread = _pthread;
	mutex.unlock();
	if (tmpthread) {
		stop(); // thread has to finish before object can be destroyed
		WaitForSingleObject(tmpthread, INFINITE);
//		pthread_join(tmpthread, 0x00);  // should block until thread quits because of to prevent  destruction of the ethread object before the thread quits
	}
}

void ethread::stop()
{
	mutex.lock();
	if (!_pthread) { mutex.unlock(); return; }

	_stopThread = true;
	condCanRun.signal();
	mutex.unlock();
}

bool ethread::trywake()
{
	mutex.lock();
	if (_stopThread || !_pausedThread)
	{
		mutex.unlock(); return(false);
	}
	if (_pthread == 0x00)
		_pthread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ethread::entrypoint, this, 0, NULL);
	//	pthread_create(&_pthread, NULL, ethread::entrypoint, this);
	_pausedThread = false;
	condCanRun.signal();
	mutex.unlock();
	return(true);
}
void ethread::wake()
{
	mutex.lock();
	while (_stopThread || !_pausedThread)
		condReady.wait(mutex);

	if (_pthread == 0x00)
		_pthread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ethread::entrypoint, this, 0, NULL);
//		pthread_create(&_pthread, NULL, ethread::entrypoint, this);
	_pausedThread = false;
	condCanRun.signal();
	mutex.unlock();
}

int ethread::_runThread()
{
	mutex.lock();
	while (1) {
		while (_pausedThread && !_stopThread)
			condCanRun.wait(mutex);
		if (_stopThread) break;

		mutex.unlock();

		_runTask();

		mutex.lock();
		_pausedThread = true;
		condReady.broadcast();
	}
	_pthread = 0x00;
	_stopThread = false;
	_pausedThread = true;
	condReady.signal();
	mutex.unlock();
	return(0);
}

bool ethread::isBusy()
{
	bool _isBusy;

	mutex.lock();
	_isBusy = !_pausedThread;
	mutex.unlock();
	return(_isBusy);
}

void ethread::wait()
{
	mutex.lock();
	while (!_pausedThread)  // only wait if something is runnning
		condReady.wait(mutex);
	mutex.unlock();
}

DWORD WINAPI ethread::entrypoint(LPVOID pthis)
{
  static_cast<ethread*>(pthis)->_runThread();
  return(0x00);
}

#else

ethread::ethread(): _stopThread(false),_pausedThread(true),_pthread(0x00)
{
}

ethread::~ethread()
{
  mutex.lock();
  pthread_t tmpthread=_pthread;
  mutex.unlock();
  if (tmpthread){
    stop(); // thread has to finish before object can be destroyed
    pthread_join(tmpthread,0x00);  // should block until thread quits because of to prevent  destruction of the ethread object before the thread quits
  }
}

void ethread::stop()
{
  mutex.lock();
  if (!_pthread) { mutex.unlock(); return; }

  _stopThread=true;
  condCanRun.signal();
  mutex.unlock();
}

bool ethread::trywake()
{
  mutex.lock();
  if (_stopThread || !_pausedThread)
    { mutex.unlock(); return(false); }
  if (_pthread==0x00)
    pthread_create(&_pthread,NULL,ethread::entrypoint, this);
  _pausedThread=false;
  condCanRun.signal();
  mutex.unlock();
  return(true);
}
void ethread::wake()
{
  mutex.lock();
  while (_stopThread || !_pausedThread)
    condReady.wait(mutex);
  if (_pthread==0x00)
    pthread_create(&_pthread,NULL,ethread::entrypoint, this);
  _pausedThread=false;
  condCanRun.signal();
  mutex.unlock();
}

int ethread::_runThread()
{
  mutex.lock();
  while (1) {
    while (_pausedThread && !_stopThread)
      condCanRun.wait(mutex);
    if (_stopThread) break;
    
    mutex.unlock();

    _runTask();

    mutex.lock();
    _pausedThread=true;
    condReady.broadcast();
  }
  _pthread=0x00;
  _stopThread=false;
  _pausedThread=true;
  condReady.signal();
  mutex.unlock();
  return(0);
}

bool ethread::isBusy()
{
  bool _isBusy;

  mutex.lock();
  _isBusy=!_pausedThread;
  mutex.unlock();
  return(_isBusy);
}

void ethread::wait()
{
  mutex.lock();
  while (!_pausedThread)  // only wait if something is runnning
    condReady.wait(mutex);
  mutex.unlock();
}

void *ethread::entrypoint(void *pthis)
{
 static_cast<ethread*>(pthis)->_runThread();
 return(0x00);
}
#endif


#ifdef _WIN32
bool ethreadFunc::tryrun(const efunc& func,const evararray& args)
{
  mutex.lock();
  if (!trywake()) { mutex.unlock(); return(false); }
  _func=func;
  _args=args;
  mutex.unlock();
  return(true);
}

void ethreadFunc::run(const efunc& func,const evararray& args)
{
  mutex.lock();
//  cout << "ethreadFunc: checking condReady" << endl;
  while (_stopThread || !_pausedThread)
    condReady.wait(mutex);
  if (_pthread==0x00)
	  _pthread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ethread::entrypoint, this, 0, NULL);
  _pausedThread=false;
  _func=func;
  _args=args;
  condCanRun.signal();
  mutex.unlock();
}

void ethreadFunc::_runTask()
{
  _func.call(_args);
//  _func.clear(); // TODO: find bug that makes codes crash here or next line
//  _args.clear();
}
#else
bool ethreadFunc::tryrun(const efunc& func, const evararray& args)
{
	mutex.lock();
	if (!trywake()) { mutex.unlock(); return(false); }
	_func = func;
	_args = args;
	mutex.unlock();
	return(true);
}

void ethreadFunc::run(const efunc& func, const evararray& args)
{
	mutex.lock();
	//  cout << "ethreadFunc: checking condReady" << endl;
	while (_stopThread || !_pausedThread)
		condReady.wait(mutex);
	if (_pthread == 0x00)
		pthread_create(&_pthread, NULL, ethread::entrypoint, this);
	_pausedThread = false;
	_func = func;
	_args = args;
	condCanRun.signal();
	mutex.unlock();
}

void ethreadFunc::_runTask()
{
	_func.call(_args);
	//  _func.clear(); // TODO: find bug that makes codes crash here or next line
	//  _args.clear();
}
#endif

eworker::~eworker() {}

void eworker::setQueue(etaskQueue& _tqueue)
{
  tqueue=&_tqueue;
}




void ethreadWorker::_runTask()
{
  etaskQueue *tmpqueue;
  mutex.lock();
  tmpqueue=tqueue;
  mutex.unlock();
  
//  cout << "running threadWorker" << endl;
  if (tmpqueue)
    while(tmpqueue->get(*this));
//  cout << "pausing threadWorker" << endl;
}

void ethreadWorker::execute(etaskBase& task,const efunc& func,const evararray& args)
{
  task.result(*this,args,func.call(args));
}

void ethreadWorker::dispatch()
{
  trywake();
//  wake();
}



ostream& operator<<(ostream& stream,const etaskBase& task)
{
  if (task.isDone())
    stream << task.getResult();
  else if (!task.hasQueue())
    stream << "(not queued)";
  else if (task.runningCount())
    stream << "(running)";
  else
    stream << "(queued)";

  return(stream);
}

etaskBase::etaskBase(): _tqueue(0x00) {}

evar etaskBase::waitResult()
{
  wait();
  return(getResult());
}

bool etaskBase::hasQueue() const
{
  return(_tqueue);
}

void etaskBase::setQueue(etaskQueue& tqueue)
{
  mutex.lock();
  _tqueue=&tqueue;
  mutex.unlock();
}

etask::etask(const efunc& func,const evararray& args): _func(func),_args(args),_total(1),_complete(0),_running(0) {}

bool etask::isDone() const
{
  return(_total==_complete);
}

int etask::queuedCount() const
{
  return(_total-_complete-_running);
}

int etask::runningCount() const
{
  return(_running);
}

void etask::run(eworker& worker)
{
  if (_total==_complete) return;
  ++_running;
//  cout << "running task" << endl;
  evararray tmpargs;
  for (int i=0; i<_args.size(); ++i) // copying array gives problems outside the mutex
    tmpargs.add(_args[i].copyPtr());
  tmpargs.add(worker.i);
  mutex.unlock();
  worker.execute(*this,_func,tmpargs);
  mutex.lock();
  --_running;
  ++_complete;
}

evar etask::getResult() const
{
  evar tmpvar;
  mutex.lock();
  tmpvar.set(_result);
  mutex.unlock();
  return(tmpvar);
}

void etask::result(eworker& worker,const evararray& args,const evar& res)
{
  mutex.lock();
  _result.set(res);
  mutex.unlock();
}

void etask::error(eworker& worker,const evararray& args)
{
  mutex.lock();
  --_running;
  if (_tqueue)
    _tqueue->wake(); // wake up workers in case they are idle
  mutex.unlock();
}

void etask::wait()
{
  mutex.lock();
  while(_total!=_complete && hasQueue())
    condWait.wait(mutex);
  mutex.unlock();
}



etaskArray::etaskArray(const efunc& func,const evararray& args,int count): etask(func,args)
{
  _total=count;
  _resultarr.init(count);
}


void etaskArray::run(eworker& worker)
{
  if (_total==_complete) return;
  ++_running;
  int node=_complete+_running-1;
//  cout << "running task array: "<< node << " of " << _total << endl;
  evararray tmpargs;
  for (int i=0; i<_args.size(); ++i) // copying array gives problems outside the mutex
    tmpargs.add(_args[i].copyPtr());
  tmpargs.add(node);
  tmpargs.add(_total);
  tmpargs.add(worker.i);
  mutex.unlock();
//  _func.call(evararray());
//  result(*this,tmpargs,_func.call(tmpargs));
  worker.execute(*this,_func,tmpargs);
//  usleep(200);
//  result(worker,tmpargs,evar());
  mutex.lock();
//  cout << "finished task array: "<< node << " of " << _total << " complete: " << _complete << endl;
  --_running;
  ++_complete;
}

void etaskArray::result(eworker& worker,const evararray& args,const evar& res)
{
  mutex.lock();
//  _resultarr[args[args.size()-2].get<int>()].set(res);
//  _result.set(_resultarr);
//  cout << "complete: " << _complete << " of " << _total << endl;
  mutex.unlock();
}

void etaskArray::error(eworker& worker,const evararray& args)
{
  ldie("to be implemented");
  mutex.lock();
  --_running;
  if (_tqueue)
    _tqueue->wake(); // wake up workers in case they are idle
  mutex.unlock();
}



etaskApply::etaskApply(const efunc& func,const evararray& args,evararray& arr): etask(func,args),_resultarr(arr)
{
  _total=_resultarr.size();
}

void etaskApply::run(eworker& worker)
{
  if (_total==_complete) return;
  ++_running;
  int tmpnode=_complete+_running-1;
//  cout << "running task array: "<< tmpnode << " of " << _total << endl;
  mutex.unlock();
  evararray tmpargs(_args);
  tmpargs.add(_resultarr[tmpnode]);
  tmpargs.add(tmpnode);
  tmpargs.add(_total);
  tmpargs.add(worker.i);
  worker.execute(*this,_func,tmpargs);
  mutex.lock();
  --_running;
  ++_complete;
}

void etaskApply::result(eworker& worker,const evararray& args,const evar& res)
{
  mutex.lock();
  _resultarr[args[args.size()-2].get<int>()].set(res);
  _result.set(_resultarr);
  mutex.unlock();
}

void etaskApply::error(eworker& worker,const evararray& args)
{
  ldie("to be implemented");
  mutex.lock();
  --_running;
  if (_tqueue)
    _tqueue->wake(); // wake up workers in case they are idle
  mutex.unlock();
}






void etaskQueue::setThreads(int count)
{
  ldieif(count<0,"negative number of threads specified: "+estr(count));
  mutex.lock();
  while (workers.size()<count){
    ethreadWorker *tworker=new ethreadWorker;
    tworker->setQueue(*this);
    tworker->wake();
    tworker->i=workers.size();
    workers.add(tworker);
  }
  while (workers.size()>count)
    delete workers[workers.size()-1];
  mutex.unlock();
}

void etaskQueue::add(etaskBase* task)
{
  mutex.lock(); // lock to prevent reading/writing of list while adding task
  queue.push_back(task);
  task->setQueue(*this);
  mutex.unlock();
  wake();
}

void etaskQueue::wake()
{
  for (int i=0; i<workers.size(); ++i)
    workers[i]->dispatch(); // wake up workers in case they are idle
}

void etaskQueue::wait()
{
  mutex.lock(); // lock to prevent reading/writing of list while adding task
  while (queue.size()) condWait.wait(mutex);
  mutex.unlock();
}

bool etaskQueue::get(eworker& worker)
{
  mutex.lock(); // lock to protect list reading operation
  list<etaskBase*>::iterator it;
  etaskBase *pTask=0x00;
  for (it=queue.begin(); it!=queue.end(); ++it){
    (*it)->mutex.lock();
    if ((*it)->queuedCount()>0){
      pTask=(*it);
      break;
    }
    (*it)->mutex.unlock();
  }
  mutex.unlock();
  if (!pTask) return(false);

  pTask->run(worker); // run does the etask mutex unlock and locks it again before returning
  bool remove=false;
  if (pTask->isDone()){
    pTask->condWait.broadcast();
    remove=true;
  }
  pTask->mutex.unlock();

  if (remove)
    taskCompleted(*pTask);

//  if (pTask->queuedCount()==0)
//    taskCompleted(*pTask); // _tqueue may change so should be kept in mutex locked code
/*
  mutex.lock();
  pTask->mutex.lock();
  if (pTask->isDone()){
    pTask->condWait.broadcast();
//    taskCompleted(*pTask);
  }
  pTask->mutex.unlock();
  mutex.lock();
*/  

//  checkCompleted();
  return(true);
}

/*
void etaskQueue::checkCompleted()
{
  return;
  mutex.lock(); // lock to prevent reading/writing while removing task
  list<etaskBase*>::iterator it;
  for (it=queue.begin(); it!=queue.end(); ++it){
    etaskBase *pTask=(*it);
    pTask->mutex.lock();
    if (pTask->isDone()) { 
      queue.erase(it);
      pTask->mutex.unlock();
      break;
    }
    pTask->mutex.unlock();
  }
  mutex.unlock();
}
*/

void etaskQueue::taskCompleted(etaskBase& task)
{
    // this function was being called by the task, which was removing it from the queue, possibly deleting it in the process
  mutex.lock(); // lock to prevent reading/writing while removing task
  list<etaskBase*>::iterator it;
  for (it=queue.begin(); it!=queue.end(); ++it){
    if ((*it)==&task) { queue.erase(it); break; }
  }
  condWait.broadcast();
  mutex.unlock();
}









/*

etaskthread::etaskthread(etaskman& _taskman): taskman(_taskman)
{
  pthread_create(&_pthread,NULL,etaskthread::entrypoint, this);
}

etaskthread::~etaskthread()
{
}

evar etaskthread::_runJob(etask& task)
{
  return(task.func.call(task.args));
}

int etaskthread::_runThread()
{
  while(1){
    // get a task from the taskmanager
    etask *ptask=taskman.getTask(*this);
    if (ptask==0x00) return(0);
    ptask->result.set(_runJob(*ptask));
    ptask->setDone();
    taskman.onTaskDone.call(evararray(taskman,*ptask));
  }
  return(0);
}

void *etaskthread::entrypoint(void *pthis)
{
 static_cast<etaskthread*>(pthis)->_runThread();
 return(0x00);
}






etask::etask(const efunc& _func,const evararray& _args): status(0),func(_func),args(_args) {}


etaskman::etaskman(): runningThreads(0),firstPendingTask(0)
{
}

etaskman::~etaskman()
{
}

void etaskman::createThread(int n)
{
  runThreadsMutex.lock();
  int i;
  for (i=0; i<n; ++i){
    threads.addref(new etaskthread(*this));
    ++runningThreads;
  }
  runThreadsMutex.unlock();
}

etask& etaskman::addTask(const efunc& func,const evararray& args)
{
  runThreadsMutex.lock();
  etask &task(tasks.add(etask(func,args)));

  runThreadsCond.broadcast();

  runThreadsMutex.unlock();
  return(task);
}

etask* etaskman::getTask(etaskthread& thread)
{
  int tmpi;
  runThreadsMutex.lock();
//  cout << pthread_self() << " [finished] run: " << runningThreads << " pending: " << firstPendingTask << " tasks: " << tasks.size() << endl;
  while (1) {
    if (tasks.size()-firstPendingTask>0){
//      cout << pthread_self() << " [running] job: " << firstPendingTask << " run: " << runningThreads << " pending: " << firstPendingTask << " tasks: " << tasks.size() << endl;
      tmpi=firstPendingTask;
      ++firstPendingTask;
      lassert(!tasks[tmpi].isPending());
      tasks[tmpi].setRunning();
      runThreadsMutex.unlock();
      return(&tasks[tmpi]);
    }
    if (runningThreads==1){
      runThreadsMutex.unlock();
      onAllDone.call(evararray(*this));
      runThreadsMutex.lock();
      if (tasks.size()-firstPendingTask>0) continue;
    }
    --runningThreads;
    if (runningThreads==0)
      finishedThreadsCond.signal();
//    cout << pthread_self() << " [waiting] run: " << runningThreads << " pending: " << firstPendingTask << " tasks: " << tasks.size() << endl;
    runThreadsCond.wait(runThreadsMutex);
//    cout << pthread_self() << " [waking] run: " << runningThreads << " pending: " << firstPendingTask << " tasks: " << tasks.size() << endl;
    ++runningThreads;
  }
//  cout << pthread_self() << " [exiting] run: " << runningThreads << " pending: " << firstPendingTask << " tasks: " << tasks.size() << endl;
  runThreadsMutex.unlock();
  return(0x00);
}

void etaskman::wait()
{
  runThreadsMutex.lock();
//  cout << "tasks.size: " << tasks.size() << " firstPending: " << firstPendingTask << endl;
  while (runningThreads>0 || firstPendingTask<tasks.size())
    finishedThreadsCond.wait(runThreadsMutex);
  runThreadsMutex.unlock();
}

#ifndef EUTILS_NOTEST

#include "etest.h"

void etaskman_test_sfunc(int i)
{
  cout << "begin thread test task: " << i << endl;
  sleep(2);
  cout << "end thread test task: " << i << endl;
}

void etaskman_test()
{
  etaskman t;
  cout << "creating 2 threads" << endl;
  t.createThread(2);
  cout << "adding 10 test tasks" << endl;
  int i;
  for (i=0; i<10; ++i)
    t.addTask(etaskman_test_sfunc,evararray((const int&)i));

  cout << "waiting for tasks to finish" << endl;
  t.wait();
  cout << "all tasks finished" << endl;

  sleep(2);

  cout << "submitting another round of tasks" << endl;
  for (i=0; i<10; ++i)
    t.addTask(etaskman_test_sfunc,evararray((const int&)i));

  cout << "waiting for tasks to finish" << endl;
  t.wait();
  cout << "all tasks finished" << endl;
}

//etestAdd(etaskman_test);

#endif

#ifndef EUTILS_NOTEST

#include "etest.h"

void ethread_test_sfunc(int i)
{
  cout << "thread running function: " << i << endl;
  sleep(3);
  cout << "thread ending function: " << i << endl;
}

void ethread_test()
{
  ethread t;
  cout << "is thread busy?: " << t.isBusy() << endl;
  cout << "running thread: " << t.run(ethread_test_sfunc,evararray(0)) << endl;
  cout << "is thread busy?: " << t.isBusy() << endl;
  cout << "testing immediate submission of new function: " << t.run(ethread_test_sfunc,evararray(0)) << endl;
  cout << "waiting for thread to be free" << endl;
  t.wait();
  cout << "submitting series of jobs to thread" << endl;
  t.waitrun(ethread_test_sfunc,evararray(0));
  t.waitrun(ethread_test_sfunc,evararray(1));
  t.wait();
}

//etestAdd(ethread_test);

#endif



*/
