/*****************************
File:       threadwrap.CPP
Language:   C++ (header)
Project:    H3DNetworkingUtils
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is H3DNetworkingUtils v1.0.

The Initial Developer of the Original Code is CSIRO.
Portions created by the Initial Developer are Copyright (C) 1009 CSIRO. All Rights Reserved.
Contributor(s):
    Chris Gunn  <Chris.Gunn@csiro.au> <ChrisJGunn@gmail.com>
***************************/

/* Distributed under a BSD type license. See the netwrap COPYRIGHT file */

#include <stdio.h>
#include <windows.h>

#include "H3DNetworkingUtils/fail.h"

#include "H3DNetworkingUtils/threadwrap.h"

/*  NOTES by Sam
 *  1.	Thread Joins and Synchronisation --
 *	Thread joinability is an important area of difference between
 *	Win32 threads and pthreads. All Win32 threads are joinable so
 *	long as you hold a real handle to the thread, and may be 
 *	joined by anyone holding a handle. pthreads are joinable by
 *	at most one thread, but may be detached so that they are not
 *	joinable at all.
 *	    A second area of difference is in how you reference a 
 *	thread. Win32 has an explicit handle model where kernel object
 *	references are incremented based on the number of handles to
 *	the object. In other words, a Win32 thread won't be cleaned
 *	up as long as its reference count is greater than zero. Win32 
 *	also provides "pseudo handles" which are pointers that don't
 *	affect the reference count. pthreads allows pointers to
 *	threads but makes no guarantees about their validity. Joining
 *	is the only general mechanism to sync on thread termination
 *	and so guarantee that a pointer is valid.
 *	    To resolve these issues this simple threading library 
 *	takes a lowest-common-denominator approach. Threads may either
 *	auto terminate or manual terminate. Auto terminating threads
 *	die silently and have all their resources cleaned up. Manually
 *	terminating threads may be stopped and may also be waited on.
 *	    In effect these threads are joinable by zero or one 
 *	processes. Calls to thread_create will return a Thread value
 *	only for manual termination threads (ie if the new thread is
 *	joinable). On Win32, manual termination will return a full 
 *	reference to be thread (incrementing the kernel object ref 
 *	count). On pthreads it will ensure the thread is left in the
 *	joinable state. Manual terminating threads will require one
 *	and only one call to either of thread_wait or thread_abort to
 *	ensure correct resource deallocation.
 *
 *  2.	Thread Priority and Scheduling Policy --
 *	Win32 effectively defines 5 priority levels for normal
 *	processes, and defines one system wide scheduling policy.
 *	pthreads only supports one scheduling policy for user level
 *	threads (SCHED_OTHER) and does not define how many levels 
 *	are available on each platform.
 *	    Again I have taken the lowest-common-denominator 
 *	approach. There are only five priority levels supported and
 *	no policies. On Win32 the priority levels map directly to
 *	the five process-relative levels. On pthreads the SCHED_OTHER
 *	policy is used and the five levels are mapped evenly across
 *	the priority range defined for that policy.
 */

static int winPriorityMap[] = {
    THREAD_PRIORITY_LOWEST,
    THREAD_PRIORITY_BELOW_NORMAL,
    THREAD_PRIORITY_NORMAL,
    THREAD_PRIORITY_ABOVE_NORMAL,
    THREAD_PRIORITY_HIGHEST
};
int * Thread::priorityMap = winPriorityMap;

Thread::Thread (ThreadFunction func, void * arg)
{
  FailNull(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg,
	0, (unsigned long *)&tid), "Thread::create");
}
   
Thread::Thread (ThreadFunction func, void * arg,
				int priority, int autoTerminate)
{
  FailNull(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg,
	0, (unsigned long *)&tid), "Thread::create");
  this->setPriority(priority);
  if (autoTerminate) {
	CloseHandle((HANDLE)tid);
	tid = NULL;
  }
}

Thread::~Thread ()
{
	if (tid != NULL)
		CloseHandle((HANDLE)tid);
}
    
void Thread::wait ()
{
	WaitForSingleObject((HANDLE)tid, INFINITE);
	CloseHandle((HANDLE)tid);
}

void Thread::cancel ()
{
  // Quietly return if thread already cancelled
  TerminateThread((HANDLE)this->tid, 0);
  CloseHandle((HANDLE)tid);
}

void Thread::exit ()
{
  ExitThread(0);
}

int Thread::getPriority ()
{
	int pr = GetThreadPriority((HANDLE)tid);
	switch (pr) {
	case THREAD_PRIORITY_LOWEST:		return PRI_0;
	case THREAD_PRIORITY_BELOW_NORMAL:	return PRI_1;
	case THREAD_PRIORITY_NORMAL:		return PRI_2;
	case THREAD_PRIORITY_ABOVE_NORMAL:	return PRI_3;
	case THREAD_PRIORITY_HIGHEST:		return PRI_4;
	default: return -1;
	};
}

void Thread::setPriority (int newPriority)
{
	if (newPriority < PRI_LOWEST || newPriority > PRI_HIGHEST)
		Fail("Thread::setPriority out of range");
	SetThreadPriority((HANDLE)tid, priorityMap[newPriority]);
}

void Thread::buildPriorityMap ()
{
	// NOP on Windows
}

void Thread::sleep (int seconds)
{
  // Win32 sleep is actually millisleep
  ::Sleep(seconds * 1000);
}

void Thread::millisleep (int millisecs)
{
  Sleep(millisecs);
}

