/*
 * private.c
 *
 * Description:
 * This translation unit implements routines which are private to
 * the implementation and may be used throughout it.
 */

#include <errno.h>

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

/* Thread ID management.
   ---------------------

   We started by simply mapping the Win32 thread handle directly to
   pthread_t. However, in order to process pthread_join()'s, we need
   to be able to keep our POSIX thread ID (pthread_t) around after the
   Win32 thread has terminated. Win32 may reuse the Win32 handle during that
   time, which will conflict.

   The pthread_t value is now actually the pointer to a thread struct:

   typedef struct _pthread * pthread_t;

   which amongst other things stores the Win32 thread handle:

   struct _pthread {
     HANDLE  win32handle;
     int     ptstatus;
     ...
   };

   So now whereever we need to use the Win32 handle it can be accessed
   as:

   pthread_t T = pthread_this();
   HANDLE    H;

   H = T->win32handle;

   // or (which is NOT preferred, let the compiler optimise to this).

   H = (HANDLE) *T;


   POSIX Threads Table
   -------------------

   Having the thread ID as a pointer to the thread struct itself
   avoids the need to search the threads table in all but the initial
   occasion where we create the thread.

   Initially we used a hash function to select a free thread struct
   from the table, possibly needing a walk through the table if the
   hash collided with an already in-use thread.

   The scheme used now is more efficient and is done as follows:

   We use two tables and two counters:

   struct _pthread  _pthread_virgins[PTHREAD_THREADS_MAX];
   pthread_t        _pthread_reuse[PTHREAD_THREADS_MAX];

   int       _pthread_virgin_next = 0;
   int       _pthread_reuse_top = -1;

   The counter _pthread_virgin_next is an index into _pthread_virgins[],
   which can be thought of as a list, and _pthread_reuse_top is an
   index into _pthread_reuse[], which can be thought of as a LIFO stack.

   Once taken from _pthread_virgins[], used and freed threads are only
   ever pushed back onto _pthread_reuse[].

   The code for choosing a new (pthread_t) thread from the pool of
   free thread structs looks like:

   if (_pthread_reuse_top >= 0)
     {
       new_thread = _pthread_reuse[_pthread_reuse_top--];
     }
   else
     {
       if (_pthread_virgin_next < PTHREAD_THREADS_MAX)
	 {
	   new_thread = _pthread_virgin[_pthread_virgin_next++];
	 }
       else
	 {
	   return EAGAIN;
	 }
     }


   The code to free a thread is:

   _pthread_reuse[++_pthread_reuse_top] = thread;


   We still need a means for pthread_self() to return its own thread
   ID.

   We use the Win32 Thread Local Storage mechanism. A single call to
   TlsAlloc() will make available a single 32 bit location to every
   thread in the process, including those created after the call is
   made.

   Provided we don't need to call pthread_self() after the Win32
   thread has terminated we can use the DLL entry point routine to
   initialise TLS for each thread. Or we can use pthread_once() in
   pthread_create() to do it.

   We can use either option. We'll use the DLL entry point routine.

 */

int
_pthread_new_thread(pthread_t * thread)
{
  pthread_t new_thread;

  if (_pthread_reuse_top >= 0)
    {
      new_thread = _pthread_reuse[_pthread_reuse_top--];
    }
  else
    {
      if (_pthread_virgin_next < PTHREAD_THREADS_MAX)
	{
	  new_thread = (pthread_t) &_pthread_virgins[_pthread_virgin_next++];
	}
      else
	{
	  return EAGAIN;
	}
    }

  new_thread->win32handle = (HANDLE) NULL;
  new_thread->ptstatus = _PTHREAD_NEW;
  pthread_attr_init(&(new_thread->attr));
  new_thread->joinvalueptr = NULL;
  new_thread->cancelstate = PTHREAD_CANCEL_ENABLE;
  new_thread->canceltype = PTHREAD_CANCEL_DEFERRED;
  new_thread->cancel_pending = FALSE;
  new_thread->cleanupstack = NULL;
  new_thread->destructorstack = NULL;
  new_thread->forkpreparestack = NULL;
  new_thread->forkparentstack = NULL;
  new_thread->forkchildstack = NULL;

  *thread = new_thread;

  return 0;
}

int
_pthread_delete_thread(_pthread_t * thread)
{
  /* We don't check that the thread has been properly cleaned up, so
     it had better be done already. */

  /* Remove the thread entry if necessary. */

  if (thread != NULL
      && thread->ptstatus == _PTHREAD_EXITED)
    {
      pthread_attr_destroy(&(thread->attr));
      thread->win32handle = (HANDLE) NULL;
      thread->ptstatus = _PTHREAD_REUSE;

      _pthread_reuse[++_pthread_reuse_top] = thread;
    }
  else
    {
      return EINVAL;
    }
  return 0;
}