/* t-thread.c
 * Copyright 2012 g10 Code GmbH
 *
 * This file is free software; as a special exception the author gives
 * unlimited permission to copy and/or distribute it, with or without
 * modifications, as long as this notice is preserved.
 *
 * This file is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "t-support.h"


static int counter;
static npth_mutex_t counter_mutex;
static int thread_twoone_ready;

static void *
thread_one (void *arg)
{
  int rc, i;

  info_msg ("thread-one started");
  npth_usleep (10);  /* Give the other thread some time to start.  */
  for (i=0; i < 10; i++)
    {
      /* We would not need the mutex here, but we use it to allow the
         system to switch to another thread.  */
      rc = npth_mutex_lock (&counter_mutex);
      fail_if_err (rc);

      counter++;

      rc = npth_mutex_unlock (&counter_mutex);
      fail_if_err (rc);
    }
  info_msg ("thread-one terminated");

  return (void*)4711;
}


static void *
thread_twoone (void *arg)
{
  int rc;

  npth_setname_np (npth_self (), "thread-twoone");
  info_msg ("thread-twoone started");

  rc = npth_detach (npth_self ());
  fail_if_err (rc);

  while (counter < 100)
    {
      npth_usleep (1000);
      counter++;
    }
  info_msg ("thread-twoone terminated");
  thread_twoone_ready = 1;

  return NULL;
}


static void *
thread_two (void *arg)
{
  int rc, i;

  info_msg ("thread-two started");

  for (i=0; i < 10; i++)
    {
      rc = npth_mutex_lock (&counter_mutex);
      fail_if_err (rc);

      counter--;

      if (i == 5)
        {
          npth_t tid;

          info_msg ("creating thread-twoone");
          rc = npth_create (&tid, NULL, thread_twoone, NULL);
          fail_if_err (rc);
          npth_usleep (10);  /* Give new thread some time to start.  */
        }

      rc = npth_mutex_unlock (&counter_mutex);
      fail_if_err (rc);
    }

  info_msg ("busy waiting for thread twoone");
  while (!thread_twoone_ready)
    npth_sleep (0);

  info_msg ("thread-two terminated");

  return (void*)4722;
}





int
main (int argc, char *argv[])
{
  int rc;
  npth_attr_t tattr;
  int state;
  npth_t tid1, tid2;
  void *retval;

  if (argc >= 2 && !strcmp (argv[1], "--verbose"))
    opt_verbose = 1;

  rc = npth_init ();
  fail_if_err (rc);

  rc = npth_mutex_init (&counter_mutex, NULL);
  fail_if_err (rc);

  rc = npth_attr_init (&tattr);
  fail_if_err (rc);
  rc = npth_attr_getdetachstate (&tattr, &state);
  fail_if_err (rc);
  if ( state != NPTH_CREATE_JOINABLE )
    fail_msg ("new tattr is not joinable");

  info_msg ("creating thread-one");
  rc = npth_create (&tid1, &tattr, thread_one, NULL);
  fail_if_err (rc);
  npth_setname_np (tid1, "thread-one");

  info_msg ("creating thread-two");
  rc = npth_create (&tid2, &tattr, thread_two, NULL);
  fail_if_err (rc);
  npth_setname_np (tid2, "thread-two");

  rc = npth_attr_destroy (&tattr);
  fail_if_err (rc);

  info_msg ("waiting for thread-one to terminate");
  rc = npth_join (tid1, &retval);
  fail_if_err (rc);
  if (retval != (void*)4711)
    fail_msg ("thread-one returned an unexpected value");

  info_msg ("waiting for thread-two to terminate");
  rc = npth_join (tid2, &retval);
  fail_if_err (rc);
  if (retval != (void*)4722)
    fail_msg ("thread-two returned an unexpected value");

  if (counter != 100)
    fail_msg ("counter value not as expected");

  return 0;
}
