/**********************************************************************
 * Copyright (c) 2005, 2006 Scapa Technologies Limited and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: thread.c,v 1.2 2006/10/26 22:40:21 kcallaghan Exp $
 * 
 * Contributors: 
 * Scapa Technologies Limited - Initial API and implementation
 **********************************************************************/

/***********************************************************************
 *
 * File: Threads.c
 * Overview: The following code provides a wrapper to the Win32 
 *                                API routines for threads.Here we have Thread 
 *                                struct, defined in thread.h, allocated and partially
 *                                initialised by the caller.
 ***********************************************************************/

#include "system.h"

#ifdef PTHREAD_INIT_NEEDED
void pthread_init(void);
#endif

#ifdef WIN32
static unsigned WINAPI ThreadWrapper(void *);
#endif
#if defined(LINUX) | defined(SOLARIS) || defined(AIX) || defined(USS) || defined(HPUX) || defined(LINUX_S390) || defined(SOLARIS_I386)
static void *ThreadWrapper(void *);
#endif

/***********************************************************************
 * Function: XThreadStartup
 * Purpose: This function calls any necessary thread startup procedures 
 * Parameters: none
 * Returns: int SUCCESS
 ***********************************************************************/

int XThreadStartup()
{
#ifdef PTHREAD_INIT_NEEDED 
  pthread_init();
#endif
  return SUCCESS;
}

int XThreadMakeAbortable()
{
#if defined(LINUX) | defined(SOLARIS) || defined(AIX) || defined(USS) || defined(HPUX) || defined(LINUX_S390) || defined(SOLARIS_I386)
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
#endif
  return SUCCESS;
}

/***********************************************************************
 * Function: XThreadCreate
 * Purpose: This function creates a thread running the routine
 *                               provided as an argument.
 * Parameters: ThreadRoutine *, a pointer to a thread start routine;
 *                                        void *, a pointer to an argument to the routine and
 *                                        Thread *, a pointer to a Thread struct.
 * Returns: int, THREAD_SUCCESS if successful creation,
 *                              THREAD_FAILURE otherwise.
 ***********************************************************************/
/* Here ThreadRoutine is of type THREAD_START_ROUTINE 
 * e.g. DWORD WINAPI ThreadStartFunc(LPVOID lpThreadParameter). */

int XThreadCreate(XThreadFunction *function, void *argument, XThread *thread)
{
#ifdef WIN32
  thread->function = function;
  thread->argument = argument;

  /* 
   * use this if the threads make c runtime library calls 
   */

  thread->handle = (HANDLE)_beginthreadex(NULL,  XTHREAD_STACKSZ, ThreadWrapper, (void *)thread, 0, &(thread->id));

  /* use this if the threads only make call to Win 32 API 
   * thread->handle = CreateThread(NULL, 0, function, argument, thread->flags, &(thread->id)); 
   */

  if (! thread->handle)
  {
    return XTHREAD_ERROR_CREATE;
  }
#endif
#if defined(LINUX) | defined(SOLARIS) || defined(USS) || defined(HPUX) || defined(AIX) || defined(LINUX_S390) || defined(SOLARIS_I386) 
//  thread->function = function;
//  thread->argument = argument;
//
//  if (pthread_create(&thread->pthread, NULL, ThreadWrapper, (void *)thread) != 0)
//  {
//    return FAILURE;
//  }
//#endif
//#if defined(AIX)
  pthread_attr_t attr;
  pthread_attr_init(&attr);
#if defined(LINUX) | defined(SOLARIS) || defined(USS) || defined(HPUX) || defined(LINUX_S390) || defined(SOLARIS_I386)
  pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
#endif  
#if defined(AIX)
  pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_UNDETACHED);
#endif

  thread->function = function;
  thread->argument = argument;

  if (pthread_create(&thread->pthread, &attr, ThreadWrapper, (void *)thread) != 0)
  {
    pthread_attr_destroy(&attr);
    return FAILURE;
  }

  pthread_attr_destroy(&attr);
#endif

  return SUCCESS;
}

/***********************************************************************
 * Function: XThreadExit
 * Purpose: The routine causes a thread to exit normally. The 
 *                               thread will not terminate until all references to the 
 *                               kernel thread object are closed.
 * Parameters: unsigned int (=UINT), the exit code for the thread.
 * Returns: void, this function does not return.
 ***********************************************************************/
void XThreadExit(unsigned int code)
{
#ifdef WIN32
  /* has same effect as returning from entry procedure, both call ExitThread(). */
  _endthreadex(code);
  /*
    ExitThread(code); */
#endif
#if defined(LINUX) | defined(SOLARIS) || defined(AIX) || defined(USS) || defined(HPUX) || defined(LINUX_S390) || defined(SOLARIS_I386)
  pthread_exit((void*)&code);
#endif
}

/***********************************************************************
 * Function: XThreadWait
 * Purpose:  The routine causes the calling thread to block until
 *                                the specified thread has terminated. The terminated
 *                                thread's Thread struct is invalidated.
 * Parameters: Thread *, pointer to Thread struct; unisigned int *
 *                                        (=LPDWORD), pointer to exit code of thread.
 * Returns: int, THREAD_SUCCESS if specified thread terminates 
 *                              with correct exit code, THREAD_FAILURE otherwise.
 ***********************************************************************/
/* Thread kernel objects are signalled on termination */
/* is LPDWORD the same as unsigned int* ??? */
int XThreadWait(XThread *thread, unsigned int *code)
{
#ifdef WIN32
  BOOL terminated;
  DWORD result;

  result = WaitForSingleObject(thread->handle, INFINITE); /* unbounded wait */
  
  if (result == WAIT_OBJECT_0)      /* signalled (=thread terminated) */
  {        
    terminated = GetExitCodeThread(thread->handle, code); /* find out the exit code of thread */
    /* terminated = TRUE as signalled implies */
    (void) CloseHandle(thread->handle);
    ZeroMemory(thread, sizeof(XThread)); /* invalidate Thread struct */
  }
  else
  {
    return XTHREAD_ERROR_WAIT;
  }
#endif
#if defined(LINUX) | defined(SOLARIS) || defined(AIX) || defined(USS) || defined(HPUX) || defined(LINUX_S390) || defined(SOLARIS_I386)
  int okay;

  if ((okay = pthread_join(thread->pthread, (void **)&code)) != 0)
  {
    return FAILURE;
  }
#endif
                
  return SUCCESS;
}

/***********************************************************************
 * Function: XThreadAbort
 * Purpose: Forcefully terminates a specified thread. The thread's
 *                               Thread struct is invalidated.
 * Parameters: Thread *, pointer to Thread struct.
 * Returns: int, THREAD_SUCCESS if successful termination,
 *                              THREAD_FAILURE otherwise.
 ***********************************************************************/
int XThreadAbort(XThread *thread)
{
#ifdef WIN32
  BOOL terminated;
  DWORD exitcode = 0;

  terminated = TerminateThread(thread->handle, exitcode);
  
  if (terminated)
  {
    (void) CloseHandle(thread->handle);
    ZeroMemory(thread, sizeof(XThread)); /* invalidate Thread struct */
  }
  else
  {
    return XTHREAD_ERROR_ABORT;
  }
#endif
#if defined(LINUX) | defined(SOLARIS) || defined(USS) || defined(HPUX) || defined(AIX) || defined(LINUX_S390) || defined(SOLARIS_I386)

  if (pthread_cancel(thread->pthread) != 0)
  {
    return FAILURE;
  }

#endif

  return SUCCESS;
}

/***********************************************************************
 * Function: XThreadGetIdentifier
 * Purpose: gets the thread identifier of the calling thread
 * Parameters: 
 * Returns: int, PROCESS_SUCCESS if set is successful,
 *                              PROCESS_FAILURE otherwise.
 ***********************************************************************/
int XThreadGetIdentifier(int *tid)
{
#ifdef WIN32
  *tid = GetCurrentThreadId();
#endif
#if defined(LINUX) | defined(SOLARIS) || defined(AIX) || defined(USS) || defined(HPUX) || defined(LINUX_S390) || defined(SOLARIS_I386)
  *tid = (int)getpid();
#endif

  return SUCCESS;
}

#ifdef WIN32
/***********************************************************************
 * Function: ThreadWrapper
 * Purpose: wrapper around our thread function to abstract windows
 *          _beginthreadex/_endthreadex threads - yuk yuk yuk ;-)
 * Parameters: void *argument which is (void *) cast of the thread
 * Returns: unsigned WINAPI SUCCESS if it returns at all ...
 ***********************************************************************/

static unsigned WINAPI ThreadWrapper(void *argument)
{
  ((XThread *)argument)->function(((XThread *)argument)->argument);
        
  _endthreadex(EXIT_SUCCESS);
  return EXIT_SUCCESS;
}
#endif
#if defined(LINUX) || defined(SOLARIS) || defined(AIX) || defined(USS) || defined(HPUX) || defined(LINUX_S390) || defined(SOLARIS_I386)
/***********************************************************************
 * Function: ThreadWrapper
 * Purpose: wrapper around our thread function to abstract specifics
 * Parameters: void *argument which is (void *) cast of the thread
 * Returns: (void *)NULL, if at all ...
 ***********************************************************************/

static void *ThreadWrapper(void *argument)
{
  (*((XThread *)argument)->function)(((XThread *)argument)->argument);
  
  return NULL;
}
#endif

/***********************************************************************
 * EOF
 ***********************************************************************/

