#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>
#include <../source/PAL/oshead.h>

/*
 * LibV core: multitasking environment wrapper for NT and UNIX.
 * Last update: 18.02.2002
 *
 * TODO: separate to:
 *	threadsd.c - thread local storage management;
 *	thread.c - abstract 'thread' object;
 *	imutex.c - internally used fast wrapper for mutex object;
 *	mutex.c - abstract 'mutex' object, based on imutex.c;
 *	cond.c - conditional variables;
 *	shared.c - libv global data management.
 *
 *	Global section can be protected by struct_mutex_t, thus we can
 *	avoid code duplication.
 */

/*
 * 17.01.30 - Sleep(0) -> Sleep(1) in schedYield()
 */

#ifdef	CHECKED_BUILD
#define	THREAD_MAGIC	0x74687264
#define	MUTEX_MAGIC		0x4D555445
#define	COND_MAGIC		0x434F4E44
V_LOCAL	char			*thread_object_name="thread";
V_LOCAL	char			*mutex_object_name="mutex";
V_LOCAL	char			*cond_object_name="event";
#endif	/* CHECKED_BUILD */

/*
 * BEGIN 'shared.c'
 */
 
/*
 *	NOTE: we can't use mutex_t here !
 *	FIXME: avoid code duplication.
 *	FIXME: should be 'libv_biglock'
 */
#ifdef	__WIN32__
V_LOCAL	CRITICAL_SECTION	libv_global_lock;
#elif	defined(__UNIX__)
pthread_mutex_t				libv_global_lock;
pthread_mutexattr_t			libv_global_lock_attr;
#endif	/* __UNIX__ */

/*
 *	See PAL/dataman.c
 *	FIXME: move to 'shared.h' and #include.
 */
extern	global_data_t		libv_global_data;

/*
 *	Debug stuff to track deadlocks inside LibV code.
 *	Enabling dumps SIGNIFICANTLY slows code down.
 */

#ifdef	DUMP_GLOBAL_LOCKS
#define	DUMP_LOCK_UNLOCK(str)	puts(str)
#else	/* !DUMP_GLOBAL_LOCKS */
#define	DUMP_LOCK_UNLOCK(str)
#endif	/* !DUMP_GLOBAL_LOCKS */

/*
 * FIXME: should be libv_acquire_biglock & libv_release_biglock
 */
 
V_LOCAL	global_data_p
lock_protected(void)
{
	DUMP_LOCK_UNLOCK("lock_protected");
#ifdef	__WIN32__
	EnterCriticalSection(&libv_global_lock);
#elif	defined(__UNIX__)
	pthread_mutex_lock(&libv_global_lock);
#endif	/* __UNIX__ */
	return &(libv_global_data);
}

V_LOCAL	global_data_p
lock_unprotected(void)
{
	DUMP_LOCK_UNLOCK("lock_UNprotected");
	return &(libv_global_data);
}

V_LOCAL	void
unlock_protected(void)
{
	DUMP_LOCK_UNLOCK("UNlock_protected");
#ifdef	__WIN32__
	LeaveCriticalSection(&libv_global_lock);
#elif	defined(__UNIX__)
	pthread_mutex_unlock(&libv_global_lock);
#endif	/* __UNIX__ */
	return;
}

V_LOCAL	void
unlock_unprotected(void)
{
	DUMP_LOCK_UNLOCK("UNlock_UNprotected");
	return;
}

/*
 * Returns contents of guarded section with global data.
 * Section than *MUST* be unlocked by libvUnlockGlobal().
 */
global_data_p	(*libvLockGlobal)(void)=lock_unprotected;
/*
 * Unlocks global data, makes it possible to use by another threads.
 */
void			(*libvUnlockGlobal)(void)=unlock_unprotected;
/*
 * Returns global data, assuming thread-safe environment (or prior lock).
 */
global_data_p	libvLockedGlobal(void)
{
	return &(libv_global_data);
}

V_LOCAL void
libv_protect_section(void)
{
#ifdef	__WIN32__
	InitializeCriticalSection(&libv_global_lock);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutexattr_init(&libv_global_lock_attr);
#ifdef	__USE_UNIX98
	pthread_mutexattr_settype(&libv_global_lock_attr,PTHREAD_MUTEX_RECURSIVE);
#else	/* !__USE_UNIX98 */
	pthread_mutexattr_setkind_np(&libv_global_lock_attr,PTHREAD_MUTEX_RECURSIVE_NP);
#endif	/* !__USE_UNIX98 */
	pthread_mutex_init(&libv_global_lock,&libv_global_lock_attr);
#endif	/* __UNIX__ */
	lock_protected();
	libvLockGlobal=lock_protected;
	libvUnlockGlobal=unlock_protected;
	/* NOTE: we leave mutex in locked state */
}

V_LOCAL void
libv_unprotect_section(void)
{
	lock_protected();
	libvLockGlobal=lock_unprotected;
	libvUnlockGlobal=unlock_unprotected;
	unlock_protected();
#ifdef	__WIN32__
	DeleteCriticalSection(&libv_global_lock);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_destroy(&libv_global_lock);
	pthread_mutexattr_destroy(&libv_global_lock_attr);
#endif	/* __UNIX__ */
}

/*
 *	Internal TSD template: used while global->thread_count==0.
 */

extern	thread_data_t		libv_thread_data;
V_LOCAL	void	*main_thread_tsd=&libv_thread_data;
V_LOCAL	u32_t	tsd_key=0;

/*
 *	Constructor and destructor of TSD's for every user-created threads.
 *	NOTE. Both procedures must be called in context of NEW (child) thread.
 */
V_LOCAL	vboolean
libvPerThreadInit(void)
{
	thread_data_t *storage;
#ifdef	CHECKED_BUILD
	char buf[256];
#endif	/* CHECKED_BUILD */
	storage=(thread_data_t *)libvGetSpecific();
	if(storage)
	{
		if(storage->magic==LIBV_TSD_MAGIC)
		{
#ifdef	CHECKED_BUILD
			libvError("libvPerThreadInit(): internal TSD for current "
				"thread already initialized.\n");
#endif	/* CHECKED_BUILD */
			return true;
		}
#ifdef	CHECKED_BUILD
        libvError(
        	"libvPerThreadInit(): garbage in internal TSD; discarded.");
#endif	/* CHECKED_BUILD */
	}
	/*
     *	NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
     *	We can't use memAlloc() because at this moment pointer
     *	hashes not yet initialized.
     */
	storage=(thread_data_t*)malloc(sizeof(thread_data_t));
	if(!storage)
	{
#ifdef	CHECKED_BUILD
		sprintf(buf,"libvPerThreadInit(): failed on allocation %d "
			"bytes for TSD.\n",sizeof(thread_data_t));
        libvError(buf);
#endif	/* CHECKED_BUILD */
		return false;
	}
	memZero(storage,sizeof(thread_data_t));
    /*
     *	Initialize some fields
     */
	storage->magic=LIBV_TSD_MAGIC;
    storage->constant_alpha=0xFF;
    /* FIXME: is this still needed ? */
	storage->meta_x_extent=storage->meta_x_extent=1000;
    libvSetSpecific(storage);
	return true;
}

V_LOCAL	vboolean
libvPerThreadShutdown(void)
{
	void *storage;
	storage=libvGetSpecific();
	if(storage)
	{
		if(((thread_data_t*)storage)->magic!=LIBV_TSD_MAGIC)
		{
#ifdef	CHECKED_BUILD
			libvError("libvPerThreadShutdown(): "
				"garbaged TSD, memory leak.\n");
#endif	/* CHECKED_BUILD */
			return false;
		}
		/*
		 * TODO: extra sanity checks
		 */
		free(storage);
		return true;
	}
	return false;
}

#if		defined(__WIN32__)

#include <../source/PAL/argsused.h>
V_LOCAL	LPVOID WINAPI
TlsGetValueStub(DWORD Key)
{
	return main_thread_tsd;
}

#include <../source/PAL/argsused.h>
V_LOCAL	BOOL WINAPI
TlsSetValueStub(DWORD Key,LPVOID Value)
{
	main_thread_tsd=Value;
	return TRUE;
}

LPVOID	(WINAPI *TlsGetValuePtr)(DWORD)=TlsGetValueStub;
BOOL	(WINAPI *TlsSetValuePtr)(DWORD,LPVOID)=TlsSetValueStub;

void	tsd_switch_to_system(void)
{
	TlsSetValuePtr=TlsSetValue;
	TlsGetValuePtr=TlsGetValue;
}

void	tsd_switch_to_stubs(void)
{
	TlsSetValuePtr=TlsSetValueStub;
	TlsGetValuePtr=TlsGetValueStub;
}

/*
 * END 'shared.c'
 */
 
/*
 *	Thread object
 */
typedef struct	os_thread_s
{
	libv_object_t	base;
	HANDLE			handle;
	DWORD			id;
	void			*arg;
	thread_proc_t	start_address;
}	os_thread_t, *os_thread_p;

/*
 *	Mutex object
 */
typedef struct	os_mutex_s
{
	libv_object_t	base;
#ifdef	MUST_DIE_DEAD
	CRITICAL_SECTION cs;
	u32_t lock_count;
#else	/* 	MUST_DIE_LIVE :) */
	/*
	 * There is no TryEnterCriticalSection() under MustDie; we forced
	 * to use WIN32 mutex object.
	 */
	HANDLE		mutex;	
#endif	/* 	MUST_DIE_LIVE */
}	os_mutex_t, *os_mutex_p;

/*
 *	Conditional variable object: similar to Win32 'Event' kernel object.
 */
typedef struct	os_cond_s
{
	libv_object_t	base;
	HANDLE		cond; /* Event */
}	os_cond_t, *os_cond_p;

/*
 *	Thread entry point
 */
DWORD	CALLBACK
Win32ThreadEntry(LPVOID param)
{
	libvPerThreadInit();
	((os_thread_t *)param)->start_address(((os_thread_t *)param)->arg);
	threadExit(0);
	return 0;
}

#elif	defined(__UNIX__)	/* !__WIN32__ */

/*
 * BEGIN 'shared.c'
 */
 
int
pthread_setspecific_stub(pthread_key_t key,const void *pointer)
{
	main_thread_tsd=(void*)pointer;
	return 0;
}

void*
pthread_getspecific_stub(pthread_key_t key)
{
	return main_thread_tsd;
}

int	(*pthread_setspecific_ptr)(pthread_key_t,const void *)=
	pthread_setspecific_stub;

void*	(*pthread_getspecific_ptr)(pthread_key_t)=
	pthread_getspecific_stub;

V_LOCAL	void
tsd_switch_to_system(void)
{
	pthread_setspecific_ptr=pthread_setspecific;
	pthread_getspecific_ptr=pthread_getspecific;
}

V_LOCAL	void
tsd_switch_to_stubs(void)
{
	pthread_setspecific_ptr=pthread_setspecific_stub;
	pthread_getspecific_ptr=pthread_getspecific_stub;
}

/*
 * END 'shared.c'
 */
 
/*
 *	Thread object
 */
typedef struct	os_thread_s
{
	libv_object_t	base;
	vboolean		active;
	pthread_t		id;
	pthread_attr_t	attr;
	void			*arg;
	thread_proc_t	start_address;
}	os_thread_t, *os_thread_p;

/*
 *	Mutex object
 *	FIXME: 1 - split code; 2 - implement recursive mutexes without
 *	both ..._setkind_np() and ..._settype() calls (see SDL code).
 */
typedef struct	os_mutex_s
{
	libv_object_t	base;
	pthread_mutex_t	handle;
	pthread_mutexattr_t	attr;
}	os_mutex_t, *os_mutex_p;

/*
 * Conditional variable object.
 * A bit tricky; we are going to mimic NT semantics.
 */
typedef	struct	os_cond_s
{
	libv_object_t	base;
	vboolean		autoreset;
	vboolean		state;
	vboolean		pulse;
	pthread_cond_t	cond;
	pthread_mutex_t	mutex;
}	os_cond_t, *os_cond_p;

/*
 *	Thread entry point
 */
void*
PosixThreadEntry(void *param)
{
	libvPerThreadInit();
	GetThreadData()->this_thread=param;
	((os_thread_t *)param)->active=true;
	((os_thread_t *)param)->start_address(((os_thread_t *)param)->arg);
	threadExit(0);
	/* Make compilers happy (although we can write for(;;); :) */
	return NULL;
}

#else	/* !__UNIX__; unknown platform (probably, __DOS32__ :( ) */

#include <../source/PAL/argsused.h>
V_LOCAL	int
thread_setspecific_stub(void *key,const void *pointer)
{
	main_thread_tsd=(void*)pointer;
	return 0;
}

#include <../source/PAL/argsused.h>
V_LOCAL	void*
thread_getspecific_stub(void *key)
{
	return main_thread_tsd;
}

int	(*thread_setspecific_ptr)(void*,const void *)=thread_setspecific_stub;
void*	(*thread_getspecific_ptr)(void*)=thread_getspecific_stub;

#define	tsd_switch_to_system()
#define	tsd_switch_to_stubs()

/*
 *	Thread object
 */
typedef struct	os_thread_s
{
	libv_object_t	base;
	void			*arg;
	thread_proc_t	start_address;
}	os_thread_t, *os_thread_p;

/*
 *	Mutex object
 */
typedef struct	os_mutex_s
{
	libv_object_t	base;
}	os_mutex_t, *os_mutex_p;

/*
 *	Conditional variable object
 */
typedef	struct	os_cond_s
{
	libv_object_t	base;
	vboolean		state;
}	os_cond_t, *os_cond_p;

/*
 *	Thread entry point
 */
V_LOCAL	void*
StubThreadEntry(void *param)
{
	/*
	 * FIXME: please, realize DOS32 scheduler ...
	 */
#ifdef	CHECKED_BUILD
	conlPrintf(LOG_WARNING,"StubThreadEntry(): "
		"multithreading not supported, "
		"executing %X(%X) sequentially ...\n",
		(u32_t)((os_thread_t *)param)->start_address,
		(u32_t)((os_thread_t *)param)->arg);
#endif	/* CHECKED_BUILD */
	((os_thread_t *)param)->start_address(((os_thread_t *)param)->arg);
	threadExit(0);	/* In fact, this is not necessary */
	return NULL;
}

#endif	/* !__UNIX__ */

/*	=======================================================================
**	TSD management.
*/

u32_t	V_GLOBAL
threadCreateKey(void)
{
	u32_t key;
#ifdef	__WIN32__
	key=(u32_t)TlsAlloc();
#elif	defined(__UNIX__) /* !__WIN32__ */
	/* Unfortunately, key #0 is VALID ! */
	if(pthread_key_create((pthread_key_t*)&key,NULL))
		return 0;
	key++;
#else	/* !__UNIX__ */
	return (u32_t)malloc(sizeof(void*));
#endif	/* __WIN32__ */
	return key;
}

void	V_GLOBAL
threadDestroyKey(u32_t key)
{
#ifdef	__WIN32__
	TlsFree((DWORD)key);
#elif	defined(__UNIX__)
	pthread_key_delete((pthread_key_t)(key-1));
#else	/* !__UNIX__ */
	if(key)
		free((void*)key);
#endif	/* __WIN32__ */
}

void	V_GLOBAL
threadSetSpecific(u32_t key,void_p specific)
{
#ifdef	__WIN32__
	TlsSetValue((DWORD)key,(LPVOID)specific);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_setspecific((pthread_key_t)(key-1),specific);
#else	/* !__UNIX__ */
	*((void**)key)=specific;
#endif	/* !__UNIX__ */
}

void_p		V_GLOBAL
threadGetSpecific(u32_t key)
{
#ifdef	__WIN32__
	return TlsGetValue((DWORD)key);
#elif	defined(__UNIX__) /* !__WIN32__ */
	return pthread_getspecific((pthread_key_t)(key-1));
#else	/* !__UNIX__ */
	return *((void**)key);
#endif	/* !__UNIX__ */
}

/*
**	Private key management
*/

V_LOCAL	vboolean
tsd_create_private_key(void)
{
	vboolean ok;
#ifdef	__WIN32__
	tsd_key=(u32_t)TlsAlloc();
	ok=tsd_key!=0xFFFFFFFF;
#elif	defined(__UNIX__) /* !__WIN32__ */
	ok=pthread_key_create((pthread_key_t*)&tsd_key,NULL)==0;
#endif	/* !__UNIX__ */
	if(!ok)
	{
		/* Can't use conlPrintf() ! */
#ifdef	__WIN32__
		if(MessageBox(NULL,"tsd_per_thread_init(): "
			"TlsAlloc() failed, prepare for DrWatson() !",
			"LibV main thread initialization error",
			MB_OKCANCEL|MB_ICONHAND)==IDOK)
			return false;
		else
			ExitProcess(1);
#elif	defined(__UNIX__)
		printf("LibV->tsd_create_private_key(): pthread_key_create() failed,"
			" prepare for core dump !\n");
		return false;
#endif	/* !__WIN32__ */
	}
	return true;
}

V_LOCAL void
tsd_destroy_private_key(void)
{
#ifdef	__WIN32__
	TlsFree(tsd_key);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_key_delete((pthread_key_t)tsd_key);
#endif	/* !__UNIX__ */
}

void	V_CALL
libvSetSpecific(void_p specific)
{
#ifdef	__WIN32__
	TlsSetValuePtr(tsd_key,specific);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_setspecific_ptr(tsd_key,specific);
#else	/* !__UNIX__ */
	thread_setspecific_ptr(tsd_key,specific);
#endif	/* !__UNIX__ */
}

void_p	V_CALL
libvGetSpecific(void)
{
#ifdef	__WIN32__
	return TlsGetValuePtr(tsd_key);
#elif	defined(__UNIX__) /* !__WIN32__ */
	return pthread_getspecific_ptr(tsd_key);
#else	/* !__UNIX__ */
	return thread_getspecific_ptr(tsd_key);
#endif	/* !__UNIX__ */
}

/*
**	Switching between thread-unsafe and thread-safe behavior.
**	NOTE: these procedures must be called from parent thread.
*/

V_LOCAL	void	before_start_thread()
{
	global_data_p data;
	data=libvLockGlobal();
	data->thread_count++;
	if(data->thread_count==1)
	{
		void *main_specific;
		/*	This is first thread, initialize key, store local tsd
		 *	and switch TSD functions */
		libvUnlockGlobal();
		libv_protect_section();
		main_specific=libvGetSpecific();
		tsd_create_private_key();
		tsd_switch_to_system();
		libvSetSpecific(main_specific);
	}
	libvUnlockGlobal();
}

V_LOCAL	void	after_exit_thread()
{
	global_data_p data;
	data=libvLockGlobal();
	data->thread_count--;
	if(data->thread_count)
		libvUnlockGlobal();
	else
	{
		/*	This is the last thread, delete key, restore local tsd
		 *	and switch TSD functions back to stubs */
		void *main_specific;
		main_specific=libvGetSpecific();
		tsd_switch_to_stubs();
		tsd_destroy_private_key();
		libvSetSpecific(main_specific);
		/* Now we can use global data without locks */
		libvUnlockGlobal();
		libv_unprotect_section();
	}
}

/*
 *	Returns 'true' if library works in single-threading mode
 *	(i.e. if there is only one thread - caller thread - and it's
 *	therefore safe to deal with global data without any locks).
 */
vboolean	V_GLOBAL
libvSafeThread(void)
{
	vboolean safe;
	global_data_p data;
	data=libvLockGlobal();
	safe = data->thread_count == 0 ? true : false;
	libvUnlockGlobal();
	return safe;
}

/*	=======================================================================
 *	Threads
 */
V_LOCAL	u32_t	V_CALL
threadDestroy(vhandle_t handle)
{
	/* We are now running in context of parent of 'handle' */
	thread_data_t *td=GetThreadData();
	os_thread_p thread=(os_thread_p)handle;
    if(--thread->base.ref_count)
    	return thread->base.ref_count;
#ifdef	__WIN32__
	if(WaitForSingleObject(thread->handle,INFINITE)
		!=WAIT_OBJECT_0)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"threadDestroy(): WaitForSingleObject() "
			"failed (code %d).\n",GetLastError());
#endif	/* CHECKED_BUILD */
		return 1; /* still exists */
	}
	GetExitCodeThread(thread->handle,(LPDWORD)&td->child_exit_code);
#elif	defined(__UNIX__)
	if(pthread_join(thread->id,(void**)&td->child_exit_code))
		return 1; /* still exists */
#endif	/* __UNIX__ */
	after_exit_thread();
    memFreeObject(handle);
    return 0;
}

thread_t	V_GLOBAL
threadCreate(thread_proc_t start_address,void *arg)
{
#ifdef	__UNIX__
	struct sched_param sp;
#endif	/* __UNIX__ */
	os_thread_t *thread;
	if(!start_address)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"threadCreate(NULL,%X): no thread entry provided.\n",
			arg);
#endif	/* CHECKED_BUILD */
		return NULL;
	}
    ALLOC_OBJECT(thread,os_thread_t,0,THREAD_MAGIC);
    thread->base.destroy=threadDestroy;
	thread->start_address=start_address;
	thread->arg=arg;
	before_start_thread();
#ifdef	__WIN32__
	thread->handle=CreateThread(NULL,0,Win32ThreadEntry,thread,0,
		&thread->id);
	if(!thread->handle)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"threadCreate(%X,%X): CreateThread() "
			"failed.\n",start_address,arg);
#endif	/* CHECKED_BUILD */
		libvCloseHandle(thread,"threadCreate",thread_object_name,THREAD_MAGIC);
		return NULL;
	}
#elif	defined(__UNIX__)	/* !__WIN32__ */
	pthread_attr_init(&thread->attr);
	pthread_attr_setscope(&thread->attr,PTHREAD_SCOPE_PROCESS);
	pthread_attr_setdetachstate(&thread->attr,PTHREAD_CREATE_JOINABLE);
	if(!pthread_attr_getschedparam(&thread->attr,&sp))
	{
		sp.sched_priority=5;
		pthread_attr_setschedparam(&thread->attr,&sp);
	}
	if(pthread_create(&thread->id,&thread->attr,
		PosixThreadEntry,(void*)thread)==-1)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"threadCreate(%X,%X): pthread_create() "
			"failed.\n",start_address,arg);
#endif	/* CHECKED_BUILD */
		libvCloseHandle(thread,"threadCreate",thread_object_name,
        	THREAD_MAGIC);
		return NULL;
	}
#else	/* !__UNIX__ */
	/* No support for threads: chain control of this thread */
	StubThreadEntry((void*)thread);
#endif	/* !__UNIX__ */
	return (thread_t)thread;
}

void		V_GLOBAL
threadExit(u32_t exit_code)
{
	libvPerThreadShutdown();
#ifdef	__WIN32__
	ExitThread(exit_code);
#elif	defined(__UNIX__)
	((os_thread_t*)GetThreadData()->this_thread)->active=false;
	pthread_exit((void*)exit_code);
#else	/* !__UNIX__ */
	return;
#endif	/* __WIN32__ */
}

vboolean	V_GLOBAL
threadJoin(thread_t handle,u32_t *exit_code)
{
	thread_data_t *td;
    /* We NEED to know is handle valid or not */
    if(libvIsHandle(handle,"threadJoin",thread_object_name,
    	THREAD_MAGIC)==NULL)
        return false;
	td=GetThreadData();
	td->child_exit_code=0;
    /*
     * Now we are going to call child's destructor in context of THIS
     * thread, so it will successfully store exit code in ours TSD.
     */
    if(__libvCloseHandle(handle)==0)
	{
		/* thread object not exist anymore; get exit code */
	    if(exit_code)
	    	*exit_code=td->child_exit_code;
		return true;
	}
   	return false;
}

vboolean	V_GLOBAL
threadLive(thread_t thread)
{
	thread=(thread_t)libvIsHandle((void*)thread,"threadLive",
		thread_object_name,THREAD_MAGIC);
	if(!thread)
		return false;
#ifdef	__WIN32__
	if(WaitForSingleObject(((os_thread_t*)thread)->handle,0)==WAIT_OBJECT_0)
		return false;
	return true;
#elif	defined(__UNIX__)
	return ((os_thread_t*)thread)->active;
#else	/* !__UNIX__ */
	return false;
#endif	/* !__UNIX__ */
}

/* Yields control to another thread / process */
void	V_GLOBAL
schedYield(void)
{
#ifdef	__WIN32__
#ifdef	MUST_DIE_DEAD
	SwitchToThread();
#else	/* MUST_DIE_LIVE */
	Sleep(1);			/* There is no way to do it under MustDie 9x */
#endif	/* MUST_DIE_LIVE */
#else	/* !__WIN32__ */
	/* usleep(1); - to switch to another process ??? */
	sched_yield();
#endif	/* !__WIN32__ */
}

/*	=======================================================================
 *	Mutexes
 */
V_LOCAL	u32_t	V_CALL
mutex_destroy(vhandle_t handle)
{
	/*
	 * First of all, release all waiting threads; it probably cause
	 * core dump, but deadlock is worse. In either case, presence of
	 * waiting threads at the time of mutexDestroy() mean broken code.
	 */
#ifdef CHECKED_BUILD
	u32_t unlock_count = 0;
#endif /* CHECKED_BUILD */
	os_mutex_p mutex=(os_mutex_p)handle;
	if(--mutex->base.ref_count)
		return mutex->base.ref_count;
#ifdef	__WIN32__

#ifdef	MUST_DIE_DEAD
	/* NT-only version */
	unlock_count = mutex->lock_count;
	while(mutex->lock_count--)
	{
		/* FIXME: this works without any timeouts and SwitchToThread()s */
		LeaveCriticalSection(&mutex->cs);
		SwitchToThread();
	}
	DeleteCriticalSection(&mutex->cs);
#else	/* !MUST_DIE_DEAD */
	/* Portable WIN32 version */
	while(ReleaseMutex(mutex->mutex))
	{
		unlock_count++;
    	Sleep(1);
    }
	CloseHandle(mutex->mutex);

#endif	/* !MUST_DIE_DEAD */

#elif	defined(__UNIX__) /* !__WIN32__ */
	while(pthread_mutex_destroy(&mutex->handle) != 0)
	{
#ifdef CHECKED_BUILD
		unlock_count++;
#endif /* CHECKED_BUILD */
		pthread_mutex_unlock(&mutex->handle);
		timeDelay(1); /* let buggy code lock it again ! */
	}
	pthread_mutexattr_destroy(&mutex->attr);
#endif	/* __UNIX__ */

#ifdef CHECKED_BUILD
	/* Complain before core dump */
	if(unlock_count > 0)
	{
		conlPrintf(LOG_ERROR,
			"mutexDestroy(): released %u times, broken code.\n",
			unlock_count);
	}
#endif /* CHECKED_BUILD */

	memFreeObject(handle);
	return 0;
}

mutex_t		V_GLOBAL
mutexCreate()
{
	os_mutex_t *mutex;
	ALLOC_OBJECT(mutex,os_mutex_t,0,MUTEX_MAGIC);
	mutex->base.destroy=mutex_destroy;
#ifdef	__WIN32__
#ifdef	MUST_DIE_DEAD
	InitializeCriticalSection(&mutex->cs);
	mutex->lock_count = 0;
#else	/* 	MUST_DIE_LIVE */
	mutex->mutex=CreateMutex(NULL,FALSE,NULL);
#endif	/* 	MUST_DIE_LIVE */
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutexattr_init(&mutex->attr);
#ifdef	__USE_UNIX98
	pthread_mutexattr_settype(&mutex->attr,PTHREAD_MUTEX_RECURSIVE);
#else	/* !__USE_UNIX98 */
	/* FIXME: pthread_mutexattr_setkind_np() may be undefined */
	pthread_mutexattr_setkind_np(&mutex->attr,PTHREAD_MUTEX_RECURSIVE_NP);
#endif	/* !__USE_UNIX98 */
	pthread_mutex_init(&mutex->handle,&mutex->attr);
#endif	/* __UNIX__ */
	return mutex;
}

void	V_GLOBAL
mutexLock(mutex_t mutex)
{
	os_mutex_t *osm = (os_mutex_t *)mutex;
	/*
	 * FIXME: libvIsHandle may take too long time to execute, even
	 * if defined(CHECKED_BUILD). May be, V_ASSERT(mutex) ?
	 */
	if(libvIsHandle(osm,"mutexLock",mutex_object_name,MUTEX_MAGIC) == NULL)
		return;
#ifdef	__WIN32__
#ifdef	MUST_DIE_DEAD
	EnterCriticalSection(&osm->cs);
	osm->lock_count++;
#else	/* 	MUST_DIE_LIVE */
	WaitForSingleObject(osm->mutex,INFINITE);
#endif	/* 	MUST_DIE_LIVE */
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&osm->handle);
#endif	/* __UNIX__ */
}

vboolean	V_GLOBAL
mutexTryLock(mutex_t mutex)
{
	/*	Calling mutexTryLock(NULL) is NOT considered as
	 *	an error. (FIXME ?) */
	if(mutex == NULL)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_TRACE,
			"mutexTryLock(NULL): NULL handle, impossible to lock.\n");
#endif	/* CHECKED_BUILD */
		return false;
	}
#ifdef	CHECKED_BUILD
	mutex=(mutex_t)libvIsHandle((void*)mutex,"mutexTryLock",
		mutex_object_name,MUTEX_MAGIC);
	if(mutex == NULL)
		return false;
#endif	/* CHECKED_BUILD */
#ifdef	__WIN32__
#ifdef	MUST_DIE_DEAD
	if(TryEnterCriticalSection(&((os_mutex_t *)mutex)->cs))
	{
		((os_mutex_t *)mutex)->lock_count++;
		return true;
	}
	else
		return false;
#else	/* 	MUST_DIE_LIVE */
	if(WaitForSingleObject(((os_mutex_t *)mutex)->mutex,0)
		==WAIT_OBJECT_0)
		return true;
	else
		return false;
#endif	/* 	MUST_DIE_LIVE */
#elif	defined(__UNIX__)
	if(pthread_mutex_trylock(&((os_mutex_t *)mutex)->handle)!=0)
		return false;
	else
		return true;
#else	/* !__UNIX__ */
	/* Nothing to do: locks always succeded */
	return true;
#endif	/* __WIN32__ */
}

void	V_GLOBAL
mutexUnlock(mutex_t mutex)
{
	mutex=(mutex_t)libvIsHandle((void*)mutex,"mutexUnlock",
		mutex_object_name,MUTEX_MAGIC);
	if(mutex == NULL)
		return;
#ifdef	__WIN32__
#ifdef	MUST_DIE_DEAD
	if(((os_mutex_t *)mutex)->lock_count > 0)
		((os_mutex_t *)mutex)->lock_count--;
	LeaveCriticalSection(&((os_mutex_t *)mutex)->cs);
#else	/* 	MUST_DIE_LIVE */
	ReleaseMutex(((os_mutex_t *)mutex)->mutex);
#endif	/* 	MUST_DIE_LIVE */
#elif	defined(__UNIX__)
	pthread_mutex_unlock(&((os_mutex_t *)mutex)->handle);
#else	/* !__UNIX__ */
	/* Nothing to do */
#endif	/* __WIN32__ */
	return;
}

void		V_GLOBAL
mutexDestroy(mutex_t mutex)
{
	libvCloseHandle(mutex,"mutexDestroy",mutex_object_name,MUTEX_MAGIC);
}

/*	=======================================================================
 *	Conditional variables (events)
 */
V_LOCAL u32_t	V_CALL
cond_destroy(vhandle_t handle)
{
	os_cond_p cond=(os_cond_p)handle;
	if(--cond->base.ref_count)
		return cond->base.ref_count;
#ifdef	__WIN32__
	CloseHandle(cond->cond);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_cond_broadcast(&cond->cond);
	pthread_cond_destroy(&cond->cond);
	pthread_mutex_destroy(&cond->mutex);
#else	/* !__UNIX__ */
	/* Nothing to do */
#endif	/* !__UNIX__ */
	memFreeObject(handle);
	return 0;
}

cond_t		V_GLOBAL
condCreate(vboolean autoreset)
{
	os_cond_t *cond;
	ALLOC_OBJECT(cond,os_cond_t,0,COND_MAGIC);
	cond->base.destroy=cond_destroy;
#ifdef	__WIN32__
	cond->cond=CreateEvent(NULL,autoreset ? FALSE : TRUE,FALSE,NULL);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_cond_init(&cond->cond,NULL);
	pthread_mutex_init(&cond->mutex,NULL);
	cond->state=false;
	cond->pulse=false;
	cond->autoreset=autoreset;
#else	/* !__UNIX__ */
	cond->state=false;
#endif	/* !__UNIX__ */
	return (cond_t)cond;
}

vboolean	V_GLOBAL
condSet(cond_t cond)
{
	cond=(cond_t)libvIsHandle((void*)cond,"condSet",
		cond_object_name,COND_MAGIC);
	if(!cond)
		return false;
#ifdef	__WIN32__
	SetEvent(((os_cond_t*)cond)->cond);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&((os_cond_t *)cond)->mutex);
	((os_cond_t *)cond)->state=true;
	pthread_cond_broadcast(&((os_cond_t *)cond)->cond);
	pthread_mutex_unlock(&((os_cond_t *)cond)->mutex);
#else	/* !__UNIX__ */
	((os_cond_t *)cond)->state=true;
#endif	/* !__UNIX__ */
	return true;
}

vboolean	V_GLOBAL
condPulse(cond_t cond)
{
	cond=(cond_t)libvIsHandle((void*)cond,"condPulse",
		cond_object_name,COND_MAGIC);
	if(!cond)
		return false;
#ifdef	__WIN32__
	PulseEvent(((os_cond_t*)cond)->cond);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&((os_cond_t *)cond)->mutex);
	((os_cond_t *)cond)->state=true;
	((os_cond_t *)cond)->pulse=true;
	pthread_cond_broadcast(&((os_cond_t *)cond)->cond);
	pthread_mutex_unlock(&((os_cond_t *)cond)->mutex);
#else	/* !__UNIX__ */
	/* Nothing to do */
#endif	/* !__UNIX__ */
	return true;
}

vboolean	V_GLOBAL
condReset(cond_t cond)
{
	cond=(cond_t)libvIsHandle((void*)cond,"condReset",
		cond_object_name,COND_MAGIC);
	if(!cond)
		return false;
#ifdef	__WIN32__
	ResetEvent(((os_cond_t*)cond)->cond);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&((os_cond_t *)cond)->mutex);
	((os_cond_t *)cond)->state=false;
	((os_cond_t *)cond)->pulse=false;
	pthread_mutex_unlock(&((os_cond_t *)cond)->mutex);
#else	/* !__UNIX__ */
	((os_cond_t *)cond)->state=false;
#endif	/* !__UNIX__ */
	return true;
}

vboolean	V_GLOBAL
condWait(cond_t cond)
{
	cond=(cond_t)libvIsHandle((void*)cond,"condWait",
		cond_object_name,COND_MAGIC);
	if(!cond)
		return false;
#ifdef	__WIN32__
	if(WaitForSingleObject(((os_cond_t*)cond)->cond,INFINITE)==
		WAIT_OBJECT_0)
		return true;
	else
		return false;
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&((os_cond_t *)cond)->mutex);
	if(((os_cond_t *)cond)->autoreset)
		((os_cond_t *)cond)->state=false;
	if(((os_cond_t *)cond)->pulse)
	{
		((os_cond_t *)cond)->pulse=false;
		((os_cond_t *)cond)->state=false;
	}
	while(!((os_cond_t *)cond)->state)
		pthread_cond_wait(&((os_cond_t *)cond)->cond,
			&((os_cond_t *)cond)->mutex);
	pthread_mutex_unlock(&((os_cond_t *)cond)->mutex);
	return true;
#else	/* !__UNIX__ */
	/* We can't stuck here on single-threaded OS ! */
#ifdef	CHECKED_BUILD
	if(((os_cond_t *)cond)->state == false)
	{
		conlPuts(LOG_WARNING,"condWait(): current cond state will cause "
			"deadlock; ignored.\n");
	}
#endif	/* CHECKED_BUILD */
	return true;
#endif	/* !__UNIX__ */
}

vboolean	V_GLOBAL
condTimedWait(cond_t cond,u32_t milliseconds)
{
#ifdef	__UNIX__
	struct timeval now;
    struct timespec timeout;
	struct timezone tz={0,0};
	vboolean state=false;
#endif	/* !__UNIX__ */
	cond=(cond_t)libvIsHandle((void*)cond,"condTimedWait",
		cond_object_name,COND_MAGIC);
	if(!cond)
		return false;
#ifdef	__WIN32__
	if(WaitForSingleObject(((os_cond_t*)cond)->cond,milliseconds)==
		WAIT_OBJECT_0)
		return true;
	else
		return false;
#elif	defined(__UNIX__) /* !__WIN32__ */
	/* This seems to be pretty complex. Are there better ways ? */
	pthread_mutex_lock(&((os_cond_t *)cond)->mutex);
	if(((os_cond_t *)cond)->autoreset)
		((os_cond_t *)cond)->state=false;
	if(((os_cond_t *)cond)->pulse)
	{
		((os_cond_t *)cond)->pulse=false;
		((os_cond_t *)cond)->state=false;
	}
	if(((os_cond_t *)cond)->state || !milliseconds)
	{
		state=((os_cond_t *)cond)->state;
		pthread_mutex_unlock(&((os_cond_t *)cond)->mutex);
		return state;
	}
	gettimeofday(&now,&tz);
	timeout.tv_sec = now.tv_sec + milliseconds/1000;
	timeout.tv_nsec = (now.tv_usec+(milliseconds % 1000)*1000)*1000;
	for(;;)
	{
		if(pthread_cond_timedwait(&((os_cond_t *)cond)->cond,
			&((os_cond_t *)cond)->mutex,&timeout))
			break;
		state=((os_cond_t *)cond)->state;
		if(state)
			break;
	}
	pthread_mutex_unlock(&((os_cond_t *)cond)->mutex);
	return state;
#else	/* !__UNIX__ */
	state=((os_cond_t *)cond)->state;
	/* If not signaled, just delay execution */
	if(!state && milliseconds)
		timeDelay(milliseconds);
	return state;
#endif	/* !__UNIX__ */
}

void		V_GLOBAL
condDestroy(cond_t cond)
{
	libvCloseHandle(cond,"condDestroy",cond_object_name,COND_MAGIC);
}
