// giltest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdlib.h>
#include <process.h>

#include <windows.h>
#include <assert.h>
#include <vector>

#define EVENT

#ifdef EVENT
LONG gilcount;
HANDLE gil;
void CreateGIL(){
	gilcount = 0;
	gil = CreateEvent(0, FALSE, FALSE, 0) ;
}

void ClaimGIL() {
	if (InterlockedIncrement(&gilcount) > 1) {
		DWORD r = WaitForSingleObject(gil, INFINITE);
		assert(r == WAIT_OBJECT_0);
	}
}

void ReleaseGIL() {
	if (InterlockedDecrement(&gilcount) > 0) {
		BOOL ok = SetEvent(gil);
		assert(ok);
	}
}
#endif 



#ifdef MUTEX
HANDLE gil = 0;
void CreateGIL(){
	gil = CreateMutex(0, FALSE, 0);
}

void ClaimGIL() {
	DWORD r = WaitForSingleObject(gil, INFINITE);
	assert(r == WAIT_OBJECT_0);
}

void ReleaseGIL() {
	BOOL ok = ReleaseMutex(gil);
	assert(ok);
}
#endif

#ifdef SEMAPHORE
HANDLE gil = 0;
void CreateGIL(){
	gil = CreateSemaphore(0,1, 1, 0);
}

void ClaimGIL() {
	DWORD r = WaitForSingleObject(gil, INFINITE);
	assert(r == WAIT_OBJECT_0);
}

void ReleaseGIL() {
	BOOL ok = ReleaseSemaphore(gil, 1, 0);
	assert(ok);
}
#endif

#ifdef CRIT
CRITICAL_SECTION gil;
void CreateGIL(){
	InitializeCriticalSection(&gil);
}

void ClaimGIL() {
	EnterCriticalSection(&gil);
}

void ReleaseGIL() {
	LeaveCriticalSection(&gil);
}
#endif


void FlashGIL() {
	DWORD id = GetCurrentThreadId();
	printf("thread %10d flashing GIL\n", id);
	ReleaseGIL();
	ClaimGIL();
	printf("thread %10d reclaims GIL\n", id);
}



void DoWork(int rounds)
{
	DWORD id = GetCurrentThreadId();
	while (rounds--) {
		int sum = 0;
		int n = rand();
		n += RAND_MAX;
		printf("thread %10d working %d units\n", id, n);
		for (int i=0; i<n; ++i)
			sum += i;
		printf("thread %10d worked %d units: %d\n", id, n, sum);
		FlashGIL();
	}
}
		
unsigned __stdcall threadfunc(void *arg) {
	int rounds = (int)arg;
	DWORD id = GetCurrentThreadId();
	printf("thread %10d starting\n", id);
	ClaimGIL();
	printf("thread %10d got GIL\n", id);
	DoWork(rounds);
	ReleaseGIL();
	return 0;
}


HANDLE StartThread(int rounds)
{
	uintptr_t h = _beginthreadex(0, 0, threadfunc, (void*)rounds, 0, 0);
	assert(h != -1L);
	return (HANDLE) h;
}


int _tmain(int argc, _TCHAR* argv[])
{
	int nThreads = 2;
	int nRounds = 10;
	CreateGIL();
	std::vector<HANDLE> threads;
	for (int i = 0; i<nThreads; i++)
		threads.push_back(StartThread(nRounds));

	DWORD r = WaitForMultipleObjects(threads.size(), &threads[0], TRUE, INFINITE);
	getc(stdin);
}

