#include "pch.hpp"
//===========================================================================//
// File:	 Threads.cpp													 //
// Contents: global variables												 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 04/06/00   AG    Worker thread API										 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//


extern volatile int ProcessingError;

//
// Structure for each of the worker threads
//
typedef struct _gosThreadInfo
{
	_gosThreadInfo*	pNext;

	DWORD Magic;
	
	void(__stdcall*ThreadRoutine)(void*);

	int ThreadRunning;							// 2=Running, 1=Finished, 0=App informed
	bool* AppThreadFinished;

	HANDLE TriggerEvent;
	HANDLE KillEvent;
	HANDLE ThreadHandle;
	unsigned int ThreadID;
	
	void* context;

} gosThreadInfo;

//
// List of all threads in the system
//
gosThreadInfo*	ListofThreads=0;



//
// Check to see how many threads have finished
//
void CheckThreads()
{
	LOG_BLOCK("Gos::CheckThreads");
	gosThreadInfo* pThis=ListofThreads;

	while( pThis )
	{
		if( pThis->ThreadRunning==1 )
		{
			pThis->ThreadRunning=0;
			*pThis->AppThreadFinished=1;
		}
		pThis=pThis->pNext;
	}
}


//
// At close down check all threads are closed
//
void DestroyThreads()
{
	if( !ProcessingError )
	{
		gosASSERT( ListofThreads==0 );			// Any threads left running?
	}
}





//
// All worker threads loop in here until triggered
//
unsigned int __stdcall WorkerThread( void* context )
{
	gosThreadInfo* pThis=(gosThreadInfo*)context;

	HANDLE EventHandles[2];
	EventHandles[0]=pThis->KillEvent;
	EventHandles[1]=pThis->TriggerEvent;

    while( WaitForMultipleObjects( 2, EventHandles, FALSE, INFINITE) == WAIT_OBJECT_0+1 )		// Kill event will always be checked first
	{
		pThis->ThreadRoutine( pThis->context );
		pThis->ThreadRunning=1;
	}

	_endthreadex(0);
	return 0;
}


extern void gosSetThreadName(DWORD dwThreadID, char *szThreadName);


//
// Creates a 'worker thread' - this API returns a handle to the thread.
//
// You must pass the address of the worker thread routine to this function.
//
DWORD __stdcall gos_CreateThread( void (__stdcall *ThreadRoutine)(void*) )
{
	gosASSERT( ThreadRoutine );

	gosThreadInfo* pThis=(gosThreadInfo*)gos_Malloc( sizeof(gosThreadInfo) );

	pThis->Magic=99882345;
	pThis->ThreadRoutine=ThreadRoutine;
	pThis->ThreadRunning=0;
	pThis->TriggerEvent=CreateEvent( NULL, false, false, NULL );
	pThis->KillEvent=CreateEvent( NULL, false, false, NULL );
	
	pThis->ThreadHandle=(HANDLE)_beginthreadex( 0, 0, WorkerThread, pThis, 0, &pThis->ThreadID );
//
// Name thread
//
	gosSetThreadName( pThis->ThreadID, "Worker" );
//
// Add to list of current threads
//
	pThis->pNext=ListofThreads;
	ListofThreads=pThis;
//
// Return thread handle
//
	return (DWORD)pThis;
}


//
// Set the thread priority
//
void __stdcall gos_SetThreadPriority( DWORD ThreadHandle, gosThreadPriority Priority )
{
	volatile gosThreadInfo* pThis=(gosThreadInfo*)ThreadHandle;
	gosASSERT( pThis->Magic==99882345 );

	int Pri=THREAD_PRIORITY_NORMAL;

	switch( Priority )
	{
		case ThreadPri_Lowest:
			Pri=THREAD_PRIORITY_LOWEST;
			break;
		case ThreadPri_BelowNormal:
			Pri=THREAD_PRIORITY_BELOW_NORMAL;
			break;
		case ThreadPri_AboveNormal:
			Pri=THREAD_PRIORITY_ABOVE_NORMAL;
			break;
		case ThreadPri_Highest:
			Pri=THREAD_PRIORITY_HIGHEST;
			break;
	}
	
	SetThreadPriority( pThis->ThreadHandle, Pri );
}


//
// Waits until the thread is not executing anymore and then deletes the thread
//
void __stdcall gos_DeleteThread( DWORD ThreadHandle )
{
	volatile gosThreadInfo* pThis=(gosThreadInfo*)ThreadHandle;
	gosASSERT( pThis->Magic==99882345 );
//
// Kill, wait and close thread
//
    SetEvent( pThis->KillEvent );
    WaitForSingleObject( pThis->ThreadHandle, INFINITE );
	CloseHandle( pThis->ThreadHandle );
	CloseHandle( pThis->TriggerEvent );
	CloseHandle( pThis->KillEvent );
//
// Remove from list of threads
//
	if( ListofThreads==pThis )
	{
		ListofThreads=pThis->pNext;
	}
	else
	{
		gosThreadInfo* pTemp=ListofThreads;
		while( pTemp->pNext!=pThis )
		{
			pTemp=pTemp->pNext;
		}
		pTemp->pNext=pThis->pNext;
	}
	gos_Free( (void*)pThis );
}





//
// Starts the created thread executing
//
// 'ThreadFinished' will be set when the thread has finished executing
// 'Context' will be passed to the thread function
//
void __stdcall gos_TriggerThread( DWORD ThreadHandle, bool* ThreadFinished, void* Context )
{
	gosThreadInfo* pThis=(gosThreadInfo*)ThreadHandle;
//
// Make sure not already running
//
	gosASSERT( pThis->ThreadRunning==0 );
//
// Record parameter to pass to thread
//
	pThis->context=Context;
	pThis->AppThreadFinished=ThreadFinished;
//
// Mark as running and trigger thread
//
	*pThis->AppThreadFinished=0;
	pThis->ThreadRunning=2;
    SetEvent( pThis->TriggerEvent );
}









