/**
 *	@file 	mprThread.c
 *	@brief 	Mbedthis Portable Runtime Base Thread Locking Support
 */

/*
 *	@copy	default
 *
 *	Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
 *	
 *	This software is distributed under commercial and open source licenses.
 *	You may use the GPL open source license described below or you may acquire 
 *	a commercial license from Mbedthis Software. You agree to be fully bound 
 *	by the terms of either license. Consult the LICENSE.TXT distributed with 
 *	this software for full details.
 *	
 *	This software is open source; you can redistribute it and/or modify it 
 *	under the terms of the GNU General Public License as published by the 
 *	Free Software Foundation; either version 2 of the License, or (at your 
 *	option) any later version. See the GNU General Public License for more 
 *	details at: http://www.mbedthis.com/downloads/gplLicense.html
 *	
 *	This program is distributed WITHOUT ANY WARRANTY; without even the 
 *	implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *	
 *	This GPL license does NOT permit incorporating this software into 
 *	proprietary programs. If you are unable to comply with the GPL, you must
 *	acquire a commercial license to use this software. Commercial licenses 
 *	for this software and support services are available from Mbedthis 
 *	Software at http://www.mbedthis.com 
 *	
 *	@end
 */

#include	"mpr.h"

#if BLD_FEATURE_MULTITHREAD
/************************************ Code ************************************/

void mprInitThreads(MprApp *app)
{
	mprAssert(app);

	if (app->globalLock == 0) {
		app->globalLock = mprCreateLock(app);
		app->allocLock = mprCreateLock(app);
	}
}

/******************************************************************************/

void mprTermThreads(MprApp *app)
{
	mprAssert(app);

	if (app->globalLock) {
		mprDestroyLock(app->globalLock);
		app->globalLock = 0;
	}
	if (app->allocLock) {
		MprLock *lock = app->allocLock;
		app->allocLock = 0;
		mprDestroyLock(lock);
	}
}

/******************************************************************************/

MprLock *mprCreateLock(MprCtx ctx)
{
	MprLock	*lock;

	mprAssert(ctx);

	lock = mprAllocType(ctx, MprLock);

#if BLD_HOST_UNIX
	pthread_mutexattr_t	attr;

	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
	pthread_mutex_init(&lock->cs, &attr);
	pthread_mutexattr_destroy(&attr);
#elif WIN
	InitializeCriticalSectionAndSpinCount(&lock->cs, 5000);
#elif VXWORKS
	lock->cs = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | 
		SEM_INVERSION_SAFE);
	if (lock->cs == 0) {
		mprAssert(0);
		mprFree(lock);
		return 0;
	}
#endif
	return lock;
}

/******************************************************************************/
/*
 *	Destroy a lock. Must be locked on entrance.
 */ 

void mprDestroyLock(MprLock *lock)
{
	mprAssert(lock);
	if (lock == 0) {
		return;
	}

#if BLD_HOST_UNIX
	pthread_mutex_unlock(&lock->cs);
	pthread_mutex_destroy(&lock->cs);
#elif WIN
	DeleteCriticalSection(&lock->cs);
#elif VXWORKS
	semDelete(lock->cs);
#endif
	mprFree(lock);
}

/******************************************************************************/
/*
 *	Lock a mutex
 */ 

void mprLock(MprLock *lock)
{
	/*
	 *	OPT -- Do this just so we can allocate MprApp before we have created its
	 *	lock. Should remove this test here and in mprUnlock.
	 */
	if (lock == 0) {
		return;
	}

#if BLD_HOST_UNIX
	pthread_mutex_lock(&lock->cs);
#elif WIN
	EnterCriticalSection(&lock->cs);
#elif VXWORKS
	semTake(lock->cs, WAIT_FOREVER);
#endif
}

/******************************************************************************/
/*
 *	Try to attain a lock. Do not block!
 */ 

int mprTryLock(MprLock *lock)
{
	mprAssert(lock);

#if BLD_HOST_UNIX
	{
		int		err;

		if ((err = pthread_mutex_trylock(&lock->cs)) != 0) {
			if (err == EBUSY) {
				return MPR_ERR_BUSY;
			} else {
				return MPR_ERR_CANT_ACCESS;
			}
		}
		return 0;
	}
#elif WIN
	if (TryEnterCriticalSection(&lock->cs) == 0) {
		return MPR_ERR_BUSY;
	}
#elif VXWORKS
	{
		int		rc;

		rc = semTake(cs, NO_WAIT);
		if (rc == -1) {
			mprAssert(0);
		}
		if (rc == S_objLib_OBJ_UNAVAILABLE) {
			return MPR_ERR_BUSY;
		} else {
			return MPR_ERR_CANT_ACCESS;
		}
		/* Success */
		return 0;
	}
#endif
	return 0;
}

/******************************************************************************/
/*
 *	Unlock.
 */ 

void mprUnlock(MprLock *lock)
{
	if (lock == 0) {
		return;
	}

#if BLD_HOST_UNIX
	pthread_mutex_unlock(&lock->cs);
#elif WIN
	LeaveCriticalSection(&lock->cs);
#elif VXWORKS
	semGive(lock->cs);
#endif
}

/******************************************************************************/
/*
 *	Big global lock. Avoid using this.
 */

void mprGlobalLock(MprCtx ctx)
{
	MprApp	*app;

	app = mprGetApp(ctx);
	mprAssert(app);

	if (app && app->globalLock) {
		mprLock(app->globalLock);
	}
}

/******************************************************************************/

void mprGlobalUnlock(MprCtx ctx)
{
	MprApp	*app;

	app = mprGetApp(ctx);
	mprAssert(app);

	if (app && app->globalLock) {
		mprUnlock(app->globalLock);
	}
}

/******************************************************************************/

int mprGetCurrentThreadID()
{
#if BLD_HOST_UNIX
	return (int) pthread_self();
#elif WIN
	return GetCurrentThreadId();
#elif VXWORKS
	return (int) pthread_self();
#endif
}

/******************************************************************************/
#endif /* BLD_FEATURE_MULTITHREAD */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim:tw=78
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */