/*
 * atomic_tester.c
 *
 * Test various atomic ops
 *
 * 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_getopt.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_threadstuff.h>

#include <stdio.h>

char *progname;

int use_kernel = 0;
long perf_iters = 0;

#define AVG_TS(_ts_,_n_,_d_) { \
  _d_ = (_ts_).ts_nsec; \
  _d_ /= (double)NASD_NSEC_PER_SEC; \
  _d_ += (_ts_).ts_sec; \
  _d_ /= (double)(_n_); \
}

void
usage()
{
  fprintf(stderr, "USAGE: %s [options]\n", progname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "  -k test kernel\n");
  fprintf(stderr, "  -p test performance (requires number of iterations)\n");
  fflush(stderr);
  exit(1);
}

void
test_perf(
  long  niters)
{
  nasd_timespec_t ts;
  nasd_timer_t tm;
  double d;
  int i;

  nasd_uint64 uint64_var, uint64_var2;
  nasd_timespec_t ts_var, ts_var2;
  nasd_timer_t tm_var;
  long long_var;
  int int_var;

  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Noop:           %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  long_var = 0;
  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    long_var++;
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Increment long: %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  int_var = 0;
  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    int_var++;
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Increment int:  %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  uint64_var = 0;
  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    uint64_var++;
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Increment u64:  %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  uint64_var = 0;
  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    NASD_ATOMIC_INC64(&uint64_var);
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Atomic inc u64: %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  uint64_var = 0;
  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    NASD_ATOMIC_DEC64(&uint64_var);
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Atomic dec u64: %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  uint64_var = 0;
  uint64_var2 = 0;
  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    NASD_ATOMIC_ADD64(&uint64_var, uint64_var2);
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Atomic add u64: %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  uint64_var = 0;
  uint64_var2 = 0;
  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    NASD_ATOMIC_SUB64(&uint64_var, uint64_var2);
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Atomic sub u64: %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    NASD_TIMESPEC_ZERO(ts_var);
    NASD_TM_START(&tm_var);
    NASD_TM_STOP_ACCUM_TS(&tm_var, &ts_var);
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Do timer:       %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);

  NASD_TIMESPEC_ZERO(ts_var);
  NASD_TIMESPEC_ZERO(ts_var2);
  uint64_var = 0;
  for(i=0;i<niters;i++) {
    NASD_TIMESPEC_ZERO(ts);
    NASD_TM_START(&tm);
    NASD_ATOMIC_TIMESPEC_ADD(&ts_var, &ts_var2);
    NASD_TM_STOP_ACCUM_TS(&tm, &ts);
  }
  AVG_TS(ts, niters, d);
  printf("Atomic ts add:  %d:%09d (avg %.9f)\n", ts.ts_sec, ts.ts_nsec, d);
  fflush(stdout);
}

int
main(
  int     argc,
  char  **argv)
{
  nasd_status_t rc;
  char c;

  progname = argv[0];

  while (nasd_getopt(argc, argv, "kp:", &c)) {
    switch(c) {
      case 'k':
        if (perf_iters)
          usage();
        use_kernel = 1;
        break;
      case 'p':
        if (use_kernel)
          usage();
        if (sscanf(nasd_optarg, "%ld", &perf_iters) != 1)
          usage();
        if (perf_iters < 1)
          usage();
        break;
      default:
        fprintf(stderr, "Unknown option '%c'\n", nasd_optopt);
        usage();
    }
  }

  if (nasd_optind < argc)
    usage();

  rc = nasd_threads_init();
  if (rc) {
    fprintf(stderr,
      "ERROR: could not initialize threads subsystem, rc=0x%x (%s)\n",
      rc, nasd_error_string(rc));
    fflush(stderr);
    exit(1);
  }

  if (perf_iters) {
    test_perf(perf_iters);
    goto done;
  }
  else if (use_kernel == 0) {
    rc = nasd_atomic_test();
  }
  else {
    rc = nasd_kernel_atomic_test();
  }

  if (rc) {
    fprintf(stderr, "Atomic tests FAILED\n");
    fflush(stderr);
    exit(1);
  }

  printf("Atomic tests SUCCEEDED\n");

done:
  nasd_threads_shutdown();
  fflush(stdout);
  exit(0);
}

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