#include "thread.h"

void initThread(thread **t,threadPool *pool)
{
 thread *thr=(thread *)malloc(sizeof(thread));

 if(!thr)
 {
  fprintf(stderr,"\nCan not allocate memory for thread. Quitting...\n");
  exit(1);
 }

 *t=thr;

 pthread_attr_init(&(thr->tattr));
 pthread_mutex_init(&(thr->lock), NULL);
 pthread_cond_init(&(thr->cond), NULL);

 pthread_attr_setdetachstate(&(thr->tattr),PTHREAD_CREATE_DETACHED);

 thr->quit=0;
 thr->p=pool;

 //Create the thread
 int ret=pthread_create(&(thr->t),&(thr->tattr),runner,(void *)thr);

 if(ret!=0)
 {
  fprintf(stderr,"\nCan not create thread. Quitting...\n");
  exit(1);
 }
}

void deleteThread(thread **t)
{
 thread *thr=*t;

 if(!t) return;

 stop(thr);

 pthread_attr_destroy(&(thr->tattr));
 pthread_cond_destroy(&(thr->cond));
 pthread_mutex_destroy(&(thr->lock));

 free(thr);
}

void stop(thread *thr)
{
 unsigned short i;
 thr->quit=1;

 pthread_cond_signal(&(thr->cond));

 for(i=0;thr->quit && i<10;i++)
 {
   if(i>=10)
   {
	pthread_kill(thr->t,9);
	break;
   }
   usleep(400);
  }
}

void wakeForWork(thread *thr,function func,argtype a)
{
 thr->f=func;
 thr->arg=a;

 pthread_cond_signal(&(thr->cond));
}

argtype runner(void* arg)
{
 thread *ptr=(thread *)arg;

 while(1)
 {
  pthread_mutex_lock(&(ptr->lock));

  if(ptr->p)
   putThread(ptr->p,ptr);

  pthread_cond_wait(&(ptr->cond),&(ptr->lock));

  if(ptr->quit) break;

  ptr->f((void *)ptr->arg);

  pthread_mutex_unlock(&(ptr->lock));
 }

 ptr->quit=0;

 return NULL;
}


void initPool(threadPool **pool,unsigned short numthreads)
{
 thread **thr;
 threadPool *p=(threadPool *)malloc(sizeof(threadPool));

 if(!p)
 {
  fprintf(stderr,"Can not get memory to create threadpool. Quitting\n");
  exit(1);
 }
 
 if(!pool)
 {
  free(p);
  return;
 }

 *pool=p;

 pthread_mutex_init(&(p->lock), NULL);
 pthread_cond_init(&(p->cond), NULL);

 p->n=numthreads;
 p->freeCount=0;
 p->n=numthreads;

 thr=(thread **)malloc(numthreads*sizeof(thread *));

 if(!thr)
 {
  fprintf(stderr,"Can not get memory to create pool of threads. Quitting\n");
  exit(1);
 }

 p->pool=(void *)thr;

}

void deletePool(threadPool **pool)
{
 threadPool *p=(threadPool *)pool;

 if(!pool) return;

 thread **thr=(thread **)p->pool;
 unsigned short i;

 for(i=0;i<p->n;i++) stop(thr[i]);

 free(p->pool);

 pthread_cond_destroy(&(p->cond));
 pthread_mutex_destroy(&(p->lock));

 free(p);

}

void putThread(threadPool *p,thread *t)
{
 unsigned short i;
 thread **pool;

 if(!p || !t) return;

 pool=(thread **)p->pool;

 pthread_mutex_lock(&(p->lock));

 i=p->freeCount;
 pool[(p->freeCount)++]=t;

 if(i<=0)pthread_cond_signal(&(p->cond));

 pthread_mutex_unlock(&(p->lock));

}

thread *getThread(threadPool *p)
{
 thread *t,**t1;

 if(!p) return NULL;

 t1=(thread **)p->pool;

 pthread_mutex_lock(&(p->lock));

 if((p->freeCount)<=0)pthread_cond_wait(&(p->cond),&(p->lock));

 t=t1[--(p->freeCount)];

 pthread_mutex_unlock(&(p->lock));

 return t;

}
