/*=============================================================================
	CAPThread.cp

	$Log: CAPThread.cpp,v $
	Revision 1.10  2005/02/24 02:26:35  dwyatt
	clear mPThread at the end of Entry even if there was an exception
	
	Revision 1.9  2004/09/30 21:03:40  jcm10
	add a definition for CompareAndSwap on Windows
	
	Revision 1.8  2004/08/26 08:13:33  jcm10
	finish bring up on Windows
	
	Revision 1.7  2004/08/26 04:42:02  jcm10
	add initial Windows support
	
	Revision 1.6  2004/08/23 06:23:25  jcm10
	make it build on Windows
	
	Revision 1.5  2004/07/17 00:13:50  asynth
	remove printfs
	
	Revision 1.4  2004/07/16 23:43:06  asynth
	set priority in the child thread
	
	Revision 1.3  2004/03/02 22:13:38  dwyatt
	add option to set fixed priority
	
	Revision 1.2  2004/03/02 20:29:25  dwyatt
	Add AssertNoError on calls to thread_policy_set
	
	Revision 1.1  2003/11/22 00:07:44  dwyatt
	initial checkin as .cpp
	
	Revision 1.5  2002/09/17 18:55:51  luke
	[2999188] fixed SetPriority(...) mechanism to work with new Mach API's.
	Also added call to get current scheduled priority of thread.
	
	Revision 1.4  2002/05/22 00:52:20  jcm10
	clean up the usage of the mach thread policy APIs
	
	Revision 1.3  2002/05/18 01:14:02  bills
	back out mistaken commit
	
	Revision 1.2  2002/05/18 01:06:09  bills
	moved files to public utils
	
	Revision 1.1  2002/03/01 01:52:40  jcm10
	moved here from ../Utility
	
	Revision 1.11  2002/02/28 23:24:29  jcm10
	added the CA prefix to DebugMacros and LogMacros for more consistency
	
	Revision 1.10  2002/02/14 23:30:32  jcm10
	clean up for gcc3
	
	Revision 1.9  2001/11/15 02:05:18  jcm10
	add an extern "C" around mach includes to make cpp-precomp happy
	
	Revision 1.8  2001/08/24 18:40:39  jcm10
	revamp the implementation to better reflect how mach works
	
	Revision 1.7  2001/01/23 01:14:01  jcm10
	in the time constraint methods, don't touch the thread unless it is actually there
	
	Revision 1.6  2001/01/16 22:23:57  jcm10
	support the time constraint thread scheduling policy
	
	Revision 1.5  2000/12/08 22:09:11  jcm10
	[2579498] include <mach/mach.h> instead of the actual mach headers
	
	Revision 1.4  2000/09/13 02:28:49  jcm10
	make it build again
	
	Revision 1.3  2000/09/12 23:14:46  jcm10
	add SetSchedulingPolicy and use it
	
	Revision 1.2  2000/08/25 01:07:26  jcm10
	update things to build the XFiles and prune the CoreAudio target to just the code necessary to run it
	
	Revision 1.1  2000/08/24 23:36:17  jcm10
	first checked in
	
	Revision 0.0  2000/01/01 12:34:56  jcm10
	created
		
	$NoKeywords: $
=============================================================================*/

//=============================================================================
//	Includes
//=============================================================================

//	Self Include
#include "CAPThread.h"

//	PublicUtility Includes
#include "CADebugMacros.h"
#include "CAException.h"

//	System Includes
#if	TARGET_OS_MAC
	#include <mach/mach.h>
#endif

//	Standard Library Includes
#include <stdio.h>

//==================================================================================================
//	CAPThread
//==================================================================================================

// returns the thread's priority as it was last set by the API
#define CAPTHREAD_SET_PRIORITY				0
// returns the thread's priority as it was last scheduled by the Kernel
#define CAPTHREAD_SCHEDULED_PRIORITY		1

CAPThread::CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPriority, bool inFixedPriority)
:
#if TARGET_OS_MAC
	mPThread(0),
    mSpawningThreadPriority(getScheduledPriority(pthread_self(), CAPTHREAD_SET_PRIORITY)),
#elif TARGET_OS_WIN32
	mThreadHandle(NULL),
	mThreadID(0),
#endif
	mThreadRoutine(inThreadRoutine),
	mThreadParameter(inParameter),
	mPriority(inPriority),
	mPeriod(0),
	mComputation(0),
	mConstraint(0),
	mIsPreemptible(true),
	mTimeConstraintSet(false),
	mFixedPriority(inFixedPriority)
{
}

CAPThread::CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible)
:
#if TARGET_OS_MAC
	mPThread(0),
    mSpawningThreadPriority(getScheduledPriority(pthread_self(), CAPTHREAD_SET_PRIORITY)),
#elif TARGET_OS_WIN32
	mThreadHandle(NULL),
	mThreadID(0),
#endif
	mThreadRoutine(inThreadRoutine),
	mThreadParameter(inParameter),
	mPriority(kDefaultThreadPriority),
	mPeriod(inPeriod),
	mComputation(inComputation),
	mConstraint(inConstraint),
	mIsPreemptible(inIsPreemptible),
	mTimeConstraintSet(true),
	mFixedPriority(false)
{
}

CAPThread::~CAPThread()
{
}

UInt32	CAPThread::GetScheduledPriority()
{
#if TARGET_OS_MAC
    return CAPThread::getScheduledPriority( mPThread, CAPTHREAD_SCHEDULED_PRIORITY );
#elif TARGET_OS_WIN32
	UInt32 theAnswer = 0;
	if(mThreadHandle != NULL)
	{
		theAnswer = GetThreadPriority(mThreadHandle);
	}
	return theAnswer;
#endif
}

void	CAPThread::SetPriority(UInt32 inPriority, bool inFixedPriority)
{
	mPriority = inPriority;
	mTimeConstraintSet = false;
	mFixedPriority = inFixedPriority;
#if TARGET_OS_MAC
	if(mPThread != 0)
	{
		
		if (mFixedPriority)
		{
			thread_extended_policy_data_t		theFixedPolicy;
			theFixedPolicy.timeshare = false;	// set to true for a non-fixed thread
			AssertNoError(thread_policy_set(pthread_mach_thread_np(mPThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT), "CAPThread::SetPriority: failed to set the fixed-priority policy");
		}
        // We keep a reference to the spawning thread's priority around (initialized in the constructor), 
        // and set the importance of the child thread relative to the spawning thread's priority.
        thread_precedence_policy_data_t		thePrecedencePolicy;
        
        thePrecedencePolicy.importance = mPriority - mSpawningThreadPriority;
        AssertNoError(thread_policy_set(pthread_mach_thread_np(mPThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT), "CAPThread::SetPriority: failed to set the precedence policy");
    } 
#elif TARGET_OS_WIN32
	if(mThreadHandle != NULL)
	{
		SetThreadPriority(mThreadHandle, mPriority);
	}
#endif
}

void	CAPThread::SetTimeConstraints(UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible)
{
	mPeriod = inPeriod;
	mComputation = inComputation;
	mConstraint = inConstraint;
	mIsPreemptible = inIsPreemptible;
	mTimeConstraintSet = true;
#if TARGET_OS_MAC
	if(mPThread != 0)
	{
		thread_time_constraint_policy_data_t thePolicy;
		thePolicy.period = mPeriod;
		thePolicy.computation = mComputation;
		thePolicy.constraint = mConstraint;
		thePolicy.preemptible = mIsPreemptible;
		AssertNoError(thread_policy_set(pthread_mach_thread_np(mPThread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&thePolicy, THREAD_TIME_CONSTRAINT_POLICY_COUNT), "CAPThread::SetTimeConstraints: thread_policy_set failed");
	}
#elif TARGET_OS_WIN32
	if(mThreadHandle != NULL)
	{
		SetThreadPriority(mThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);
	}
#endif
}

void	CAPThread::Start()
{
#if TARGET_OS_MAC
	if(mPThread == 0)
	{
		OSStatus			theResult;
		pthread_attr_t		theThreadAttributes;
		
		theResult = pthread_attr_init(&theThreadAttributes);
		ThrowIf(theResult != 0, CAException(theResult), "CAPThread::Start: Thread attributes could not be created.");
		
		theResult = pthread_attr_setdetachstate(&theThreadAttributes, PTHREAD_CREATE_DETACHED);
		ThrowIf(theResult != 0, CAException(theResult), "CAPThread::Start: A thread could not be created in the detached state.");
		
		theResult = pthread_create(&mPThread, &theThreadAttributes, (ThreadRoutine)CAPThread::Entry, this);
		ThrowIf(theResult != 0 || !mPThread, CAException(theResult), "CAPThread::Start: Could not create a thread.");
		
		pthread_attr_destroy(&theThreadAttributes);
		
	}
#elif TARGET_OS_WIN32
	if(mThreadID == 0)
	{
		//	clean up the existing thread handle
		if(mThreadHandle != NULL)
		{
			CloseHandle(mThreadHandle);
			mThreadHandle = NULL;
		}
		
		//	create a new thread
		mThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Entry, this, 0, &mThreadID);
		ThrowIf(mThreadHandle == NULL, CAException(GetLastError()), "CAPThread::Start: Could not create a thread.");
	}
#endif
}

#if TARGET_OS_MAC

void*	CAPThread::Entry(CAPThread* inCAPThread)
{
	void* theAnswer = NULL;

	try 
	{
		if(inCAPThread->mTimeConstraintSet)
		{
			inCAPThread->SetTimeConstraints(inCAPThread->mPeriod, inCAPThread->mComputation, inCAPThread->mConstraint, inCAPThread->mIsPreemptible);
		}
		else
		{
			inCAPThread->SetPriority(inCAPThread->mPriority, inCAPThread->mFixedPriority);
		}

		if(inCAPThread->mThreadRoutine != NULL)
		{
			theAnswer = inCAPThread->mThreadRoutine(inCAPThread->mThreadParameter);
		}
	}
	catch (...)
	{
		// what should be done here?
	}
	inCAPThread->mPThread = 0;
	return theAnswer;
}

UInt32 CAPThread::getScheduledPriority(pthread_t inThread, int inPriorityKind)
{
    thread_basic_info_data_t			threadInfo;
	policy_info_data_t					thePolicyInfo;
	unsigned int						count;

	if (inThread == NULL)
		return 0;
    
    // get basic info
    count = THREAD_BASIC_INFO_COUNT;
    thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (thread_info_t)&threadInfo, &count);
    
	switch (threadInfo.policy) {
		case POLICY_TIMESHARE:
			count = POLICY_TIMESHARE_INFO_COUNT;
			thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&(thePolicyInfo.ts), &count);
            if (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) {
                return thePolicyInfo.ts.cur_priority;
            }
            return thePolicyInfo.ts.base_priority;
            break;
            
        case POLICY_FIFO:
			count = POLICY_FIFO_INFO_COUNT;
			thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (thread_info_t)&(thePolicyInfo.fifo), &count);
            if ( (thePolicyInfo.fifo.depressed) && (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) ) {
                return thePolicyInfo.fifo.depress_priority;
            }
            return thePolicyInfo.fifo.base_priority;
            break;
            
		case POLICY_RR:
			count = POLICY_RR_INFO_COUNT;
			thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (thread_info_t)&(thePolicyInfo.rr), &count);
			if ( (thePolicyInfo.rr.depressed) && (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) ) {
                return thePolicyInfo.rr.depress_priority;
            }
            return thePolicyInfo.rr.base_priority;
            break;
	}
    
    return 0;
}

#elif TARGET_OS_WIN32

UInt32 WINAPI	CAPThread::Entry(CAPThread* inCAPThread)
{
	UInt32 theAnswer = 0;

	try 
	{
		if(inCAPThread->mTimeConstraintSet)
		{
			inCAPThread->SetTimeConstraints(inCAPThread->mPeriod, inCAPThread->mComputation, inCAPThread->mConstraint, inCAPThread->mIsPreemptible);
		}
		else
		{
			inCAPThread->SetPriority(inCAPThread->mPriority, inCAPThread->mFixedPriority);
		}

		if(inCAPThread->mThreadRoutine != NULL)
		{
			theAnswer = reinterpret_cast<UInt32>(inCAPThread->mThreadRoutine(inCAPThread->mThreadParameter));
		}
		inCAPThread->mThreadID = 0;
	}
	catch (...)
	{
		// what should be done here?
	}
	return theAnswer;
}

//	a definition of this function here for now
extern "C"
Boolean CompareAndSwap(UInt32 inOldValue, UInt32 inNewValue, UInt32* inOldValuePtr)
{
	return InterlockedCompareExchange((volatile LONG*)inOldValuePtr, inNewValue, inOldValue) == inOldValue;
}

#endif
