/*	XGSemaphore.cpp
 *
 *		Semaphore support. This encapsulates semaphore functionality on
 *	the destination architecture
 *
 *		Note: If this is a non-threaded build of the YAAF libraries,
 *	this still maintains an enter/leave critical count in case I'm
 *	recursively calling something I should't be. That'll at least simplify
 *	porting a non-threaded YAAF application into a threaded YAAF build.
 */

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or any
 *  later version.
 *  
 *  This library is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library General
 *  Public License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XError.h>
#include <XThread.h>

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

// @G"Construction/Destruction"

/*	@F"XGSemaphore::XGSemaphore" @I
 *
 *		Create this semaphore. The count provided is the maximum number
 *	of threads that are permitted to pass the "EnterCritical" section.
 *	By default that is set to 1.
 */

XGSemaphore::XGSemaphore(unsigned long max)
{
#if OPT_THREADED == 1
	/*
	 *	Threads are supported
	 */
	
#if OPT_MACOS == 1
	fAccess = max;
#endif

#if OPT_WINOS == 1
	fHandle = CreateSemaphore(NULL,max,max,NULL);
#endif

#if OPT_XWIN == 1
	pthread_mutex_init(&fMutex,NULL);
	pthread_cond_init(&fCond,NULL);
	fAccess = max;
#endif
#else

	/*
	 *	Threads are not supported
	 */
	
	fAccess = max;
#endif
}

/*	@F"XGSemaphore::~XGSemaphore" @I
 *
 *		Delete this thing. It is undefined what happens when you
 *	delete this thing while something is in the critical section.
 */

XGSemaphore::~XGSemaphore()
{
#if OPT_THREADED == 1
	/*
	 *	Threads are supported
	 */
	
#if OPT_WINOS == 1
	if (fHandle) CloseHandle(fHandle);
#endif // OPT_WINOS
#endif // OPT_THREADED
}

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*	@F"XGSemaphore::EnterCritical" @I
 *
 *		Enter critical section that is protected by this semaphore
 */
 
void XGSemaphore::EnterCritical(void)
{
#if OPT_THREADED == 1
	/*
	 *	Threads are supported
	 */
	
#if OPT_MACOS == 1
	ThreadBeginCritical();				// Stop tasks swap temporarly
	
	while (fAccess == 0) {
		ThreadEndCritical();
		YieldToAnyThread();				// Unable to enter yet.
		ThreadBeginCritical();
	}
	fAccess--;							// I'm in. Allow access again.
	
	ThreadEndCritical();
#endif

#if OPT_WINOS == 1
	WaitForSingleObject(fHandle,INFINITE);
#endif

#if OPT_XWIN == 1
	// lock
	pthread_mutex_lock(&fMutex);
	
	// now see if I have access
	while (fAccess == 0) {
		pthread_cond_wait(&fCond,&fMutex);
	}
	
	// I'm in. Decrement the count to indicate that I'm in
	fAccess--;
	
	// unlock
	pthread_mutex_unlock(&fMutex);
#endif
#else // OPT_THREADED == 0
	/*
	 *	Threads are not supported
	 */
	
	Assert(fAccess != 0);
	fAccess--;
#endif // OPT_THREADED
}

/*	@F"XGSemaphore::LeaveCritical" @I
 *
 *		Leave critical section
 */

void XGSemaphore::LeaveCritical(void)
{
#if OPT_THREADED == 1
	/*
	 *	Threads are supported
	 */
	
#if OPT_MACOS == 1
	ThreadBeginCritical();				// Stop tasks swap temporarly
	fAccess++;							// Atomic increment allowed access
	ThreadEndCritical();				// Restart task swapping
#endif

#if OPT_WINOS == 1
	ReleaseSemaphore(fHandle,1,NULL);
#endif

#if OPT_XWIN == 1
	// lock
	pthread_mutex_lock(&fMutex);
	
	// Increment access count
	fAccess++;
	
	// Send the message if I was locked before. (Locking only happens
	// when fAccess is zero, so send the conditional unlock when this
	// is one.
	if (fAccess == 1) pthread_cond_signal(&fCond);
	
	// unlock
	pthread_mutex_unlock(&fMutex);
#endif
#else // OPT_THREADED == 0
	/*
	 *	Threads are not supported
	 */
	
	fAccess++;
#endif
}
