/* beginth.c (emx+gcc) -- Copyright (c) 1992-1998 by Eberhard Mattes */

#if defined (__MT__)

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <emx/thread.h>
#include <emx/syscalls.h>
#define INCL_DOSPROCESS
#define INCL_DOSERRORS
#define INCL_DOSEXCEPTIONS
#include <os2emx.h>

#define MAX_THREADS 1024

static struct _thread thread_1 =
{
  0,                            /* _th_errno */
  NULL,                         /* _th_arg */
  NULL,                         /* _th_start */
  NULL,                         /* _th_strtok_ptr (must be NULL) */
  {0},                          /* _th_asctime_buf */
  {0},                          /* _th_tmpnam_buf */
  {0},                          /* _th_gmtime_buf */
  1,                            /* _th_rand */
  NULL,                         /* _th_store */
  0,                            /* _th_mblen_shift */
  0,                            /* _th_mbtowc_shift */
  0,                            /* _th_wctomb_shift */
  "",                           /* _th_vollabel */
  "",                           /* _th_error */
  NULL,                         /* _th_rheap */
  NULL,                         /* _th_theap */
  ""                            /* _th_ttyname */
};

struct _thread *_thread_tab[MAX_THREADS+1] = {NULL, &thread_1};


struct _thread *__alloc_thread (void)
{
  struct _thread *tp;
  ULONG rc;

  rc = DosAllocMem ((PPVOID)&tp, sizeof (struct _thread),
                    PAG_COMMIT|PAG_READ|PAG_WRITE);
  if (rc != 0)
    return NULL;
  memset (tp, 0, sizeof (struct _thread));
  tp->_th_rand = 1;             /* Initialize rand() */
  return tp;
}


static void start_thread (struct _thread *tp)
{
  EXCEPTIONREGISTRATIONRECORD reg;

  __initthread (&reg);
  tp->_th_start (tp->_th_arg);
  _endthread();
}


int _beginthread (void (*start)(void *arg), void *stack, unsigned stack_size,
                  void *arg_list)
{
  ULONG rc;
  TID tid;
  struct _thread *tp;

  tp = __alloc_thread ();
  if (tp == NULL)
    {
      errno = ENOMEM;
      return -1;
    }
  tp->_th_start = start;
  tp->_th_arg = arg_list;
  rc = DosCreateThread (&tid, (PFNTHREAD)start_thread, (ULONG)tp,
                        3, stack_size);
  if (rc != 0)
    {
      if (rc == ERROR_NOT_ENOUGH_MEMORY)
        errno = ENOMEM;
      else if (rc == ERROR_MAX_THRDS_REACHED)
        errno = EAGAIN;
      else
        errno = EINVAL;
      DosFreeMem (tp);
      return -1;
    }
  if (tid > MAX_THREADS)
    {
      DosKillThread (tid);
      errno = EAGAIN;
      DosFreeMem (tp);
      return -1;
    }
  if (__newthread (tid) != 0)
    {
      DosKillThread (tid);
      DosFreeMem (tp);
      return -1;
    }
  _thread_tab[tid] = tp;
  rc = DosResumeThread (tid);
  if (rc != 0)
    {
      errno = ESRCH;
      DosFreeMem (tp);
      return -1;
    }
  return tid;
}


void _endthread (void)
{
  struct _thread *tp;
  int tid;

  tid = _gettid ();
  tp = _thread ();
  if (tp != &thread_1)
    DosFreeMem (tp);
  _thread_tab[tid] = NULL;
  __endthread (tid);
  for (;;)
    DosExit ((tid == 1 ? EXIT_PROCESS : EXIT_THREAD), 0);
}

#endif
