/* Generic Thread library W32/UNIX see thread.h for details */

#ifdef __XCODE__
#include "xcode_quiet.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#ifdef WIN32
#include <windows.h>
#include <process.h>
#define USE_BEGINTHREAD TRUE
#else
#include <sys/types.h>
#include <unistd.h>
#endif

#include "thread.h"
#include "nedmalloc.h"
#include "debug.h"

//#undef calloc
//#undef free

#ifndef WIN32
#include <sched.h>
#endif

#ifndef WIN32
#ifndef done_pthread_h
#define done_pthread_h 1
#define _GNU_SOURCE 1 // so pthread_yield will exist...
#include <pthread.h>
#endif
#endif


#ifndef FALSE
#	define FALSE 0
#endif

#ifndef TRUE
#	define TRUE (!FALSE)
#endif

void emsg(char * arg_list, ...);

static char *id_names[1000];
static int id_count[1000];
static int mid;

void lib_abort_real(char *src, int line);
#define lib_abort() lib_abort_real(__FILE__,__LINE__)

int lib_msleep(int x);
static int spinlock;
int th_set_spinlock(int x)
{
	spinlock = x;
	return TRUE;
}


#ifdef NOTHREADS

Thread *th_new_stack_real(int fn(void *x),void *arg,int stack,char *src, int line)
{
	fn(arg);
	return NULL;	
}	
/* Free a thread */
int th_free(Thread **tt)
{
	return TRUE;
}
/* Returns current thread id */
int th_id(void)
{
	return 1;
}
/* Returns current process id */
int th_pid(void)
{
	return getpid();
}
/* Create a new mutex "Mutually Exclusive" for a shared resource */
Mutex *th_mutex_new_remember(char *name, char *in_file, int at_line) 
{
	Mutex *m;

#ifdef USE_DMALLOC
	m = calloc(1,sizeof(Mutex));dmalloc_ignore_leak();
#else
	m = calloc(1,sizeof(Mutex));
#endif
	mid++;
	m->id = mid;
	if (mid<1000) id_names[mid] = name;
	m->file = in_file;
	m->line = at_line;
	m->name = name;
	return m;
}
/* Free a mutex "Mutually Exclusive" resource */
void th_mutex_free(Mutex *m)
{
	free(m);
}
/* Wait for rights to mutex "Mutually Exclusive" resource */
int th_mutex_wait_remember(Mutex *m, unsigned int timeout, char *line, int file, int notimeout)
{
	return TRUE;
}
/* Release rights to mutex "Mutually Exclusive" resource */
int th_mutex_release(Mutex *m)
{
	return TRUE;
}
int th_exit(int x)
{
	return TRUE;
}
 

#else
#ifdef WIN32 /* Windows 32 functions */

#ifdef __BORLANDC__
#define uintptr_t long
#endif

/* Creates a new thread using fn, execution begins immediately */
Thread *th_new_stack_real(int  fn(void *x),void *arg, int stack, char *source, int line)
{
	Thread *t;
	uintptr_t h;
	stack = stack / 8192;
	stack = stack * 8192;  // make it a nice round number... or it will fail on some systems.  

//printf("th_new_stack %s:%d %d\n",source,line,stack);
#ifdef USE_BEGINTHREAD	
//	ndone++;
//	if (ndone>30) return NULL; // TEST BUG...

	t = calloc(1,sizeof(Thread));
	t->h = 0;
	h = _beginthread((void (*)(void *))fn,stack,(void *) arg);	
	t->h = (void *) h;
	if (h == -1) {
		free(t);
		return NULL;
	}
	return t;
#else
	DWORD thid;
	t = calloc(1,sizeof(Thread));
	t->h = CreateThread(
		NULL,	/* Not inherited handle */
		5000,	/* Stack */
		(LPTHREAD_START_ROUTINE) fn,(LPVOID) arg,
		0,	/* Start immediately */
		&thid);	/* Thread id stored  */
	if (t->h==NULL) {
		free(t);
		return NULL;
	}
	return t;
#endif
}

/* Free a thread */
int th_free(Thread **tt)
{
	Thread *t=*tt;
#ifndef USE_BEGINTHREAD
	if (t->h!=0) if (!CloseHandle( (HANDLE) t->h)) {
		emsg("Thread close failed\n");
		t->h = 0;
		free(t);
		*tt = NULL;
		return FALSE;
	}
#endif
	t->h = 0;
	free(t);
	*tt = NULL;
	return TRUE;
}
int th_kill(Thread *tt, int x) /* Only use when about to shut down process as is dangerous */
{
	return TerminateThread((HANDLE) (tt->h),x);	
}
/* Returns current thread id */
int th_id(void)
{
	return GetCurrentThreadId();
}

int th_cpu_msec(Thread *tt)
{
	unsigned int lo,hi,tot,r;
	FILETIME crtime,extime,ktime,utime;

	GetThreadTimes((HANDLE) tt->h,&crtime,&extime,&ktime,&utime);
	lo = ktime.dwLowDateTime >> 16;
	hi = ktime.dwHighDateTime << 16;
	tot = lo + hi;
	lo = utime.dwLowDateTime >> 16;
	hi = utime.dwHighDateTime << 16;
	tot += lo + hi;
	/*
		100 nano second intervals.
		4000000000
		10000000 per second
		CLOCKS_PER_SEC per second
	*/
	
	r = (int) ((double) tot * .0065536 * 1000 );
	r = r & 0x07fffffff;
	return (int) r;
}
/* Returns current process id */
int th_pid(void)
{
	return GetCurrentProcessId();
}
/* Create a new mutex "Mutually Exclusive" for a shared resource */
Mutex *th_mutex_new_remember(char *name, char *in_file, int at_line)
{
	Mutex *m;
	m = calloc(1,sizeof(Mutex));
	mid++;
	m->id = mid;
	if (mid<1000) id_names[mid] = name;
	m->file = in_file;
	m->line = at_line;
	m->name = name;
	SetLastError(0);
	m->h = 	CreateMutex(NULL,FALSE,NULL);
	if (ERROR_ALREADY_EXISTS == GetLastError()){
		emsg("Leaked a byte of memory getting first mutex for \"%s\", no worries, carying on", name);
		/*  possibly free it and return the handle to the real one, if this ever happens :-)  */
		return m;
	}
	return m;
}
/* Free a mutex "Mutually Exclusive" resource */
void th_mutex_free(Mutex *m)
{
	CloseHandle(m->h);
	free(m);
}
#ifdef XANALYZE
typedef struct ANALYZE {Mutex * mlist[200]; int nm; } Analyze;
#define MAXALIST 40000
static Analyze* alist[MAXALIST];

#define MAXMUTEX 100
typedef struct USED {Mutex * ulist[MAXMUTEX];  } Used;
static Used aused[MAXMUTEX];
static Mutex *aname[MAXMUTEX];

#endif

int th_analyze_show(void)
{
#ifdef XANALYZE
	FILE *f=NULL;
	int i,k;
	f = fopen("analyze.txt","w");
	if (f==NULL) return TRUE; 
	for (i=0; i<MAXMUTEX;i++) {
		if (aname[i]!=NULL) {
			fprintf(f,"%s ",aname[i]->name);
			for (k=0; k<MAXMUTEX; k++) {
//				if (aused[i].ulist[k] != NULL) printf("i=%d k=%d ",i,k);
				if (aused[i].ulist[k] != NULL) {
					fprintf(f,"%s ",aused[i].ulist[k]->name);
					// Now see if this one as a parent, contains the other one..
					if (aused[k].ulist[i] !=NULL) fprintf(f," =========== ");
				}
			}
			fprintf(f,"\n");
		}
	}
	fclose(f);
#endif
	return TRUE;
}
int th_analyze(Mutex *m, int add)
{
	int ret=FALSE;
#ifdef XANALYZE
	Analyze *a;
	int thid = (int) th_id();
	if (thid<0) return FALSE;
	if (thid>40000) return FALSE;
	if (m->id>MAXMUTEX) return FALSE;
	if (alist[thid]==NULL) alist[thid] = calloc(1,sizeof(Analyze));
	a = alist[thid];
	if (add) {
		if (a->nm>10) {
			printf("thread.c mutex add issue %d\n",a->nm);
			lib_abort();
		}
		a->mlist[a->nm++] = m;
		if (a->nm>1) {
			aused[ a->mlist[0]->id ].ulist[m->id] = m;
			aname[ a->mlist[0]->id ] = a->mlist[0];
		}
	} else {
		if (a->nm<1) lib_abort();
		a->nm--;
	}
#endif
		
	return ret;
}
/* Wait for rights to mutex "Mutually Exclusive" resource */
int th_mutex_wait_remember(Mutex *m, unsigned int timeout, char *in_file, int at_line, int notimeout)
{
	int r;
	if (m == NULL) return FALSE;

	r = WaitForSingleObject(m->h,timeout);
	//time(&m->aquired);  chrisp removed as not used as far as I can tell, and time calls are sometimes slow and should be avoided me thinks.  
	m->file = in_file;
	m->line = at_line;
	if (r==WAIT_TIMEOUT) return FALSE;
	if (r==WAIT_FAILED) return FALSE;
	if (th_analyze(m,TRUE)) ;  // printf("analyze %s %d (%s)\n",in_file,at_line,m->name);
	return TRUE;
}

/* Release rights to mutex "Mutually Exclusive" resource */
int th_mutex_release(Mutex *m)
{
	th_analyze(m,FALSE);
	if (!ReleaseMutex(m->h)) {
//		emsg("Releaseing mutex failed\n");
		return FALSE;
	}
	m->aquired = 0;
	return TRUE;
}

/* Call this before exiting a thread (called by the thread itself) */
int th_exit(int x)
{
#ifdef USE_NED
	neddisablethreadcache(0);
#endif
#ifdef USE_BEGINTHREAD		
	_endthread();
#else
	ExitThread(x);
#endif
	return TRUE;
}
void th_new_stack_bug(int fn(void *x),void *arg, int vstack)
{
}

#else /* Unix functions, using pthread */

void th_new_stack_bug(int fn(void *x),void *arg, int vstack)
{
}

int th_analyze_show(void)
{
	return TRUE;
}
void th_new_stack_bug_leaks(int fn(void *x),void *arg, int vstack)
{
	// Thread *t=calloc(1,sizeof(Thread));
	pthread_attr_t attr;
	int r;
	pthread_t thread_id;
	
	size_t stack = vstack;
	pthread_attr_init(&attr);
	errno = 0;
/*
    if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)!=0) { 
		if (errno!=0) emsg("Thread setscope failed %s\n",strerror(errno));
	}
#ifndef BAD_DETACH
        if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)!=0) 
		emsg("Thread setdetachstate failed %s\n",strerror(errno));
#endif

		if (stack==0) stack = 800*1024;
		stack = stack / 4096;
		stack = stack * 4096; // Make it a round number. 
		// NOTE, STACK MUST BE DIVISIBLE BY 1024 OR IT WILL FAIL
		*/

#ifdef SETSTACK
		r = pthread_attr_setstacksize(&attr, stack );
        if (r!=0) {
			emsg("setstacksize failed (%s) %d\n",strerror(r),r);
		}
		{
			size_t result;
			pthread_attr_getstacksize(&attr,&result);
			//printf("Stack size used was: %zd\n",result);
			if (result!=stack) emsg("Stack was set to (%d/%d) by mistake\n",result,stack);
		}
#else
#endif	
	r = pthread_create(&thread_id,&attr,(void * (*)(void *)) fn,arg);
	pthread_attr_destroy(&attr); // destroy attribute structure (fixes a memory leak we think) 
	if (r!=0) {
		emsg("Thread creation failed r=%d %s\n",r,strerror(r));
		errno = r;
	//	free(t);
		return ;	
	}
	return ;
}	


/* Creates a new thread using fn, execution begins immediately */
Thread *th_new_stack_real(int fn(void *x),void *arg, int vstack, char *source, int line)
{
	Thread *t=calloc(1,sizeof(Thread));
	pthread_attr_t attr;
	int r;
	size_t stack = vstack;
	pthread_attr_init(&attr);
	errno = 0;
        if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)!=0) {  /* So signals work */ 
		if (errno!=0) emsg("Thread setscope failed %s\n",strerror(errno));
	}
#ifndef BAD_DETACH
        if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)!=0)  /* So resources are freed */
		emsg("Thread setdetachstate failed %s\n",strerror(errno));
#endif

		if (stack==0) stack = 900*1024;
		stack = stack + 4096*4; // correction for new stack guard size.
		stack = stack / 16384;  // 4096;  chrisp increasd to 8192
		stack = stack * 16384; // Make it a round number. 
		// NOTE, STACK MUST BE DIVISIBLE BY 1024 OR IT WILL FAIL

/* This is needed on freebsd *** */
#ifdef SETSTACK

		r = pthread_attr_setguardsize(&attr,4096*4); // 16k of guard memory, should be sufficient I think...
		if (r!=0) {
			emsg("setguardsize failed (%s) %d\n",strerror(r),r);
		}

		r = pthread_attr_setstacksize(&attr, stack );
        if (r!=0) {
			; /* on freebsd stack does NOT grow!!! */
			emsg("setstacksize failed (%s) %d\n",strerror(r),r);
		}
		{
			size_t result;
			pthread_attr_getstacksize(&attr,&result);
			//printf("Stack size used was: %zd\n",result);
			if (result!=stack) emsg("Stack was set to (%d/%d) by mistake\n",result,stack);
		}
#endif	
	r = pthread_create(&t->h,&attr,(void * (*)(void *)) fn,arg);
	pthread_attr_destroy(&attr); // destroy attribute structure (fixes a memory leak we think) 
	if (r!=0) {
		emsg("Thread creation failed r=%d %s\n",r,strerror(r));
		errno = r;
		free(t);
		return NULL;	
	}
	return t;
}	
/* Free a thread */
int th_free(Thread **tt)
{
	Thread *t=*tt;
	if (pthread_detach(t->h)!=0) {
		/* emsg("Detaching thread failed\n");*/ 
	}
	free(t);
	*tt=NULL;
	return TRUE;
}
int th_cpu_msec(Thread *tt)
{
	return 0; // MUST NEVER USE THE CLOCK() FUNCTION IT'S BADLY BROKEN ON LINUX AND KILLS PERFORMANCE...
//	return (int)((long)clock() * (long)1000 / (long)CLOCKS_PER_SEC);
}

/* Returns current thread id */
int th_id(void)
{
	return (int) pthread_self();
}
/* Returns current process id */
int th_pid(void)
{
	return (int)getpid();
}
/* Create a new mutex "Mutually Exclusive" for a shared resource */
Mutex *th_mutex_new_remember(char *name, char *in_file, int at_line)
{
	Mutex *m;

#ifdef USE_DMALLOC
	m = calloc(1,sizeof(Mutex));
#else
	m = calloc(1,sizeof(Mutex));
#endif
	mid++;
	m->id = mid;
	if (mid<1000) id_names[mid] = name;
	m->file = in_file;
	m->line = at_line;
	m->name = name;
/*	m->h.m_kind =  PTHREAD_MUTEX_RECURSIVE_NP;*/
/*	m->h = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;*/

	if (pthread_mutex_init(&m->h,NULL)!=0) {
		emsg("Mutex init failed %s\n",strerror(errno));		
	}
	return m;
}
/* Free a mutex "Mutually Exclusive" resource */
void th_mutex_free(Mutex *m)
{
	pthread_mutex_destroy(&(m->h));
	free(m);
}
#ifdef is_solaris
#define USE_SCHED_YIELD 1
#endif

#ifdef is_solaris
//#define USE_TIMEDLOCK 1  needs solaris 9...
#endif
#ifdef is_solaris9
#define USE_TIMEDLOCK 1 
#endif

#ifdef is_osx
#define USE_SCHED_YIELD 1
#endif
#ifdef DO_IOS
#define USE_SCHED_YIELD 1
#endif
#ifdef USE_SCHED_YIELD
#define do_yield sched_yield
#else
#define do_yield pthread_yield
#endif 
/* Wait for rights to mutex "Mutually Exclusive" resource */
int th_mutex_wait_remember(Mutex *m, unsigned int timeout, char *in_file, int at_line, int notimeout)
{
	int r;
	int wanted=FALSE;
	int nloop=0;
	time_t start=0;
	if (m == NULL) return FALSE;
/*printf("mutex_lock %d %d %d\n",m->nref,m->threadid,th_id());*/
	if (th_equal(m->threadid,th_oid())) {
	//if (m->threadid==th_id()) {
		m->nref++;
		return TRUE;
	}

	if (m->wanted>0) { // another thread is waiting, since we may have just had the mutex, do a yield first so that the waiting thread can grab it...
		static int k;
		k++;
		//if (k%30==20) lib_msleep(10); // Let another thread take it once in a while. 
		do_yield();
		m->wanted = 0;	
	}

	r = pthread_mutex_trylock(&(m->h));
	if (r==0) {
//		time(&m->aquired);
		if (wanted) m->wanted = 0; // we wanted it so reset it so someone else must wante it befor we do the yield first...
		m->threadid = th_oid();
		m->nref = 0;
		m->file = in_file;
		m->line = at_line;
		return TRUE;
	}
	if (r==EBUSY) {
		if (timeout==0) return FALSE; // fail immediately.
		start = time(NULL);
		for (;time(NULL) < start+(timeout/1000);) {
			nloop++;
			r = pthread_mutex_trylock(&(m->h));
			if (r==0) {
//				time(&m->aquired);
gotit:			m->threadid = th_oid();
				m->nref = 0;
				m->file = in_file;
				m->line = at_line;
				return TRUE;
			}
			if (r!=EBUSY) break;
			// lib_msleep(1) worst case use this
#ifdef USE_TIMEDLOCK
			{
				struct timespec ab;
				ab.tv_sec = time(NULL)+1+(timeout/1000);
				ab.tv_nsec = 0;
				r = pthread_mutex_timedlock(&(m->h),&ab);			
				if (r==0) goto gotit;
			}	
			break;
#endif
			if (notimeout) {
				r = pthread_mutex_lock(&(m->h));
				if (r==0) goto gotit;		
			}
			m->wanted++; // tell other threads we want it.
			wanted = TRUE;
			do_yield();
			if (spinlock) {
				if (nloop%80==70) { // After 2 yeilds go to sleep as something is clearly hogging mutex and no need to keep trying...
					lib_msleep(20); // avoid burning  cpu if the other thread is asleep.  -- chrisp guess at what we should do...
					//and log the name of the mutex so we can detect and fix ? 
					th_bad_mutex(m);
				}
			} else {
				if (nloop%4==2) { // After 2 yeilds go to sleep as something is clearly hogging mutex and no need to keep trying...
					lib_msleep(20); // avoid burning  cpu if the other thread is asleep.  -- chrisp guess at what we should do...
					//and log the name of the mutex so we can detect and fix ? 
					th_bad_mutex(m);
				}
			}
		}
	}
//	r = pthread_mutex_lock(&(m->h));
	//printf("Mutex lock failed, waited %d seconds %s:%d\n",(int) (time(NULL)-start),in_file,at_line);
	return FALSE;
}

/* Release rights to mutex "Mutually Exclusive" resource */
int th_mutex_release(Mutex *m)
{
	if (m->nref>0) {
		m->nref--;
		return TRUE;
	}	
	m->threadid = 0;
	if (pthread_mutex_unlock(&(m->h))==0) {
//		m->aquired = 0;
		return TRUE;
	}
//	emsg("Releaseing mutex failed\n");
	//printf("releasing mutex failed\n");
	lib_abort();
	return FALSE;
}
int th_exit(int x)
{
	pthread_exit(NULL); /* doesn't return */
	return TRUE;
}

/*	th_kill() calls the UNIX pthread_kill() function.
 */
int th_kill(Thread *tt, int x)
{
#ifndef NOPTHREAD_KILL
	return pthread_kill(tt->h, x);
#else
	return 0;
#endif
}


#endif

int th_mutex_wait_or_die_real(Mutex *m, unsigned int timeout, char *in_file, int at_line)
{
	int r;
	r = th_mutex_wait_remember(m,timeout,in_file,at_line,FALSE);
	if (!r) {
#ifdef WIN32
		emsg("Mutex wait or die failed: name(%s), called by(%s:%d), held by(%s:%d)",m->name?m->name:"UNNAMED",in_file,at_line,m->file,m->line);
#else
		emsg("Mutex wait or die failed: name(%s), called by(%s:%d), held by(%s:%d), threadid(%d), nref(%d)",m->name?m->name:"UNNAMED",in_file,at_line,m->file,m->line,m->threadid,m->nref);
#endif
		lib_abort();
	}
	return r;
}
/*

	CHRISP COMMENTED THIS OUT AS IT DOESN'T SEEM TO BE USED.... It may be some other project uses it....

typedef struct TQUEUE {
	Mutex *mutex;
	int *queue;
	int sq;
	int nq;
	struct TQUEUE *next;
} TQueue;

static Mutex *qm;
static TQueue **tqueue;
static int tsz;
static int tnq;

void th_mutex_queue_init()
{
	if (qm) {
		emsg("th_mutex_queue_init called twice");
		lib_abort();
	}
	qm = th_mutex_new("mutex_queue");

	tsz = 16;
	tqueue = calloc(tsz,sizeof(TQueue*));
}

void tqueue_free(TQueue **q)
{
	free((*q)->queue);
	free(*q);
}

void th_mutex_queue_clear()
{
	int i;
	for(i = 0; i < tnq; i++) {
		tqueue_free(&tqueue[i]);
	}
	free(tqueue);
	th_mutex_free(qm);
}

void tqueue_expand()
{
	if (tnq == tsz) {
		tsz += (tsz/2);
		tqueue = (TQueue**)realloc(tqueue,sizeof(TQueue*) * tsz);
	}
}

void tqueue_expand_q(TQueue *q)
{
	if (q->nq == q->sq) {
		q->sq += (q->sq/2);
		q->queue = (int*)realloc(q->queue,sizeof(int) * q->sq);
	}
}

void tqueue_join(Mutex *m)
{
	int id = th_id();
	TQueue *q = NULL;
	int i,j;	

	th_mutex_get(qm);
	for(i = 0; i < tnq; i++) {
		if (tqueue[i]->mutex == m) {
			q = tqueue[i];
			break;
		}
	}

	if (!q) {
		tqueue_expand();

		q = calloc(1,sizeof(TQueue));
		q->mutex = m;
		q->sq = 16;
		q->queue = calloc(q->sq,sizeof(int));

		tqueue[tnq++] = q;
	}

	tqueue_expand_q(q);

	for(j = 0; j < q->nq; j++) {
		if(q->queue[j] == id) goto done;
	}

	q->queue[q->nq++] = th_id();
done:
	th_mutex_release(qm);
}

void tqueue_leave(Mutex *m)
{
	int id = th_id();
	TQueue *q = NULL;
	int i,j;

	th_mutex_get(qm);
	for(i = 0; i < tnq; i++) {
		if (tqueue[i]->mutex == m) {
			q = tqueue[i];
			break;
		}
	}
	if (!q) goto done;
	for(j = 0; j < q->nq; j++) {
		if (q->queue[j] == id) {
			memmove(&q->queue[j],&q->queue[j+1],sizeof(int)*(q->nq-j));
			q->nq--;
			break;
		}
	}
done:
	th_mutex_release(qm);
}

int tqueue_empty_or_first(Mutex *m)
{
	int rval = FALSE;
	int id = th_id();
	TQueue *q = NULL;
	int i;

	th_mutex_get(qm);
	for(i = 0; i < tnq; i++) {
		if (tqueue[i]->mutex == m) {
			q = tqueue[i];
			break;
		}
	}
	if (!q || q->nq == 0 || q->queue[0] == id) rval = TRUE;
	th_mutex_release(qm);
	return rval;
}

int th_mutex_get_queue_real(Mutex *m, char *in_file, int at_line)
{
	int joined = FALSE;
	int check,r;

	if (!qm) {
		emsg("th_mutex_queue_init not called");
		lib_abort();
	}

	while(TRUE) {
		if (m->aquired > 0 && (m->aquired - time(NULL)) > 120) {
#ifdef WIN32
			emsg("Mutex get queue failed: name(%s), called by(%s:%d), held by(%s:%d), aquired (%d)",m->name?m->name:"UNNAMED",in_file,at_line,m->file,m->line,m->aquired);
#else
			emsg("Mutex get queue failed: name(%s), called by(%s:%d), held by(%s:%d), threadid(%d), nref(%d), aquired (%d)",m->name?m->name:"UNNAMED",in_file,at_line,m->file,m->line,m->threadid,m->nref,m->aquired);
#endif
			lib_abort();
		}

		check = tqueue_empty_or_first(m);
		if (check) {
			r = th_mutex_wait_remember(m,1000,in_file,at_line);
			if (r) break;
		}

		if (!joined) {
			tqueue_join(m);
			joined = TRUE;
		}

		if (!check) lib_msleep(1000);
	}

	if (joined) tqueue_leave(m);

	return r;
}

*/
#endif
typedef struct TLIST {void *arg; Thread *h; int (*fn)(void *x); int set_exit; int ready;} Tlist;
//static Tlist *tlist; 
typedef struct ORDERS {int *isgo; Tlist *tlist_arg; struct ORDERS *next;} Orders;
static Orders *orders;
static int kill_all_threads = FALSE;
static int th_count = 0;
int th_orders_add(int *isgo, Tlist *tlist_arg);
int th_orders_wait(Tlist *tlist_arg);

static Mutex *rm;
int th_reuse_mutex_wait(void)
{
	if (rm==NULL) rm = th_mutex_new("reuse");
	if (!th_mutex_wait(rm,600000)) { /* 10 minutes */
		emsg("Cannot get mutex, fatal problem, dieing for reuse\n");
		lib_abort();
	}
	return TRUE;
}
int th_reuse_mutex_release(void)
{
	th_mutex_release(rm);
	return TRUE;
}
int th_exit_now(void *thing){
	return FALSE; /* A thread that exits right away */
}
int th_init_threads(void){
	orders = NULL;
	th_count = 0;
	kill_all_threads = FALSE;
	return TRUE;
}
int th_kill_all_threads_exit(void){
	int tick = 25;
	Orders *o;
	th_reuse_mutex_wait();
	kill_all_threads = TRUE;
	while (orders){
		orders->tlist_arg->fn = th_exit_now;
		orders->tlist_arg->arg = NULL;
		orders->tlist_arg->set_exit = TRUE;
		*(orders->isgo) = TRUE;
		o = orders;
		orders = orders->next;
		free(o);
	}
	orders = NULL;
	th_reuse_mutex_release();
	while (th_count > 0 && tick>0){
		lib_msleep(40); /*  wait up to  one second for threads to free themselves. */
		tick--;
	}
	return TRUE;
}
int th_reuse(void *arg);
int th_orders_find_th(int (*fn)(void *x), void *arg, Thread **th);

int th_new_reuse_th(int fn(void *x),void *arg, Thread **th)
{
	Tlist t;
	if (th_orders_find_th(fn,arg,th)) { 
		return TRUE;
	}
	th_reuse_mutex_wait();
	t.fn = fn;
	t.arg = arg;
	t.set_exit = FALSE;
	t.ready = FALSE;
	t.h = th_new(th_reuse,&t);
	if (th == NULL) return FALSE;
	(*th) = t.h; 
	th_reuse_mutex_release();
	while (t.ready == FALSE)
		lib_msleep(20);
	return TRUE;
}
int th_new_reuse(int fn(void *x),void *arg){
	return th_new_reuse_th(fn,arg, NULL);
}
int th_reuse(void *tlist_arg)
{
	Tlist t, *tp = (Tlist *) tlist_arg;

	th_reuse_mutex_wait();
	th_reuse_mutex_release();
	t.set_exit = tp->set_exit;
	t.arg = tp->arg;
	t.fn = tp->fn;
	t.h = tp->h;
	tp->ready = TRUE;
	th_count++;
	for (;!(t.set_exit) && !kill_all_threads;) {
		if (!t.fn(t.arg))
			break; /*  If function returns false we exit.  */
		if (t.set_exit)
			break;
		th_orders_wait(&t);
	}
	th_count--;
	return TRUE;
}
int th_orders_wait(Tlist *tlist_arg)
{
	int isgo=FALSE;
	if (kill_all_threads) return FALSE;
	th_orders_add(&isgo,tlist_arg);
	for (;!isgo && !kill_all_threads;) {
		lib_msleep(100);
	}
	return TRUE;
}
int th_orders_add(int *isgo, Tlist *tlist_arg)
{
	Orders *o;
	o = calloc(1,sizeof(Orders));

	th_reuse_mutex_wait();
	o->tlist_arg = tlist_arg;
	o->isgo = isgo;
	o->next = orders;
	orders = o;
	th_reuse_mutex_release();
	return TRUE;
}
int th_orders_find_th_optional_exit(int (*fn)(void *x), void *arg, Thread **th, int set_exit)
{
	Orders *o;
	if (orders==NULL) return FALSE;
	th_reuse_mutex_wait();
	o = orders;
	((Tlist *)(o->tlist_arg))->fn = fn;
	((Tlist *)(o->tlist_arg))->arg = arg;
	((Tlist *)(o->tlist_arg))->set_exit = set_exit;
	*(o->isgo) = TRUE;
	if (th != NULL) (*th) = ((Tlist *)o->tlist_arg)->h;
	orders = o->next;
	free(o);
	th_reuse_mutex_release();
	return TRUE;
}
int th_orders_find_th(int (*fn)(void *x), void *arg, Thread **th){
	return th_orders_find_th_optional_exit(fn, arg, th, FALSE);
}
/* Almost the same as th_new() except that it will use up an idle reuse thread if there are any haning around   */
Thread *th_new_or_idle(int fn(void *x),void *arg)
{
	Thread *th = NULL;

	if (!th_orders_find_th_optional_exit(fn, arg, &th, TRUE))
		th = th_new(fn, arg);
	return th;
}	

int th_orders_find(int (*fn)(void *x), void *arg)
{
	return th_orders_find_th(fn, arg, NULL);
}

Thread *th_new_real(int  fn(void *x),void *arg, char *source, int line)
{
	int stack = (512)*1024; // 262144;

//	int stack = 131072;
	return th_new_stack_real(fn,arg, stack,source, line);
}
int th_bad_mutex(Mutex *m) 
{
	int id = m->id;
	if (id<0) return TRUE;
	if (id>999) return TRUE;
	id_count[id]++;
	return TRUE;
}
int th_bad_get(int n, char *name)
{
	strcpy(name,"");
	if (id_names[n]) strncpy(name,id_names[n],100);
	return id_count[n];
}

static Mutex *m;
int th_lib_lock(int *lock, int msec)
{
	time_t start = time(NULL);
	if (m==NULL) m = th_mutex_new("objlock");
	if (!th_mutex_wait(m,msec)) return FALSE;
	for (;*lock;) {
		th_mutex_release(m);
		lib_msleep(20);
		if (time(NULL)-start > msec/1000) return FALSE;
		if (!th_mutex_wait(m,msec)) return FALSE;
	}
	*lock = TRUE;
	th_mutex_release(m);
	return TRUE;			
}
int th_lib_unlock(int *lock)
{
	*lock = FALSE;
	return TRUE;
}


#ifdef WIN32
o_thread th_oid(void)
{
	return (o_thread) th_id();
}
int th_equal(o_thread t1, o_thread t2)
{
	int r1=(int) t1;
	int r2=(int) t2;
	return r1==r2;
}
#else
o_thread th_oid(void)
{
	return (o_thread) pthread_self();
}
int th_equal(o_thread t1, o_thread t2)
{
	return pthread_equal((pthread_t) t1,(pthread_t) t2);
}

#endif
char *th_showid(o_thread pt, char *tmp)
{
	int i;
  unsigned char *ptc = (unsigned char*)(void*)(&pt);
  strcpy(tmp,"");
  for ( i=0; i<sizeof(pt); i++) {
    sprintf(tmp+(int) strlen(tmp),"%02x", (unsigned)(ptc[i]));
  }
  return tmp;
}
	