/****************************************************************************
**			TAU Portable Profiling Package			   **
**			http://www.cs.uoregon.edu/research/tau	           **
*****************************************************************************
**    Copyright 1997  						   	   **
**    Department of Computer and Information Science, University of Oregon **
**    Advanced Computing Laboratory, Los Alamos National Laboratory        **
****************************************************************************/
/***************************************************************************
**	File 		: WindowsThreadLayer.cpp				  **
**	Description 	: TAU Profiling Package RTS Layer definitions     **
**			  for supporting Microsoft Windows' Threads 			  **
**	Author		: Robert Ansell-Bell					  **
**	Contact		: bertie@cs.uoregon.edu 	  **
**	Flags		: Compile with				          **
**			  -DPROFILING_ON to enable profiling (ESSENTIAL)  **
**			  -DPROFILE_STATS for Std. Deviation of Excl Time **
**			  -DSGI_HW_COUNTERS for using SGI counters 	  **
**			  -DPROFILE_CALLS  for trace of each invocation   **
**			  -DSGI_TIMERS  for SGI fast nanosecs timer	  **
**			  -DTULIP_TIMERS for non-sgi Platform	 	  **
**			  -DPOOMA_STDSTL for using STD STL in POOMA src   **
**			  -DPOOMA_TFLOP for Intel Teraflop at SNL/NM 	  **
**			  -DPOOMA_KAI for KCC compiler 			  **
**			  -DDEBUG_PROF  for internal debugging messages   **
**                        -DPROFILE_CALLSTACK to enable callstack traces  **
**	Documentation	: See http://www.cs.uoregon.edu/research/tau      **
***************************************************************************/
#ifdef TAU_WINDOWS

//////////////////////////////////////////////////////////////////////
// Include Files 
//////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;
#include "Profile/Profiler.h"

/////////////////////////////////////////////////////////////////////////
// Member Function Definitions For class WindowsThreadLayer
// This allows us to get thread ids from 0..N-1 instead of long nos 
// as generated by built-in threadid calls. 
/////////////////////////////////////////////////////////////////////////

DWORD	WindowsThreadLayer::tauWindowsthreadId;
HANDLE	WindowsThreadLayer::tauThreadcountMutex; 
HANDLE	WindowsThreadLayer::tauDBMutex;
HANDLE	WindowsThreadLayer::tauEnvMutex;


int WindowsThreadLayer::tauThreadCount = 0;

////////////////////////////////////////////////////////////////////////
// RegisterThread() should be called before any profiling routines are
// invoked. This routine sets the thread id that is used by the code in
// FunctionInfo and Profiler classes. This should be the first routine a 
// thread should invoke from its wrapper. Note: main() thread shouldn't
// call this routine. 
////////////////////////////////////////////////////////////////////////
int WindowsThreadLayer::RegisterThread(void)
{

  int *threadId = new int;

  DWORD WaitCheck;

  WaitCheck = WaitForSingleObject(tauThreadcountMutex, INFINITE);
  if(WaitCheck == WAIT_OBJECT_0)
  {
	  tauThreadCount ++;
	  // A thread should call this routine exactly once. 
	  *threadId = tauThreadCount;
	  DEBUGPROFMSG("Thread id "<< tauThreadCount<< " Created! "<<endl;);

	  ReleaseMutex(tauThreadcountMutex);
	  if(!(TlsSetValue(tauWindowsthreadId, threadId)))
	  {
		  cout << "There was a problem setting the thread specific ID" << endl;
		  return 0;
	  }
  }
  else
  {
	  //The mutex was abandoned for some reason.  Signal it to the user
	  //and then exit.
	  cout << "The mutex was abandoned" << endl;
	  return 0;
  }

  return 0;
}


////////////////////////////////////////////////////////////////////////
// GetThreadId returns an id in the range 0..N-1 by looking at the 
// thread specific data. Since a getspecific has to be preceeded by a 
// setspecific (that all threads besides main do), we get a null for the
// main thread that lets us identify it as thread 0. It is the only 
// thread that doesn't do a WindowsThreadLayer::RegisterThread(). 
////////////////////////////////////////////////////////////////////////
int WindowsThreadLayer::GetThreadId(void) 
{

  static int initflag = WindowsThreadLayer::InitializeThreadData();
  // if its in here the first time, setup mutexes etc.

  int *id; 

  id = (int *) TlsGetValue(tauWindowsthreadId);
  
  if (id == NULL)
  {
    return 0; // main() thread 
  } 
  else
  { 
    return *id;
  }

}

////////////////////////////////////////////////////////////////////////
// InitializeThreadData is called before any thread operations are performed. 
// It sets the default values for static private data members of the 
// WindowsThreadLayer class.
////////////////////////////////////////////////////////////////////////
int WindowsThreadLayer::InitializeThreadData(void)
{
  
	//Initializing mutexes and thread specific data stuff.
  
	tauWindowsthreadId = TlsAlloc();
	tauThreadcountMutex = CreateMutex(NULL, FALSE, NULL);  
	//cout << "WindowsThreadLayer::Initialize() done! " <<endl;

  return 1;
}

////////////////////////////////////////////////////////////////////////

int WindowsThreadLayer::InitializeDBMutexData(void)
{
  // For locking functionDB 
  tauDBMutex = CreateMutex(NULL, FALSE, NULL);
  
  //cout <<" Initialized the functionDB Mutex data " <<endl;
  return 1;
}

////////////////////////////////////////////////////////////////////////
// LockDB locks the mutex protecting TheFunctionDB() global database of 
// functions. This is required to ensure that push_back() operation 
// performed on this is atomic (and in the case of tracing this is 
// followed by a GetFunctionID() ). This is used in 
// FunctionInfo::FunctionInfoInit().
////////////////////////////////////////////////////////////////////////
int WindowsThreadLayer::LockDB(void)
{
  static int initflag=InitializeDBMutexData();
  DWORD WaitCheckLDB;
  
  // Lock the functionDB mutex  
  WaitCheckLDB = WaitForSingleObject(tauDBMutex, INFINITE);
  if(WaitCheckLDB == WAIT_OBJECT_0)
	  return 1;

  else
  {
	  //The mutex was abandoned for some reason.  Signal it to the user
	  //and then exit.
	  cout << "The mutex was abandoned" << endl;
	  //Note to myself ... check to make sure that it is ok to return 0 here.
	  return 0;
  }

}

////////////////////////////////////////////////////////////////////////
// UnLockDB() unlocks the mutex tauDBMutex used by the above lock operation
////////////////////////////////////////////////////////////////////////
int WindowsThreadLayer::UnLockDB(void)
{
  // Unlock the functionDB mutex
  ReleaseMutex(tauDBMutex);
  return 1;
}

////////////////////////////////////////////////////////////////////////

int WindowsThreadLayer::InitializeEnvMutexData(void)
{
  // For locking functionEnv 
  tauEnvMutex = CreateMutex(NULL, FALSE, NULL);
  
  //cout <<" Initialized the functionEnv Mutex data " <<endl;
  return 1;
}

////////////////////////////////////////////////////////////////////////
// LockEnv locks the mutex protecting TheFunctionEnv() global database of 
// functions. This is required to ensure that push_back() operation 
// performed on this is atomic (and in the case of tracing this is 
// followed by a GetFunctionID() ). This is used in 
// FunctionInfo::FunctionInfoInit().
////////////////////////////////////////////////////////////////////////
int WindowsThreadLayer::LockEnv(void)
{
  static int initflag=InitializeEnvMutexData();
  DWORD WaitCheckLEnv;
  
  // Lock the functionEnv mutex  
  WaitCheckLEnv = WaitForSingleObject(tauEnvMutex, INFINITE);
  if(WaitCheckLEnv == WAIT_OBJECT_0)
	  return 1;

  else
  {
	  //The mutex was abandoned for some reason.  Signal it to the user
	  //and then exit.
	  cout << "The mutex was abandoned" << endl;
	  //Note to myself ... check to make sure that it is ok to return 0 here.
	  return 0;
  }

}

////////////////////////////////////////////////////////////////////////
// UnLockEnv() unlocks the mutex tauEnvMutex used by the above lock operation
////////////////////////////////////////////////////////////////////////
int WindowsThreadLayer::UnLockEnv(void)
{
  // Unlock the functionEnv mutex
  ReleaseMutex(tauEnvMutex);
  return 1;
}


#endif //TAU_WINDOWS
