/* threads.c (emx+gcc) */

#define INCL_DOSMEMMGR
#define INCL_DOSPROCESS
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <os2emx.h>

static int alloca_flag = FALSE;
static int malloc_flag = FALSE;
static int malloc_errors = 0;

static char *check[] =
{
  "ok", "empty", "bad begin", "bad node", "bad end", "bad rover"
};


static void usage (void)
{
  fputs ("Usage: threads [-ams] <number>\n", stderr);
  exit (1);
}


static void thread (void *t)
{
  int i;
  ULONG written;
  char c;
  void *p;

  c = *(char *)t;
  if (alloca_flag)
    p = alloca (0x10000);
  for (i = 0; i < 1000; ++i)
    {
      if (malloc_flag)
        {
          p = malloc (4);
          if (p == NULL || realloc (p, 16) == NULL)
            {
              ++malloc_errors;
              c = (char)toupper (c);
            }
        }
      DosWrite (1, &c, 1, &written);
      c = (char)tolower (c);
    }
}


static void show_mem (void)
{
  ULONG rc, size, flags, base, esp;

  base = 0x10000;
  esp = (ULONG)&esp;
  for (;;)
    {
      size = 0xffffffff;
      rc = DosQueryMem ((PVOID)base, &size, &flags);
      if (rc != 0)
        return;
      printf ("%.8lx - %.8lx (%.8lx)", base, base + size - 1, size);
      if (flags & PAG_FREE)    printf (" FREE  ");
      if (flags & PAG_COMMIT)  printf (" COMMIT");
      if (flags & PAG_GUARD)   printf (" GUARD ");
      if (flags & PAG_SHARED)  printf (" share ");
      if (flags & PAG_READ)    printf (" read  ");
      if (flags & PAG_READ)    printf (" write ");
      if (flags & PAG_EXECUTE) printf (" exec  ");
      if (base <= esp && esp < base + size) printf ("<-- ESP");
      putchar ('\n');
      base += size;
      if (base == 0) break;
    }
}


int main (int argc, char *argv[])
{
  char *tmp;
  int c, i, n, rc, running, show_mem_flag, big_stack_flag;
  TID tid;

  show_mem_flag = FALSE; big_stack_flag = FALSE;
  opterr = FALSE;
  while ((c = getopt (argc, argv, "abms")) != -1)
    switch (c)
      {
      case 'a':
        alloca_flag = TRUE;
        break;
      case 'b':
        big_stack_flag = TRUE;
        break;
      case 'm':
        malloc_flag = TRUE;
        break;
      case 's':
        show_mem_flag = TRUE;
        break;
      default:
        usage ();
      }
  if (argc - optind != 1)
    usage ();
  n = atoi (argv[optind]);
  if (n < 1 || n > 26)
    usage ();
  c = 'a'; running = 0;
  for (i = 0; i < n; ++i)
    {
      tmp = alloca (1);
      *tmp = (char)c;
      rc = _beginthread (thread, NULL,
                         (big_stack_flag ? 16*1024*1024 : 128*1024), tmp);
      if (rc == -1)
        {
          perror ("_beginthread");
          exit (2);
        }
      ++c; ++running;
    }
  if (show_mem_flag)
    {
      DosEnterCritSec ();
      putchar ('\n');
      show_mem ();
      fflush (stdout);
      DosExitCritSec ();
    }
  while (running != 0)
    {
      tid = 0;
      if (DosWaitThread (&tid, DCWW_WAIT) != 0)
        break;
    }
  if (malloc_flag)
    {
      printf ("\n");
      if (malloc_errors != 0)
        printf ("%d malloc errors\n", malloc_errors);
      printf ("Heap check: %s\n", check[_heapchk ()]);
    }
  return 0;
}
