#include "stdafx.h"
#include "Threads.h"

// Code borrowed from SDL.

//! Synchronization functions which can time out return this value if they time out.
#define MUTEX_TIMEDOUT	1

//! This is the timeout value which corresponds to never time out
#define MUTEX_MAXWAIT	(~(udword)0)

// MUTEX

// Creates a mutex
IceMutex* CreateMutex()
{
	// Allocate mutex memory
	IceMutex* Mutex = new IceMutex;
	if(!Mutex)	return null;

	// Create the mutex, with initial value signaled
	Mutex->id = ::CreateMutex(null, FALSE, null);
	if(!Mutex->id)
	{
		SetIceError("Couldn't create mutex", null);
		DELETESINGLE(Mutex);
	}
	return Mutex;
}

// Frees the mutex
void DestroyMutex(IceMutex* mutex)
{
	if(mutex)
	{
		if(mutex->id)
		{
			CloseHandle(mutex->id);
			mutex->id = 0;
		}
		DELETESINGLE(mutex);
	}
}

// Locks the mutex
int LockMutex(IceMutex* mutex)
{
	if(!mutex)	return -1;

	if(WaitForSingleObject(mutex->id, INFINITE) == WAIT_FAILED)
	{
		SetIceError("Couldn't wait on mutex", null);
		return -1;
	}
	return 0;
}

// Unlocks the mutex
int UnlockMutex(IceMutex* mutex)
{
	if(!mutex)	return -1;

	if(ReleaseMutex(mutex->id) == FALSE)
	{
		SetIceError("Couldn't release mutex", null);
		return -1;
	}
	return 0;
}

// SEMAPHORE

// Creates a semaphore
IceSem* CreateSemaphore(udword initial_value)
{
	// Allocate sem memory
	IceSem* sem = new IceSem;
	if(sem)
	{
		// Create the semaphore, with max value 32K
		sem->id = ::CreateSemaphore(null, initial_value, 32*1024, null);
		sem->count = initial_value;
		if(!sem->id)
		{
			SetIceError("Couldn't create semaphore", null);
			DELETESINGLE(sem);
		}
	}
	else SetIceError("Out of memory!", EC_OUT_OF_MEMORY);

	return sem;
}

// Frees the semaphore
void DestroySemaphore(IceSem* sem)
{
	if(sem)
	{
		if(sem->id)
		{
			CloseHandle(sem->id);
			sem->id = 0;
		}
		DELETESINGLE(sem);
	}
}

int SemWaitTimeout(IceSem* sem, udword timeout)
{
	int retval;
	DWORD dwMilliseconds;

	if(!sem)
	{
		SetIceError("Passed a NULL sem", null);
		return -1;
	}

	if(timeout==MUTEX_MAXWAIT)
	{
		dwMilliseconds = INFINITE;
	}
	else
	{
		dwMilliseconds = (DWORD)timeout;
	}

	switch (WaitForSingleObject(sem->id, dwMilliseconds))
	{
	    case WAIT_OBJECT_0:
		--sem->count;
		retval = 0;
		break;
	    case WAIT_TIMEOUT:
		retval = MUTEX_TIMEDOUT;
		break;
	    default:
		SetIceError("WaitForSingleObject() failed", null);
		retval = -1;
		break;
	}
	return retval;
}

int SemTryWait(IceSem* sem)
{
	return SemWaitTimeout(sem, 0);
}

int SemWait(IceSem* sem)
{
	return SemWaitTimeout(sem, MUTEX_MAXWAIT);
}

// Returns the current count of the semaphore
udword SemValue(IceSem* sem)
{
	if(!sem)
	{
		SetIceError("Passed a NULL sem", null);
		return 0;
	}
	return sem->count;
}

int SemPost(IceSem* sem)
{
	if(!sem)
	{
		SetIceError("Passed a NULL sem", null);
		return -1;
	}

	/* Increase the counter in the first place, because
	 * after a successful release the semaphore may
	 * immediately get destroyed by another thread which
	 * is waiting for this semaphore.
	 */
	++sem->count;
	if(ReleaseSemaphore(sem->id, 1, null)==FALSE)
	{
		--sem->count;	// restore
		SetIceError("ReleaseSemaphore() failed", null);
		return -1;
	}
	return 0;
}

void RunThread(void* data);

static DWORD WINAPI gRunThread(LPVOID data)
{
	RunThread(data);
	return(0);
}

int SYS_CreateThread(IceThread* thread, void* args)
{
	DWORD  threadnum;

//	thread->handle = ::CreateThread(null, 64*1024, gRunThread, args, STACK_SIZE_PARAM_IS_A_RESERVATION, &threadnum);
	thread->handle = ::CreateThread(null, 0, gRunThread, args, 0, &threadnum);
	if(!thread->handle)
	{
		SetIceError("Not enough resources to create thread", null);
		return -1;
	}
	return 0;
}

void SYS_SetupThread()
{
	return;
}

udword ThreadID()
{
	return((udword)GetCurrentThreadId());
}

void SYS_WaitThread(IceThread *thread)
{
	WaitForSingleObject(thread->handle, INFINITE);
	CloseHandle(thread->handle);
}

/* WARNING: This function is really a last resort.
 * Threads should be signaled and then exit by themselves.
 * TerminateThread() doesn't perform stack and DLL cleanup.
 */
void SYS_KillThread(IceThread *thread)
{
	TerminateThread(thread->handle, FALSE);
}

// System independent thread management routines

#define ARRAY_CHUNKSIZE	32
/* The array of threads currently active in the application
   (except the main thread)
   The manipulation of an array here is safer than using a linked list.
*/
static int gMaxThreads = 0;
static int gNumThreads = 0;
static IceThread** gThreads = null;
static IceMutex* gThreadLock = null;
int _creating_thread_lock = 0;

int ThreadsInit()
{
	int retval = 0;
	/* Set the thread lock creation flag so that we can reuse an
	   existing lock on the system - since this mutex never gets
	   destroyed (see ThreadsQuit()), we want to reuse it.
	*/
	_creating_thread_lock = 1;
	gThreadLock = CreateMutex();
	_creating_thread_lock = 0;
	if(!gThreadLock)
	{
		retval = -1;
	}
	return retval;
}

/* This should never be called...
   If this is called by Quit(), we don't know whether or not we should
   clean up threads here.  If any threads are still running after this call,
   they will no longer have access to any per-thread data.
 */
void ThreadsQuit()
{
	IceMutex* mutex = gThreadLock;
	gThreadLock = null;
	if(mutex)
	{
		DestroyMutex(mutex);
	}
}

// Routines for manipulating the thread list
static void AddThread(IceThread* thread)
{
	/* WARNING:
	   If the very first threads are created simultaneously, then
	   there could be a race condition causing memory corruption.
	   In practice, this isn't a problem because by definition there
	   is only one thread running the first time this is called.
	*/
	if(!gThreadLock)
	{
		if(ThreadsInit() < 0)
		{
			return;
		}
	}
	LockMutex(gThreadLock);

	/* Expand the list of threads, if necessary */
#ifdef DEBUG_THREADS
	printf("Adding thread (%d already - %d max)\n", gNumThreads, gMaxThreads);
#endif
	if(gNumThreads == gMaxThreads)
	{
		IceThread** threads = new IceThread*[gMaxThreads+ARRAY_CHUNKSIZE];
		if(!threads)
		{
			SetIceError("Out of memory!", EC_OUT_OF_MEMORY);

			goto done;
		}
		memcpy(threads, gThreads, gNumThreads*(sizeof *threads));
		gMaxThreads += ARRAY_CHUNKSIZE;
		DELETEARRAY(gThreads);
		gThreads = threads;
	}
	gThreads[gNumThreads++] = thread;
done:
	UnlockMutex(gThreadLock);
}

static void DelThread(IceThread* thread)
{
	int i;

	if(gThreadLock)
	{
		LockMutex(gThreadLock);
		for(i=0; i<gNumThreads; ++i)
		{
			if(thread == gThreads[i])
			{
				break;
			}
		}
		if(i < gNumThreads)
		{
			if(--gNumThreads > 0)
			{
				while(i < gNumThreads)
				{
					gThreads[i] = gThreads[i+1];
					++i;
				}
			}
			else
			{
				gMaxThreads = 0;
				DELETEARRAY(gThreads);
			}
#ifdef DEBUG_THREADS
			printf("Deleting thread (%d left - %d max)\n", gNumThreads, gMaxThreads);
#endif
		}
		UnlockMutex(gThreadLock);
	}
}

// The default (non-thread-safe) global error variable
static ThreadError gGlobalError;

// Routine to get the thread-specific error variable
ThreadError* GetErrBuf()
{
	ThreadError* errbuf = &gGlobalError;
	if(gThreads)
	{
		int i;
		udword this_thread;

		this_thread = ThreadID();
		LockMutex(gThreadLock);
		for(i=0; i<gNumThreads; ++i)
		{
			if(this_thread == gThreads[i]->threadid)
			{
				errbuf = &gThreads[i]->errbuf;
				break;
			}
		}
		UnlockMutex(gThreadLock);
	}
	return errbuf;
}

// Arguments and callback to setup and run the user thread function
	typedef struct
	{
		int			(*func)(void *);
		void*		data;
		IceThread*	info;
		IceSem*		wait;
	} thread_args;

void RunThread(void* data)
{
	thread_args *args;
	int (*userfunc)(void *);
	void *userdata;
	int *statusloc;

	/* Perform any system-dependent setup
	   - this function cannot fail, and cannot use SetError()
	 */
	SYS_SetupThread();

	/* Get the thread id */
	args = (thread_args *)data;
	args->info->threadid = ThreadID();

	/* Figure out what function to run */
	userfunc = args->func;
	userdata = args->data;
	statusloc = &args->info->status;

	/* Wake up the parent thread */
	SemPost(args->wait);

	/* Run the function */
	*statusloc = userfunc(userdata);
}

IceThread* CreateThread(int (*fn)(void *), void *data)
{
	int ret;

	// Allocate memory for the thread info structure
	IceThread* thread = new IceThread;
	if(!thread)
	{
		SetIceError("Out of memory!", EC_OUT_OF_MEMORY);
		return null;
	}
	ZeroMemory(thread, (sizeof *thread));
	thread->status = -1;

	// Set up the arguments for the thread
	thread_args* args = new thread_args;
	if(!args)
	{
		SetIceError("Out of memory!", EC_OUT_OF_MEMORY);
		DELETESINGLE(thread);
		return null;
	}
	args->func = fn;
	args->data = data;
	args->info = thread;
	args->wait = CreateSemaphore(0);
	if(!args->wait)
	{
		DELETESINGLE(thread);
		DELETESINGLE(args);
		return null;
	}

	// Add the thread to the list of available threads
	AddThread(thread);

	// Create the thread and go!
	ret = SYS_CreateThread(thread, args);
	if( ret >= 0)
	{
		// Wait for the thread function to use arguments
		SemWait(args->wait);
	}
	else
	{
		// Oops, failed.  Gotta free everything
		DelThread(thread);
		DELETESINGLE(thread);
	}
	DestroySemaphore(args->wait);
	DELETESINGLE(args);

	// Everything is running now
	return thread;
}

void WaitThread(IceThread* thread, int* status)
{
	if(thread)
	{
		SYS_WaitThread(thread);
		if(status)
		{
			*status = thread->status;
		}
		DelThread(thread);
		DELETESINGLE(thread);
	}
}

udword GetThreadID(IceThread* thread)
{
	udword id;

	if(thread)	id = thread->threadid;
	else		id = ThreadID();

	return(id);
}

void KillThread(IceThread* thread)
{
	if(thread)
	{
		SYS_KillThread(thread);
		WaitThread(thread, null);
	}
}





void TestRadix();
void TestRadix2();
void TestIntroSort();
void TestStdSort();
void InitSortValues();
void ReleaseSortValues();

int _tmain(int argc, _TCHAR* argv[])
{
	SYSTEM_INFO SI;
	GetSystemInfo(&SI);
	printf("%d processors found\n", SI.dwNumberOfProcessors);

	InitSortValues();
	TestRadix();
	TestRadix2();
	TestIntroSort();
	TestStdSort();
	ReleaseSortValues();
	return 0;
}

