/***************************************************************************/
/* Circthread.c, circular buffer example and test progam between threads.   
 *
 * Usage: Circthread <buffer-size> <event-size> <repeat-count> [<cpu1> <cpu2>]
 */
/***************************************************************************/
static char Usage[] = "\
Usage: Circthread <buffer-size> <event-size> <repeat-count> [<cpu1> <cpu2>]\
";
#define USAGE printf("%s\n",Usage), exit(1)

#ifdef USE_THREAD
#include <pthread.h>
#endif

#include <stdio.h>

#include<sys/types.h>
#include<sys/time.h>

#define DO_CHECKno    /* do data consistency checks  */

#include "Circ.h"

#define MAX_EVTSIZE 65536*2
#define MAGIC 0x31071965
#define GO_ON   0
#define STOP_IT 1

pthread_attr_t thread_attribute;
pthread_t thread_write, thread_read;

#if defined(LynxOS)
#define pthread_addr_t void**
#define pthread_startroutine_t void**
#endif

static unsigned long full, empty;
static int repeat, bufsize, evtsize;
static int cpu1 = -1, cpu2 = -1; 

#if defined AIX || defined Linux
extern void *t_circ_read(void *);
extern void *t_circ_write(void *);
#else
void t_circ_read (int);
void t_circ_write (int);
#endif

#if defined AIX || defined Linux
int rc;
#endif

/***************************************************************************/
int main (int argc, char *argv[])
/*-------------------------------
*/
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
{
  int cid;
  long cpu_mask;
#if defined AIX || defined Linux
  void *status;
#else
  pthread_addr_t status;
#endif
/*......................................................................... */

  if (argc != 4 && argc != 6)
    USAGE;
  bufsize = atoi (argv[1]);
  evtsize = atoi (argv[2]);
  repeat  = atoi (argv[3]);
  if (argc == 6)
    {
      cpu1 = atoi (argv[4]);
      cpu2 = atoi (argv[5]);
    }

  if ((cid = CircOpen (NULL, "NONE", bufsize)) < 0)
    {
      fprintf (stderr, "%s> Circular buffer open failed !\n", argv[0]);
      exit (1);
    }
  printf ("%s> Circular buffer opened for read/write : id=%d\n",
	  argv[0], cid);


  /* setup and start threads to do tcp-send
   */
#if defined AIX || defined Linux
  if (rc = pthread_attr_init (&thread_attribute))
    {
      printf("%s %d\n", "pthread_attr_init", rc);
      exit (1);
    }
#ifdef Linux
  if (rc = pthread_attr_setdetachstate (&thread_attribute, 
      PTHREAD_CREATE_JOINABLE))
#else
  if (rc = pthread_attr_setdetachstate (&thread_attribute, 
      PTHREAD_CREATE_UNDETACHED))
#endif
    {
      printf("%s %d\n", "pthread_setdetachstate", rc);
      exit (1);
    }
#else
  if (pthread_attr_create (&thread_attribute) < 0)
    {
      perror ("pthread_attr_create");
      exit (1);
    }
#endif

#if defined AIX || defined Linux
  if (rc = pthread_create (&thread_read, &thread_attribute,
		      t_circ_read,
		      (void *)cid))
    {
      printf("%s %d\n", "pthread_create t_circ_read()", rc);
      exit (1);
    }
#else
  if (pthread_create (&thread_read, thread_attribute,
		      (pthread_startroutine_t) t_circ_read,
		      (void *) cid) < 0)
    {
      perror ("pthread_create t_circ_read()");
      exit (1);
    }
#endif
#ifdef __osf__
  if (cpu1 >= 0) /* does not work ! */
    {
      cpu_mask = 1<<cpu1;
      printf("mask1=0x%x\n",cpu_mask);
      if (pthread_bind_to_cpu_np(thread_read, cpu_mask) < 0)
	perror ("cpu1: pthread_bind_to_cpu_np");
    }
#endif

#if defined AIX || defined Linux
  if (rc = pthread_create (&thread_write, &thread_attribute,
		      t_circ_write,
		      (void *)cid))
    {
      printf("%s %d\n", "pthread_create t_circ_write()", rc);
      exit (1);
    }
#else
  if (pthread_create (&thread_write, thread_attribute,
		      (pthread_startroutine_t) t_circ_write,
		      (void *) cid) < 0)
    {
      perror ("pthread_create t_circ_write()");
      exit (1);
    }
#endif
#ifdef __osf__
  if (cpu2 >= 0)
    {
      cpu_mask = 1<<cpu2;
      printf("mask2=0x%x\n",cpu_mask);
      if (pthread_bind_to_cpu_np(thread_write, cpu_mask) < 0)
	perror ("cpu2: pthread_bind_to_cpu_np");
    }
#endif

#if defined AIX || defined Linux
  if (rc = pthread_join (thread_write, &status))
    printf("%s %d\n", "pthread_join write", rc);
  if (rc = pthread_join (thread_read, &status))
    printf("%s %d\n", "pthread_join read", rc);
#else
  if (pthread_join (thread_write, &status) < 0)
    perror ("pthread_join");
  if (pthread_join (thread_read, &status) < 0)
    perror ("pthread_join");
#endif

  (void) CircClose (cid);
}

/***************************************************************************/
#if defined AIX || defined Linux
void *t_circ_read(void *cid_arg)
#else
void t_circ_read (int cid)
#endif
/*------------------------
*/
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
{
#if defined AIX || defined Linux
  int cid = (int)cid_arg;
#endif
  int repeat, evtsize, number, last;
  int *data;
  int *ptr;
/*......................................................................... */

  data = (int *) malloc (MAX_EVTSIZE);

  /* Test 1: CircLocate, CircRelease without memcpy
   */
  printf ("%s> Test #1 using CircLocate/CircRelease without moving data\n",
	  "t_circ_read");

  empty = 0;
  repeat = 0;
  data[0] = GO_ON;
  while (data[0] == GO_ON)
    {
      while ( (ptr = (int*)CircLocate (cid, &number, &evtsize)) 
	      == (int*)-1)
	{
	  empty++;
	  pthread_yield ();
	}
      last = (evtsize/sizeof (int) - 1);
      if (repeat++ == 0)
	timing_start ();
#ifdef DO_CHECK
      if (ptr[last] != MAGIC)
	{
	  printf ("%s> Invalid MAGIC number %x found for event #%d !\n",
		  ptr[last], repeat);
	  exit (1);
	}
      if (repeat != number)
	{
	  printf ("%s> Invalid event number %d found instead of %d !\n",
		  number, repeat);
	  exit (1);
	}
#endif
      data[0] = ptr[0];
      (void) CircRelease (cid);
    }

  /* Test 2: CircGet with memcpy
   */
  printf ("%s> Test #2 using CircGet and moving data (memcpy)\n",
	  "t_circ_read");
  empty = 0;
  repeat = 0;
  data[0] = GO_ON;
  while (data[0] == GO_ON)
    {
      while ((evtsize = CircGet (cid, &number, (char *) data, MAX_EVTSIZE))
	     < 0)
	{
	  empty++;
	  pthread_yield ();
	}
#ifdef DO_CHECK
      last = (evtsize/sizeof (int) - 1);
      if (data[last] != MAGIC)
	{
	  printf ("%s> Invalid MAGIC number %x found for event number %d !\n",
		  "thread_read", data[last], repeat);
	  exit (1);
	}
      if (++repeat != number)
	{
	  printf ("%s> Invalid event number %d found instead of %d !\n",
		  "thread_read", number, repeat);
	  exit (1);
	}
#endif
    }
}
/***************************************************************************/
#if defined AIX || defined Linux
void *t_circ_write(void *cid_arg)
#else
void t_circ_write (int cid)
#endif
/*-------------------------
*/
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
{
#if defined AIX || defined Linux
  int cid = (int)cid_arg;
#endif
  int *data, i, last;
  int *ptr;
  double real_time, cpu_time, cpu_usage;
/*......................................................................... */

  data = (int *) malloc (evtsize);
  last = (evtsize/sizeof(int) - 1);
  data[0] = 0;
  data[last] = MAGIC;

  /* Test #1: CircReserve, CircValidate without memcpy
     ****************************************** */
  printf ("%s> Test #1 using CircReserve/CircValidate without moving data\n", 
  "t_circ_write");
  full = 0;
  timing_start ();
  for (i = 1; i <= repeat; i++)
    {

      while ( (ptr = (int *)CircReserve (cid, i, evtsize)) 
	      == (int*)-1)
	{
	  full++;
	  pthread_yield ();
	}
      if (i < repeat)
	ptr[0] = 0;
      else
	ptr[0] = 1;		/* mark end of test */
#ifdef DO_CHECK
      ptr[last] = MAGIC;
#endif
      (void) CircValidate (cid, i, (char *) ptr, evtsize);
    }
  timing_stop (&real_time, &cpu_time, &cpu_usage);
  if (real_time > 0.0)
    {
      printf ("%s> Real-time: %.2fs, CPU-time: %.2fs, CPU-usage: %.1f%%\n",
	      "t_circ_write", real_time, cpu_time, cpu_usage);
      printf ("%s> Events: %d, %.0f per sec\n", "t_circ_write",
	      repeat, (double) repeat / real_time);
      printf ("%s> Bytes : %.0f, %.1f MB /sec\n", "t_circ_write",
	      (double) repeat * evtsize,
	      (double) repeat * evtsize / 1024. / 1024. / real_time);
      printf ("%s> Buffer full: %d empty: %d\n", "t_circ_write", full, empty);
    }
  else
    printf ("%s> not timing possible\n", "t_circ_write");

  /* Test 1: CircPut with memcpy
     ***************************** */
  printf ("%s> Test #1 using CircPut and moving data (memcpy)\n",
	  "t_circ_write");

  full = 0;
  timing_start ();
  for (i = 1; i <= repeat; i++)
    {
      if (i == repeat)
	data[0] = 1;		/* mark end of test */

      while (CircPut (cid, i, (char *) data, evtsize) < 0)
	{
	  full++;
	  pthread_yield ();
	}
    }
  timing_stop (&real_time, &cpu_time, &cpu_usage);
  if (real_time > 0.0)
    {
      printf ("%s> Real-time: %.2fs, CPU-time: %.2fs, CPU-usage: %.1f%%\n",
	      "t_circ_write", real_time, cpu_time, cpu_usage);
      printf ("%s> Events: %d, %.0f per sec\n", "t_circ_write",
	      repeat, (double) repeat / real_time);
      printf ("%s> Bytes : %.0f, %.1f MB /sec\n", "t_circ_write",
	      (double) repeat * evtsize,
	      (double) repeat * evtsize / 1024. / 1024. / real_time);
      printf ("%s> Buffer full: %d empty: %d\n", "t_circ_write", full, empty);
    }
  else
    printf ("%s> not timing possible\n", "t_circ_write");
  
}