/*
 * misc.c
 *
 * Description:
 * This translation unit implements miscellaneous thread functions.
 */

#include "pthread.h"
#include "implement.h"

int
pthread_once(pthread_once_t *once_control,
	     void (*init_routine)(void))
{
  /* A flag, allocated per invocation, that indicates if the atomic
     test-and-set occured. */
  unsigned short flag = 0;

  if (once_control == NULL || init_routine == NULL)
    {
      return EINVAL;
    }

  /* An atomic test-and-set of the "once" flag. */
  pthread_mutex_lock(&once_control->lock);
  if (once_control->flag == 0)
    {
      flag = once_control->flag = 1;
    }
  pthread_mutex_unlock(&once_control->lock);

  if (flag)
    {
      /* Run the init routine. */
      init_routine();
    }
  
  return 0;
}

/*
 * Code contributed by John E. Bossom <JEB>.
 */

pthread_t
pthread_self (void)
     /*
      * ------------------------------------------------------
      * DOCPUBLIC
      *      This function returns a reference to the current running
      *      thread.
      *
      * PARAMETERS
      *      N/A
      *
      *
      * DESCRIPTION
      *      This function returns a reference to the current running
      *      thread.
      *
      * RESULTS
      *              pthread_t       reference to the current thread
      *
      * ------------------------------------------------------
      */
{
  pthread_t self = NULL;
  /*
   * need to ensure there always is a self
   */

  if ((self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey)) 
      == NULL)
    {
      /*
       * Need to create an implicit 'self' for the currently
       * executing thread.
       */
      self = (pthread_t) calloc (1, sizeof (*self));
      if (self != NULL)
	{

	  self->implicit = 1;
	  self->detachState = PTHREAD_CREATE_DETACHED;

	  self->thread = GetCurrentThreadId ();
	  self->threadH = GetCurrentThread ();
	}

      pthread_setspecific (_pthread_selfThreadKey, self);
    }

  return (self);

}				/* pthread_self */

int
pthread_equal (pthread_t t1, pthread_t t2)
     /*
      * ------------------------------------------------------
      * DOCPUBLIC
      *      This function returns zero if t1 and t2 are equal, else
      *      returns nonzero
      *
      * PARAMETERS
      *      t1,
      *      t2
      *              references to an instances of thread_t
      *
      *
      * DESCRIPTION
      *      This function returns zero if t1 and t2 are equal, else
      *      returns nonzero.
      *
      * RESULTS
      *              0               if t1 and t2 refer to the same thread,
      *              non-zero        t1 and t2 do not refer to the same thread
      *
      * ------------------------------------------------------
      */
{
  int result;

  result = !((t1 == t2) || (t1->thread == t2->thread));

  return (result);

}				/* pthread_equal */


static int
CancelableWait (HANDLE waitHandle, DWORD timeout)
     /*
      * -------------------------------------------------------------------
      * This provides an extra hook into the pthread_cancel
      * mechanism that will allow you to wait on a Windows handle and make it a
      * cancellation point. This function blocks until the given WIN32 handle is
      * signaled or pthread_cancel has been called. It is implemented using
      * WaitForMultipleObjects on 'waitHandle' and a manually reset WIN32
      * event used to implement pthread_cancel.
      * 
      * Given this hook it would be possible to implement more of the cancellation
      * points.
      * -------------------------------------------------------------------
      */
{
  int result;
  pthread_t self;
  HANDLE handles[2];
  DWORD nHandles = 1;
  DWORD status;

  handles[0] = waitHandle;

  if ((self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey)) 
      != NULL)
    {
      /*
       * Get cancelEvent handle
       */
      if (self->cancelState == PTHREAD_CANCEL_ENABLE)
        {

          if ((handles[1] = self->cancelEvent) != NULL)
            {
              nHandles++;
            }
        }
    }
  else
    {
      handles[1] = NULL;
    }

  status = WaitForMultipleObjects (
                                    nHandles,
                                    handles,
                                    FALSE,
                                    timeout);


  if (status == WAIT_FAILED)
    {
      result = EINVAL;

    }
  else if (status == WAIT_TIMEOUT)
    {
      result = ETIMEDOUT;
    }
  else if (status == WAIT_ABANDONED_0)
    {
      result = EINVAL;
    }
  else
    {
      /*
       * Either got the mutex or the cancel event
       * was signaled
       */
      switch (status - WAIT_OBJECT_0)
        {

        case 0:
          /*
           * Got the mutex
           */
          result = 0;
          break;

        case 1:
          /*
           * Got cancel request
           */
          ResetEvent (handles[1]);

          if (self != NULL && !self->implicit)
            {
              /*
               * Thread started with pthread_create
               */

#ifdef _MSC_VER

              DWORD exceptionInformation[3];

              exceptionInformation[0] = (DWORD) (0);
              exceptionInformation[1] = (DWORD) (0);

              RaiseException (
                               EXCEPTION_PTHREAD_SERVICES,
                               0,
                               3,
                               exceptionInformation);

#else /* _MSC_VER */

#ifdef __cplusplus

	      throw pthread_exception;

#endif /* __cplusplus */

#endif /* _MSC_VER */
            }

	  /* Should never get to here. */
	  result = EINVAL;
          break;

        default:
          result = EINVAL;
          break;
        }
    }

  return (result);

}                               /* pthreadCancelableWait */

int
pthreadCancelableWait (HANDLE waitHandle)
{
  return (CancelableWait(waitHandle, INFINITE));
}

int
pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout)
{
  return (CancelableWait(waitHandle, timeout));
}

/* </JEB> */