/*
 * nasd_timeout.c
 *
 * Generic NASD timeout mechanism.
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_shutdown.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_timeout.h>

NASD_DECLARE_ONCE(nasd_timeout_init_once)
NASD_DECLARE_MUTEX(nasd_timeout_use_counter_lock)
int nasd_timeout_use_counter;
nasd_shutdown_list_t *nasd_timeout_shutdown_list = NULL;

/*
 * nasd_timeout_real_init
 *
 * Called when the system comes into use after not being
 * used (start-of-day call to nasd_timeout_init(), or first
 * such call after the last call to nasd_timeout_shutdown()
 * which actually deactivated the system).
 */
nasd_status_t
nasd_timeout_real_init()
{
  nasd_status_t rc;

  NASD_ASSERT(nasd_timeout_use_counter == 1);

  /* already did threads_init() */

  rc = nasd_mem_init();
  if (rc) {
    nasd_threads_shutdown();
    return(rc);
  }

  rc = nasd_shutdown_sys_init();
  if (rc) {
    nasd_threads_shutdown();
    nasd_mem_shutdown();
    return(rc);
  }

  rc = nasd_shutdown_list_init(&nasd_timeout_shutdown_list);
  if (rc) {
    nasd_shutdown_cleanup();
    nasd_mem_shutdown();
    nasd_threads_shutdown();
    return(rc);
  }

  rc = nasd_sys_timeout_init(nasd_timeout_shutdown_list);
  if (rc)
    goto bad;

  return(NASD_SUCCESS);

bad:
  rc = nasd_shutdown_list_shutdown(nasd_timeout_shutdown_list,
    NASD_SHUTDOWN_ANNOUNCE_NONE);
  if (rc) {
    NASD_PANIC();
  }
  nasd_shutdown_cleanup();
  nasd_mem_shutdown();
  nasd_threads_shutdown();
  return(rc);
}

/*
 * nasd_timeout_real_shutdown
 *
 * Called when last user of the timeout system calls nasd_timeout_shutdown().
 * Clean up and deallocate resources.
 */
void
nasd_timeout_real_shutdown()
{
  nasd_status_t rc;

  NASD_ASSERT(nasd_timeout_use_counter == 0);

  rc = nasd_shutdown_list_shutdown(nasd_timeout_shutdown_list,
    NASD_SHUTDOWN_ANNOUNCE_NONE);
  if (rc) {
    NASD_PANIC();
  }
  nasd_timeout_shutdown_list = NULL;
  nasd_shutdown_cleanup();
  nasd_mem_shutdown();
  nasd_threads_shutdown();
}

/*
 * nasd_timeoutsys_init
 *
 * Executed exactly once, the first time nasd_timeout_init() is
 * called. Initialize counter tracking number of times system
 * is initted.
 */
void
nasd_timeoutsys_init()
{
  nasd_status_t rc;

  rc = nasd_mutex_init(&nasd_timeout_use_counter_lock);
  if (rc) {
    NASD_PANIC();
  }

  nasd_timeout_use_counter = 0;
}

/*
 * nasd_timeout_init
 *
 * Keep a counter of the number of times we're initted and
 * shutdown. When the last shutdown arrives, really deallocate.
 * This lets multiple subsystems use us without knowing about
 * one another.
 */
nasd_status_t
nasd_timeout_init()
{
  nasd_status_t rc;

  rc = nasd_threads_init();
  if (rc)
    return(rc);
  nasd_once(&nasd_timeout_init_once, nasd_timeoutsys_init);
  NASD_LOCK_MUTEX(nasd_timeout_use_counter_lock);
  nasd_timeout_use_counter++;
  if (nasd_timeout_use_counter == 1) {
    rc = nasd_timeout_real_init();
    if (rc) {
      nasd_timeout_use_counter = 0;
    }
  }
  else {
    /* get rid of extra threads init above */
    nasd_threads_shutdown();
    rc = NASD_SUCCESS;
  }
  NASD_UNLOCK_MUTEX(nasd_timeout_use_counter_lock);

  return(rc);
}

/*
 * nasd_timeout_shutdown
 *
 * Previous caller of nasd_timeout_init() not using the timeout
 * subsystem any more. Deallocate and cleanup iff necessary.
 */
void
nasd_timeout_shutdown()
{
  NASD_ASSERT(nasd_timeout_use_counter != 0);

  NASD_LOCK_MUTEX(nasd_timeout_use_counter_lock);
  nasd_timeout_use_counter--;
  if (nasd_timeout_use_counter == 0) {
    nasd_timeout_real_shutdown();
  }
  NASD_UNLOCK_MUTEX(nasd_timeout_use_counter_lock);
}

#if NASD_TIMEOUT_TEST > 0

typedef struct nasd_timeout_tester_s nasd_timeout_tester_t;

struct nasd_timeout_tester_s {
  char                   name[1024];
  NASD_DECLARE_COND(c)
  NASD_DECLARE_MUTEX(m)
  nasd_timespec_t        desired_time;
  nasd_timespec_t        fire_time;
  int                    fired;
  int                    counter;
  nasd_timeout_handle_t  handle;
};

nasd_timeout_tester_t nasd_timeout_test_rel_10;
nasd_timeout_tester_t nasd_timeout_test_abs_10;
nasd_timeout_tester_t nasd_timeout_test_stk_cancel;
nasd_timeout_tester_t nasd_timeout_test_dyn_cancel;
nasd_timeout_tester_t nasd_timeout_test_per;
nasd_timeout_tester_t nasd_timeout_test_still_in_queue;

int nasd_timeout_test_failed = 0;

nasd_timespec_t nasd_timeout_test_period = { 0, 500000000 };

nasd_timespec_t nasd_timeout_test_delta = { 0, 9000000 };

#define NASD_TIMEOUT_TEST_NPERIODIC 45

#define TT_INIT(_t_) { \
  (_t_)->fired = 0; \
  (_t_)->counter = 0; \
  rc = nasd_cond_init(&(_t_)->c); \
  if (rc) { \
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) initting cond for %s\n", \
      rc, nasd_error_string(rc), (_t_)->name); \
    goto done; \
  } \
  rc = nasd_shutdown_cond(nasd_timeout_shutdown_list, &(_t_)->c); \
  if (rc) { \
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) initting shutdown of cond for %s\n", \
      rc, nasd_error_string(rc), (_t_)->name); \
    goto done; \
  } \
  rc = nasd_mutex_init(&(_t_)->m); \
  if (rc) { \
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) initting mutex for %s\n", \
      rc, nasd_error_string(rc), (_t_)->name); \
    goto done; \
  } \
  rc = nasd_shutdown_mutex(nasd_timeout_shutdown_list, &(_t_)->m); \
  if (rc) { \
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) initting shutdown of mutex for %s\n", \
      rc, nasd_error_string(rc), (_t_)->name); \
    goto done; \
  } \
}

#define TT_WAIT(_t_) { \
  NASD_LOCK_MUTEX((_t_)->m); \
  while((_t_)->fired == 0) { \
    NASD_WAIT_COND((_t_)->c, (_t_)->m); \
  } \
  NASD_UNLOCK_MUTEX((_t_)->m); \
  nasd_printf("NASD TIMEOUT TESTER completed %s\n", (_t_)->name); \
  if (nasd_timeout_test_failed) { \
    rc = NASD_FAIL; \
    goto done; \
  } \
}

#define TT_DONE(_t_) { \
  NASD_LOCK_MUTEX((_t_)->m); \
  (_t_)->fired = 1; \
  (_t_)->counter++; \
  NASD_UNLOCK_MUTEX((_t_)->m); \
  NASD_SIGNAL_COND((_t_)->c); \
}

nasd_status_t
nasd_timeout_test_check_tester(
  nasd_timeout_tester_t  *t)
{
  nasd_timespec_t diff;
  int early;

  nasd_gettime(&t->fire_time);
  if (NASD_TIMESPEC_GE(t->fire_time, t->desired_time)) {
    diff = t->fire_time;
    NASD_TIMESPEC_SUB(diff, t->desired_time);
    early = 0;
  }
  else {
    diff = t->desired_time;
    NASD_TIMESPEC_SUB(diff, t->fire_time);
    early = 1;
  }

  if (NASD_TIMESPEC_LE(diff, nasd_timeout_test_delta) && (early == 0))
    return(NASD_SUCCESS);

  nasd_printf("NASD TIMEOUT TEST- WARNING: timeout %s diff %d:%09d early=%d\n",
    t->name, diff.ts_sec, diff.ts_nsec, early);
  nasd_printf("  desired %d:%09d   fired %d:%09d\n",
    t->desired_time.ts_sec, t->desired_time.ts_nsec,
    t->fire_time.ts_sec, t->fire_time.ts_nsec);

  return(NASD_FAIL);
}

void
nasd_timeout_test_basic(
  nasd_timeout_handle_t   th,
  void                   *arg1,
  void                   *arg2)
{
  nasd_timeout_tester_t *t;
  nasd_timeout_status_t st;
  nasd_status_t rc;
  void *p2;

  t = (nasd_timeout_tester_t *)arg1;
  p2 = (void *)&t->fired;

  nasd_printf("NASD TIMEOUT TESTER: fire %s (%d)\n", t->name, t->counter);

  if (p2 != arg2) {
    nasd_printf("NASD TIMEOUT TESTER: %s got arg2 0x%lx\n",
      t->name, (unsigned long)arg2);
    nasd_timeout_test_failed = 1;
    TT_DONE(t);
    return;
  }

  if (bcmp((char *)&th, (char *)&t->handle, sizeof(nasd_timeout_handle_t))) {
    nasd_printf("NASD TIMEOUT TESTER: %s got called with handle 0x%lx "
           "expected 0x%lx\n",
      t->name, (unsigned long)th, (unsigned long)t->handle);
    nasd_timeout_test_failed = 1;
    TT_DONE(t);
    return;
  }

  rc = nasd_timeout_test_check_tester(t);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: failed check on %s\n", t->name);
    nasd_timeout_test_failed = 1;
    /* keep going */
  }

  rc = nasd_timeout_get_status(th, &st);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: failed getting status for %s\n",
      t->name);
    nasd_timeout_test_failed = 1;
    TT_DONE(t);
    return;
  }

  if (!(st&NASD_TIMEOUT_S_RUNNING)) {
    nasd_printf("NASD TIMEOUT TESTER: didn't get running flag for %s\n",
      t->name);
    nasd_timeout_test_failed = 1;
    TT_DONE(t);
    return;
  }

  if (!(st&NASD_TIMEOUT_S_KNOWN)) {
    nasd_printf("NASD TIMEOUT TESTER: didn't get known flag for %s\n",
      t->name);
    nasd_timeout_test_failed = 1;
    TT_DONE(t);
    return;
  }

  if (t == &nasd_timeout_test_per) {
    nasd_gettime(&t->desired_time);
    NASD_TIMESPEC_ADD(t->desired_time, nasd_timeout_test_period);
    if (!(st&NASD_TIMEOUT_S_PERIODIC)) {
      nasd_printf("NASD TIMEOUT TESTER: didn't get periodic flag for %s\n",
        t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    if (st&(~(NASD_TIMEOUT_S_KNOWN|NASD_TIMEOUT_S_RUNNING|NASD_TIMEOUT_S_PERIODIC)))
    {
      nasd_printf("NASD TIMEOUT TESTER: got unexpected flags 0x%x for %s\n",
        st, t->name);
      nasd_timeout_test_failed = 1;
    }
    if (t->counter == NASD_TIMEOUT_TEST_NPERIODIC) {
      rc = nasd_timeout_cancel(th);
      if (rc) {
        nasd_printf("NASD TIMEOUT TESTER: got error 0x%x (%s) cancelling %s\n",
          rc, nasd_error_string(rc), t->name);
        nasd_timeout_test_failed = 1;
        TT_DONE(t);
        return;
      }
    }
    if (t->counter > NASD_TIMEOUT_TEST_NPERIODIC) {
      nasd_printf("NASD TIMEOUT TESTER: cancel didn't take for %s!\n",
        t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    TT_DONE(t);
    return;
  }

  if ((t == &nasd_timeout_test_rel_10) || (t == &nasd_timeout_test_rel_10)) {
    if (st&(~(NASD_TIMEOUT_S_KNOWN|NASD_TIMEOUT_S_RUNNING))) {
      nasd_printf("NASD TIMEOUT TESTER: got unexpected flags 0x%x for %s\n",
        st, t->name);
      nasd_timeout_test_failed = 1;
    }
    TT_DONE(t);
    return;
  }

  if (t == &nasd_timeout_test_stk_cancel) {
    /*
     * Technically, the state of the cancelled flag could go
     * either way here. Realistically, we don't expect to see
     * it.
     */
    if (st&(~(NASD_TIMEOUT_S_KNOWN|NASD_TIMEOUT_S_RUNNING|NASD_TIMEOUT_S_CANCELLED)))
    {
      nasd_printf("NASD TIMEOUT TESTER: got unexpected flags 0x%x for %s\n",
        st, t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    TT_DONE(t);
    return;
  }

  if (t == &nasd_timeout_test_dyn_cancel) {
    if (st&(~(NASD_TIMEOUT_S_KNOWN|NASD_TIMEOUT_S_RUNNING))) {
      nasd_printf("NASD TIMEOUT TESTER: got unexpected flags 0x%x for %s\n",
        st, t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    rc = nasd_timeout_cancel(th);
    if (rc) {
      nasd_printf("NASD TIMEOUT TESTER: got error 0x%x (%s) cancelling %s\n",
        rc, nasd_error_string(rc), t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    rc = nasd_timeout_get_status(th, &st);
    if (rc) {
      nasd_printf("NASD TIMEOUT TESTER: failed re-getting status for %s\n",
        t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    if (!(st&NASD_TIMEOUT_S_RUNNING)) {
      nasd_printf("NASD TIMEOUT TESTER: didn't get running flag for %s\n",
        t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    if (!(st&NASD_TIMEOUT_S_KNOWN)) {
      nasd_printf("NASD TIMEOUT TESTER: didn't get known flag for %s\n",
        t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    if (!(st&NASD_TIMEOUT_S_CANCELLED)) {
      nasd_printf("NASD TIMEOUT TESTER: didn't get cancelled flag for %s\n",
        t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    if (st&(~(NASD_TIMEOUT_S_KNOWN|NASD_TIMEOUT_S_RUNNING|NASD_TIMEOUT_S_CANCELLED)))
    {
      nasd_printf("NASD TIMEOUT TESTER: got unexpected flags 0x%x for %s\n",
        st, t->name);
      nasd_timeout_test_failed = 1;
      TT_DONE(t);
      return;
    }
    TT_DONE(t);
    return;
  }

  TT_DONE(t);
}

nasd_status_t
nasd_timeout_test(
  nasd_timespec_t  delta)
{
  nasd_delaycounter_t delayer;
  nasd_timeout_status_t st;
  nasd_timespec_t tf, ti;
  nasd_status_t rc;

  nasd_timeout_test_delta = delta;

  nasd_timeout_test_failed = 0;

  rc = nasd_timeout_init();
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) initting timeout subsys\n",
      rc, nasd_error_string(rc));
    return(rc);
  }

  strcpy(nasd_timeout_test_rel_10.name, "relative-1.0");
  TT_INIT(&nasd_timeout_test_rel_10);
  strcpy(nasd_timeout_test_abs_10.name, "absolute-1.0");
  TT_INIT(&nasd_timeout_test_abs_10);
  strcpy(nasd_timeout_test_dyn_cancel.name, "dynamic-cancel");
  TT_INIT(&nasd_timeout_test_dyn_cancel);
  strcpy(nasd_timeout_test_stk_cancel.name, "static-cancel");
  TT_INIT(&nasd_timeout_test_stk_cancel);
  strcpy(nasd_timeout_test_per.name, "periodic");
  TT_INIT(&nasd_timeout_test_per);
  strcpy(nasd_timeout_test_still_in_queue.name, "still-in-queue");
  TT_INIT(&nasd_timeout_test_still_in_queue);

  tf.ts_sec = 1;
  tf.ts_nsec = 0;
  ti.ts_sec = 0;
  ti.ts_nsec = 0;
  nasd_gettime(&nasd_timeout_test_rel_10.desired_time);
  NASD_TIMESPEC_ADD(nasd_timeout_test_rel_10.desired_time, tf);
  rc = nasd_timeout_add(&nasd_timeout_test_rel_10.handle,
    nasd_timeout_test_basic, &nasd_timeout_test_rel_10,
    &nasd_timeout_test_rel_10.fired, tf, ti, 0);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) adding timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_rel_10.name);
    goto done;
  }
  TT_WAIT(&nasd_timeout_test_rel_10);

  nasd_gettime(&tf);
  ti.ts_sec = 0;
  ti.ts_nsec = 750000000;
  NASD_TIMESPEC_ADD(tf, ti);
  ti = nasd_timeout_test_period;
  nasd_timeout_test_per.desired_time = tf;
  rc = nasd_timeout_add(&nasd_timeout_test_per.handle,
    nasd_timeout_test_basic, &nasd_timeout_test_per,
    &nasd_timeout_test_per.fired, tf, ti,
    NASD_TIMEOUT_F_PERIODIC|NASD_TIMEOUT_F_ABSOLUTE);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) adding timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_per.name);
    goto done;
  }

  nasd_gettime(&tf);
  tf.ts_sec += 2;
  ti.ts_sec = 0;
  ti.ts_nsec = 0;
  nasd_timeout_test_abs_10.desired_time = tf;
  nasd_timeout_test_abs_10.desired_time = tf;
  rc = nasd_timeout_add(&nasd_timeout_test_abs_10.handle,
    nasd_timeout_test_basic, &nasd_timeout_test_abs_10,
    &nasd_timeout_test_abs_10.fired, tf, ti, NASD_TIMEOUT_F_ABSOLUTE);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) adding timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_abs_10.name);
    goto done;
  }
  TT_WAIT(&nasd_timeout_test_abs_10);

  nasd_gettime(&tf);
  tf.ts_sec += 8;
  ti.ts_sec = 0;
  ti.ts_nsec = 0;
  nasd_timeout_test_dyn_cancel.desired_time = tf;
  rc = nasd_timeout_add(&nasd_timeout_test_dyn_cancel.handle,
    nasd_timeout_test_basic, &nasd_timeout_test_dyn_cancel,
    &nasd_timeout_test_dyn_cancel.fired, tf, ti, NASD_TIMEOUT_F_ABSOLUTE);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) adding timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_dyn_cancel.name);
    goto done;
  }

  nasd_gettime(&tf);
  tf.ts_sec += 1;
  ti.ts_sec = 0;
  ti.ts_nsec = 0;
  nasd_timeout_test_stk_cancel.desired_time = tf;
  NASD_TIMESPEC_ADD(nasd_timeout_test_stk_cancel.desired_time, tf);
  rc = nasd_timeout_add(&nasd_timeout_test_stk_cancel.handle,
    nasd_timeout_test_basic, &nasd_timeout_test_stk_cancel,
    &nasd_timeout_test_stk_cancel.fired, tf, ti, NASD_TIMEOUT_F_ABSOLUTE);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) adding timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_stk_cancel.name);
    goto done;
  }
  rc = nasd_timeout_cancel(nasd_timeout_test_stk_cancel.handle);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) cancelling timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_stk_cancel.name);
    goto done;
  }
  NASD_BEGIN_DELAYCNT(&delayer);
  NASD_DELAY_FROM(&delayer, 2000000);
  if (nasd_timeout_test_stk_cancel.fired) {
    nasd_printf("NASD TIMEOUT TESTER: unexpectedly fired %s\n",
      nasd_timeout_test_stk_cancel.name);
    goto done;
  }
  rc = nasd_timeout_get_status(nasd_timeout_test_stk_cancel.handle, &st);
  if (rc == NASD_SUCCESS) {
    if (st&NASD_TIMEOUT_S_KNOWN) {
      nasd_printf("NASD TIMEOUT TESTER: unexpectedly knew cancelled timeout %s\n",
        nasd_timeout_test_stk_cancel.name);
      goto done;
    }
  }
  nasd_printf("NASD TIMEOUT TESTER: correctly cancelled %s\n",
    nasd_timeout_test_stk_cancel.name);

  nasd_gettime(&tf);
  tf.ts_nsec += 1;
  ti.ts_sec = 0;
  ti.ts_nsec = 0;
  nasd_timeout_test_stk_cancel.desired_time = tf;
  rc = nasd_timeout_add(&nasd_timeout_test_stk_cancel.handle,
    nasd_timeout_test_basic, &nasd_timeout_test_stk_cancel,
    &nasd_timeout_test_stk_cancel.fired, tf, ti, NASD_TIMEOUT_F_ABSOLUTE);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) adding timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_stk_cancel.name);
    goto done;
  }
  NASD_BEGIN_DELAYCNT(&delayer);
  NASD_DELAY_FROM(&delayer, 1000000);
  rc = nasd_timeout_cancel(nasd_timeout_test_stk_cancel.handle);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) cancelling timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_stk_cancel.name);
    goto done;
  }
  nasd_printf("NASD TIMEOUT TESTER: wait to complete %s\n",
    nasd_timeout_test_stk_cancel.name);
  TT_WAIT(&nasd_timeout_test_stk_cancel);

  nasd_printf("NASD TIMEOUT TESTER: wait to complete %s\n",
    nasd_timeout_test_dyn_cancel.name);
  TT_WAIT(&nasd_timeout_test_dyn_cancel);

  nasd_printf("NASD TIMEOUT TESTER: wait to complete %s\n", nasd_timeout_test_per.name);
  NASD_LOCK_MUTEX(nasd_timeout_test_per.m);
  while(nasd_timeout_test_per.counter < NASD_TIMEOUT_TEST_NPERIODIC) {
    if (nasd_timeout_test_failed) {
      NASD_UNLOCK_MUTEX(nasd_timeout_test_per.m);
      rc = NASD_FAIL;
      goto done;
    }
    NASD_WAIT_COND(nasd_timeout_test_per.c,
      nasd_timeout_test_per.m);
    if (nasd_timeout_test_failed) {
      NASD_UNLOCK_MUTEX(nasd_timeout_test_per.m);
      rc = NASD_FAIL;
      goto done;
    }
  }
  NASD_UNLOCK_MUTEX(nasd_timeout_test_per.m);

  /*
   * Leave one in the queue to clean up
   */
  tf.ts_sec = 120;
  tf.ts_nsec = 0;
  ti.ts_sec = 0;
  ti.ts_nsec = 0;
  nasd_timeout_test_still_in_queue.desired_time = ti; /* should never fire */
  rc = nasd_timeout_add(&nasd_timeout_test_still_in_queue.handle,
    nasd_timeout_test_basic, &nasd_timeout_test_still_in_queue,
    &nasd_timeout_test_still_in_queue.fired, tf, ti, 0);
  if (rc) {
    nasd_printf("NASD TIMEOUT TESTER: error 0x%x (%s) adding timeout %s\n",
      rc, nasd_error_string(rc), nasd_timeout_test_still_in_queue.name);
    goto done;
  }

  if (nasd_timeout_test_failed) {
    nasd_printf("NASD TIMEOUT TESTER: One or more tests failed\n");
    rc = NASD_FAIL;
  }
  else {
    nasd_printf("NASD TIMEOUT TESTER: All tests passed.\n");
    rc = NASD_SUCCESS;
  }

done:
  nasd_timeout_shutdown();

#if NASD_MEM_COUNT_ALLOC > 0
  if (nasd_mem_allocated) {
    nasd_printf("NASD TIMEOUT TESTER- WARNING: %" NASD_MEMALLOC_FMT
           " bytes of memory still outstanding, something leaked core.\n",
            nasd_mem_allocated);
  }
  else {
    nasd_printf("NASD TIMEOUT TESTER: 0 bytes of memory still outstanding.\n");
  }
#endif /* NASD_MEM_COUNT_ALLOC > 0 */

  return(rc);
}

#endif /* NASD_TIMEOUT_TEST > 0 */

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
