/*	XThread.h
 *
 *		This encapsulates thread support
 */

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

#ifndef PandaWave_XTHREAD
#define PandaWave_XTHREAD

#include <XConfig.h>

#if OPT_MACOS == 1
#include <Threads.h>
#endif


#if OPT_MACOS || OPT_WINOS
	#define OPT_THREADED			1
#else
	// Unix environment: on Linux, I have pthreads, so...
	#define OPT_THREADED			1
	#include <pthread.h>
#endif

/************************************************************************/
/*																		*/
/*	Forwards															*/
/*																		*/
/************************************************************************/

class XGThread;

/************************************************************************/
/*																		*/
/*	Thread Support														*/
/*																		*/
/************************************************************************/

typedef void (* XGThreadProcPtr)(XGThread *, long arg);

/*	@C"XGThread"
 *	@H"XThread.h"
 *	@I
 *
 *		This is the thread object; this knows how to handle, identify,
 *	fold, and mutulate a thread object.
 *
 *		Attach/Detach semantics provides me a way to manipulate a
 *	thread, even after the thread dies. Normally, the XGThread class
 *	pointer is owned by the thread--when the thread dies, the XGThread
 *	class object is deleted. This sucks if you want to keep a pointer
 *	to the thread object pass it's expiration.
 *
 *		Thus, the Attach/Detach semantics allows other objects to 
 *	keep a pointer to the XGThread, and claim ownership past the
 *	thread's termination.
 *
 *		Note that when the thread is first created, it's owned by
 *	the thread itself. That means if you want to create this thread
 *	and keep a pointer to that thread, you need to explicitly attach
 *	to the thread after it's creation: thus,
 *
 *		XGThread *ptr = new XGThread(myProc,0,0,true);	// create suspended
 *		ptr->Attach();									// attach to it
 *		ptr->Resume();									// and resume execution
 */

class XGThread {
	public:
								XGThread(XGThreadProcPtr,
										 long arg = 0,
										 long stack = 0, 
										 bool suspend = false);
								XGThread();
		virtual					~XGThread();
		
		/*
		 *	Attach/detach semantics--for other threads which want
		 *	to know information about this thread.
		 */
		 
		// @G"Attach/Detach"
		
		/*	@F"XGThread::Attach" @I
		 *
		 *		Increments the attach count. When created, this
		 *	thread has an attach count of 1, corrisponding to
		 *	the thread procedure which owns this thread.
		 */
		
		void					Attach(void)
									{
										fAttach++;
									}
									
		/*	@F"XGThread::Detach" @I
		 *
		 *		Decrements the attach count, and if the count
		 *	reaches zero, deletes this object.
		 */
		
		void					Detach(void)
									{
										if (--fAttach <= 0) delete this;
									}
		
		/*	@F"XGThread::GetAttach" @I
		 *
		 *		Returns the attach count for this object
		 */
		
		long					GetAttach(void) const
									{
										return fAttach;
									}
		
		/*
		 *	Thread management routines
		 */
		 
		// @G"Thread Management"
		
		static XGThread			*CurrentThread();
		static void				YieldThread(bool f = false);
		
		/*	@F"XGThread::GetThreadCount" @I
		 *
		 *		Returns the number of threads that currently exist
		 */
		
		static long				GetThreadCount()
									{
										return gCount;
									}
#if OPT_MACOS
		static void				SetThreadPool(short number, long stack);
#endif
		
		/*
		 *	Scheduling routines
		 */
		
		void					Suspend(void);
		void					Resume(void);
		bool					IsSuspend(void);	// Thread suspended?
		
		void					Kill(void);
		bool					IsAlive(void);		// Valid thread?

	private:
		static long				gCount;
		long					fAttach;

#if OPT_THREADED == 1
		XGThreadProcPtr			fProc;
		long					fArg;
		
		XGThread				*fNext;
		static XGThread			*gList;

#if OPT_MACOS == 1
		ThreadID				fThreadID;
		static long				gStackSize;
		static bool				gThreadPool;
		static pascal void		*ThreadEntry(void *);
#endif

#if OPT_WINOS == 1
		bool					fThreadSuspend;
		HANDLE					fThreadHandle;
		unsigned int			fThreadID;
		
		static unsigned int WINAPI ThreadEntry(LPVOID);
#endif

#if OPT_XWIN == 1
		bool					fThreadValid;
		pthread_t				fThreadID;
		static void				*ThreadEntry(void *);
#endif

#endif // OPT_THREADED
};

/*	@C"XGSemaphore"
 *	@H"XThread.h"
 *	@I
 *
 *		This is a semaphore object. This allows me to create and
 *	control access to a chunk of critical code
 */

class XGSemaphore {
	public:
								XGSemaphore(unsigned long maxaccess = 1);
		virtual					~XGSemaphore();
		
		/*
		 *	Access routines
		 */
		
		void					EnterCritical(void);
		void					LeaveCritical(void);

	private:

#if OPT_THREADED == 1
	#if OPT_MACOS == 1
		unsigned long			fAccess;
	#endif

	#if OPT_WINOS == 1
		HANDLE					fHandle;
	#endif
	
	#if OPT_XWIN == 1
		pthread_mutex_t			fMutex;
		pthread_cond_t			fCond;
		unsigned long			fAccess;
	#endif
#else // OPT_THREADED == 0
	unsigned long				fAccess;
#endif // OPT_THREADED
};

/*	@C"XGCritical"
 *	@H"XThread.h"
 *	@I
 *
 *		Access object. This provides the enter/exit semantics in
 *	a stack object for semaphore access; this provides a return-safe
 *	and throw-safe way of providing enter/exit semaphore access
 */

class XGCritical {
	public:
		// @G"Construction/Destruction"
		
		/*	@F"XGCritical::XGCritical" @I
		 *
		 *		Constructs this object. Stores a pointer to the
		 *	semaphore, and calls the semaphore's "EnterCritical"
		 *	method.
		 */
		
								XGCritical(XGSemaphore *x)
									{
										fSemaphore = x;
										x->EnterCritical();
									}
									
		/*	@F"XGCritical::~XGCritical" @I
		 *
		 *		Destructs this object. This calls the "LeaveCritical"
		 *	method of the stored semaphore
		 */
		
								~XGCritical()
									{
										fSemaphore->LeaveCritical();
									}
	
	private:
		XGSemaphore				*fSemaphore;
};

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

#if OPT_MACOS
extern bool	GYieldThread;		// true--everyone wants to give up time
#endif

#endif /* PandaWave_XTHREAD */
