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

/*
 * This is LibV's Core: multitasking environment wrapper for NT and POSIX.
 * Last update: 21.01.2006
 *
 * TODO: separate to:
 *  _ move all arch-dependent calls to /arch subtree; _
 *
 *	threadsd.c - thread local storage management;
 *	thread.c - abstract 'thread' object;
 *	mutex.c - abstract 'mutex' object;
 *	cond.c - conditional variables;
 *	shared.c - libv global data management.
 *
 *	Global section should be protected by arch_mutex_t, thus we can
 *	avoid code duplication.
 */

/*
 * 02.07.04 - Win32: call to SwitchToThread() replaced by code
 * of the ZwYieldExecution() (kernel call via int 2E).
 * UPD: as of nov 2010, this call is still UNTESTED neither under NT 6.0, nor under NT 6.1.
 * 05.09.04 - split process started
 * 21.01.06 - conditional vars: code cleanups
 */

#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'
 */

arch_mutex_t	 libv_global_lock;

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

/*
 *	Debug stuff to track deadlocks inside LibV code.
 *	Enabling dumps _significantly_ slows the 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 */

V_LOCAL	global_data_p
libv_acquire_locked(void)
{
	DUMP_LOCK_UNLOCK("lock_protected");
	archLockMutex(&libv_global_lock);
	return &(libv_global_data);
}

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

V_LOCAL	void
libv_release_locked(void)
{
	DUMP_LOCK_UNLOCK("libv_release_locked");
	archUnlockMutex(&libv_global_lock);
	return;
}

V_LOCAL	void
libv_release_unlocked(void)
{
	DUMP_LOCK_UNLOCK("libv_release_unlocked");
	return;
}

/*
 * Returns contents of guarded section with global data.
 * Section than *MUST* be unlocked by libvUnlockGlobal().
 */
global_data_p	(*libvLockGlobal)(void) = libv_acquire_unlocked;

/*
 * Unlocks global data, makes it possible to use by another threads.
 */
void			(*libvUnlockGlobal)(void) = libv_release_unlocked;

/*
 * 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)
{
	archCreateMutex(&libv_global_lock);
	libv_acquire_locked();
	libvLockGlobal = libv_acquire_locked;
	libvUnlockGlobal = libv_release_locked;
	/* NOTE: we leave mutex in locked state */
}

V_LOCAL void
libv_unprotect_section(void)
{
	libv_acquire_locked();
	libvLockGlobal = libv_acquire_unlocked;
	libvUnlockGlobal = libv_release_unlocked;
	libv_release_locked();
	archDestroyMutex(&libv_global_lock);
}

/*
 * Since 05.09.04, support for mutexes is platform-independent
 */

typedef struct	libv_mutex_s
{
	libv_object_t	base;
	arch_mutex_t	mutex;
}	libv_mutex_t, *libv_mutex_p;

V_LOCAL u32_t V_CALL
libv_mutex_destroy(vhandle_t handle)
{
	libv_mutex_p mutex = (libv_mutex_p)handle;
	UNREF_EXIT(mutex);
	archDestroyMutex(&mutex->mutex);
	memFreeObject(handle);
	return 0;
}

mutex_t V_GLOBAL
mutexCreate()
{
	libv_mutex_p mutex;
	ALLOC_OBJECT(mutex,libv_mutex_t,0,MUTEX_MAGIC);
	mutex->base.destroy = libv_mutex_destroy;
	if(!archCreateMutex(&mutex->mutex))
	{
		memFreeObject(mutex);
		return LIBV_INVALID_HANDLE;
	}
	return mutex;
}

#define	GET_MUTEX(handle) (libv_mutex_p)libvIsHandle(handle, \
	caller_name,mutex_object_name,MUTEX_MAGIC)

void V_GLOBAL
mutexLock(mutex_t handle)
{
	CALLER_NAME(mutexLock)
	/*
	 * FIXME: libvIsHandle may take too long time to execute, even
	 * if !defined(CHECKED_BUILD). May be, V_ASSERT(mutex) ?
	 */
	libv_mutex_p mutex = GET_MUTEX(handle);
	if(mutex == LIBV_INVALID_HANDLE)
		return;
	archLockMutex(&mutex->mutex);
}

vboolean	V_GLOBAL
mutexTryLock(mutex_t handle)
{
	CALLER_NAME(mutexTryLock)
	libv_mutex_p mutex = GET_MUTEX(handle);
	if(mutex == LIBV_INVALID_HANDLE)
		return false;
	return archTryLockMutex(&mutex->mutex);
}

void	V_GLOBAL
mutexUnlock(mutex_t handle)
{
	CALLER_NAME(mutexUnlock)
	libv_mutex_p mutex = GET_MUTEX(handle);
	if(mutex == LIBV_INVALID_HANDLE)
		return;
	archUnlockMutex(&mutex->mutex);
}

void		V_GLOBAL
mutexDestroy(mutex_t handle)
{
	CALLER_NAME(mutexDestroy)
	libvCloseHandle(handle,caller_name,mutex_object_name,MUTEX_MAGIC);
}

#undef	GET_MUTEX

/*
 *	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 */
	}
	/*
     *	We can't use memAlloc() because at this moment pointers'
     *	hashes are not initialized.
     *  FIXME: archBigAlloc() + archBigFree() + full-blown memory management.
     */
	storage = (thread_data_t*)malloc(sizeof(thread_data_t));
	if(!storage)
	{
#ifdef	CHECKED_BUILD
		sprintf(buf,"libvPerThreadInit(): failed on allocation %u "
			"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;

/*
 *	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;

/*
 * 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;

/*
 *	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);	/* 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__)
		fprintf(stderr,
			"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-threaded mode
 *	(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;
	UNREF_EXIT(thread);
#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 */
    
    /* 2007.apr.04 - What The F... ??? - check it : NULL | LIBV_IVLD_HNDL ? */
    
    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
threadIsLive(thread_t thread)
{
	thread=(thread_t)libvIsHandle((void*)thread,"threadIsLive",
		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 */

#if	defined(__WIN32__) && defined(CONFIG_NT_ONLY)

/*
 * There is no SwitchToThread() entry in many import libs.
 * Assume we have inline assembly (FIXME).
 */
void	__stdcall zw_yield_execution(void)
{
	__asm {
		mov eax, 0xF7
		lea edx, [esp+4]
		int 0x2E /* "уходим под воду в нейтральной воде"... */
		/* eax now contains 0x40000024 if thread was _not_ switched */
	}
}
#endif

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

/*	=======================================================================
 *	Conditional variables (events)
 */

#define	GET_COND(handle) \
	(os_cond_p)libvIsHandle((vhandle_t)(handle),caller_name, \
		cond_object_name,COND_MAGIC)

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;
}

vhandle_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 (vhandle_t)cond;
}

vboolean	V_GLOBAL
condSet(vhandle_t handle)
{
	CALLER_NAME(condSet)
	os_cond_p cond = GET_COND(handle);
	if(!cond)
		return false;
#ifdef	__WIN32__
	SetEvent(cond->cond);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&cond->mutex);
	cond->state = true;
	pthread_cond_broadcast(&cond->cond);
	pthread_mutex_unlock(&cond->mutex);
#else	/* !__UNIX__ */
	cond->state = true;
#endif	/* !__UNIX__ */
	return true;
}

vboolean	V_GLOBAL
condPulse(vhandle_t handle)
{
	CALLER_NAME(condPulse)
	os_cond_p cond = GET_COND(handle);
	if(!cond)
		return false;
#ifdef	__WIN32__
	PulseEvent(cond->cond);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&cond->mutex);
	cond->state = true;
	cond->pulse = true;
	pthread_cond_broadcast(&cond->cond);
	pthread_mutex_unlock(&cond->mutex);
#else	/* !__UNIX__ */
	/* Nothing to do */
#endif	/* !__UNIX__ */
	return true;
}

vboolean	V_GLOBAL
condReset(vhandle_t handle)
{
	CALLER_NAME(condReset)
	os_cond_p cond = GET_COND(handle);
	if(!cond)
		return false;
#ifdef	__WIN32__
	ResetEvent(cond->cond);
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&cond->mutex);
	cond->state = false;
	cond->pulse = false;
	pthread_mutex_unlock(&cond->mutex);
#else	/* !__UNIX__ */
	cond->state = false;
#endif	/* !__UNIX__ */
	return true;
}

vboolean	V_GLOBAL
condWait(vhandle_t handle)
{
	CALLER_NAME(condWait)
	os_cond_p cond = GET_COND(handle);
	if(!cond)
		return false;
#ifdef	__WIN32__
	if(WaitForSingleObject(cond->cond,INFINITE) == WAIT_OBJECT_0)
		return true;
	return false;
#elif	defined(__UNIX__) /* !__WIN32__ */
	pthread_mutex_lock(&cond->mutex);
	if(cond->autoreset)
		cond->state = false;
	if(cond->pulse)
	{
		cond->pulse = false;
		cond->state = false;
	}
	while(!cond->state)
		pthread_cond_wait(&cond->cond,&cond->mutex);
	pthread_mutex_unlock(&cond->mutex);
	return true;
#else	/* !__UNIX__ */
	/* We can't stuck here on single-threaded OS ! */
#ifdef	CHECKED_BUILD
	if(!cond->state)
    	WARNING_GENERIC("current cond state will cause deadlock; ignored");
#endif	/* CHECKED_BUILD */
	return true;
#endif	/* !__UNIX__ */
}

vboolean	V_GLOBAL
condTimedWait(vhandle_t handle,u32_t milliseconds)
{
	CALLER_NAME(condTimedWait)
#ifdef	__UNIX__
	struct timeval now;
    struct timespec timeout;
	struct timezone tz={0,0};
	vboolean state=false;
#endif	/* !__UNIX__ */
	os_cond_p cond = GET_COND(handle);
	if(!cond)
		return false;
#ifdef	__WIN32__
	if(WaitForSingleObject(cond->cond,milliseconds) == WAIT_OBJECT_0)
		return true;
	return false;
#elif	defined(__UNIX__) /* !__WIN32__ */
	/* This seems to be pretty complex. Are there better ways ? */
	pthread_mutex_lock(&cond->mutex);
	if(cond->autoreset)
		cond->state = false;
	if(cond->pulse)
	{
		cond->pulse = false;
		cond->state = false;
	}
	if(cond->state || !milliseconds)
	{
		state = cond->state;
		pthread_mutex_unlock(&cond->mutex);
		return state;
	}
	/* FIXME: can we move syscall out of the lock? */
	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(&cond->cond,&cond->mutex,&timeout))
			break;
		state = cond->state;
		if(state)
			break;
	}
	pthread_mutex_unlock(&cond->mutex);
	return state;
#else	/* !__UNIX__ */
	state = cond->state;
	/* If not signaled, just delay execution */
	if(!state && milliseconds)
		timeDelay(milliseconds);
	return state;
#endif	/* !__UNIX__ */
}

void		V_GLOBAL
condDestroy(vhandle_t handle)
{
	libvCloseHandle(handle,"condDestroy",cond_object_name,COND_MAGIC);
}

#undef	GET_COND
