    /***********************************************************************
                   Copyright (c) 2007-2008 SKY Computers, Inc.
    
     Redistribution and use in source and binary forms are permitted
     provided that this notice is preserved and that due credit is given to
     SKY Computers, Inc. The name of SKY Computers, Inc. may not be used to
     endorse or promote products derived from this software without
     specific prior written permission. This software is provided ``as is''
     without express or implied warranty.
     ************************************************************************/
  
/*
 *--------------------------------------------------------------------------
 *
 >++
 *
 *   File: wh_main.c
 *
 *   Function: To provide the startup of the other process threads.
 *
 *   Calling Sequence: See below. 
 *  
 *   Implementation Details:
 *       1. Create the common data structures.
 *       2. Start the other threads.
 *
 *   Restrictions:
 *       1. None.
 *
 >**
 *   Revision Information:
 *    Date       By   Rev    Changes Made...
 *    10/22/07   bwj  0.0    New Module.
 *
 >--
 *--------------------------------------------------------------------------
 */

/* Include files */
#include "sky_ex_inc.h"

#define EX_MAIN   (1)
#include "wh_tcb.h"

#define PASSES  (5000)   /* will not run forever */

/* Globals */
TCB *gTcbBgn;

/*
 *  TimeTrac variables -- Use ifdef's if we want to delete these when
 *  TimeTrac is not compiled into the executable. See Makefile
 */
#ifdef TIME_TRAC
/* Time_Trac variables, handles, functions. */
TTHandle Trace_Log[NUM_THREADS];

TTEventHandle tt_hello_bgn[NUM_THREADS], tt_hello_end[NUM_THREADS];
TTEventHandle tt_vran_bgn[NUM_THREADS], tt_vran_end[NUM_THREADS];
TTEventHandle tt_vadd_bgn[NUM_THREADS], tt_vadd_end[NUM_THREADS];
TTEventHandle tt_vmul_bgn[NUM_THREADS], tt_vmul_end[NUM_THREADS];
TTEventHandle tt_vsub_bgn[NUM_THREADS], tt_vsub_end[NUM_THREADS];
TTEventHandle tt_vsum_bgn, tt_vsum_end;

TTEventHandle MN_init_bgn, MN_init_end;
TTEventHandle MN_runa_bgn, MN_runa_end;
TTEventHandle MN_runb_bgn, MN_runb_end;
#endif  /* TIME_TRAC */

/* Thread entry points. */
extern int compute_main (TCB *t);
extern int compute_sum (TCB *t);

/* ---------------------------------------------------------------------- */

/*
 >++
 *
 *   void tt_init_mn (rank):  (int rank):
 *       int rank: Assigned to the calling thread (always 0 in this example)
 *
 *       Set up the TimeTrac file (open the file, define the range events) 
 *       for the main processing thread.
 *
 *       Returns: Nothing
 >--
 */

void tt_init_mn (rank)
{
  int ret;
  char trace_name[128];
  
  sprintf (trace_name, "%02d_Main", rank);
  
  ret = time_trac_open (trace_name, 0, TT_MAX_EVENTS, 0xffffffff, TT_NO_AUTO_SAVE, 
			TT_CREATE_FILE, 1, &Trace_Log[rank]);
  if (ret != TTE_SUCCESS)
    {
      printf ("%2d -- WARNING: TimeTrac file (%s) could not be opened.", rank, trace_name);
    }
  
  time_trac_reg_range_event (Trace_Log[rank], "a. Main Init", "Brown", 1, &MN_init_bgn, &MN_init_end);
  time_trac_reg_range_event (Trace_Log[rank], "b. Compute", "Black", 1, &MN_runa_bgn, &MN_runa_end);
  time_trac_reg_range_event (Trace_Log[rank], "b. Sum", "Red", 1, &MN_runb_bgn, &MN_runb_end);
}

/* ---------------------------------------------------------------------- */ 

/*
 >++
 *
 *   void tt_init_co (TCB *tcb): (TCB *tcb):
 *       TCB *tcb: Pointer to the thread control block describing this thread.
 *
 *       Set up the TimeTrac file (open the file, define the range events)
 *       for each of the compute threads.
 *
 *       Returns: Nothing
 >--
 */

int tt_init_co (TCB *tcb)
{
  unsigned group_mask = 0xffffffff;
  unsigned group_id = 0x1;
  int use_thread_safe = 0;
  int num_events = 10000;
  int context_depth = 1;
  int status = 4;
  char filename[256];
  
  /* Create the trace file name */
  sprintf (filename, "%02d_Compute", tcb->Rank);
  
  /* Create all neccessary handles */
  status = time_trac_open (filename, use_thread_safe, num_events, group_mask,
			   TT_NO_AUTO_SAVE, TT_CREATE_FILE, context_depth, &Trace_Log[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
  
  status = time_trac_reg_range_event (Trace_Log[tcb->Rank], "a. hello", "Green",
				      group_id, &tt_hello_bgn[tcb->Rank], &tt_hello_end[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
  
  status = time_trac_reg_range_event (Trace_Log[tcb->Rank], "b. vinput", "Brown",
				      group_id, &tt_vran_bgn[tcb->Rank], &tt_vran_end[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
  
  status = time_trac_reg_range_event (Trace_Log[tcb->Rank], "c. vadd", "Orange",
				      group_id, &tt_vadd_bgn[tcb->Rank], &tt_vadd_end[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
  
  status = time_trac_reg_range_event (Trace_Log[tcb->Rank], "d. vmult", "Violet",
				      group_id, &tt_vmul_bgn[tcb->Rank], &tt_vmul_end[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
  
  status = time_trac_reg_range_event (Trace_Log[tcb->Rank], "e. vsub", "Blue",
				      group_id, &tt_vsub_bgn[tcb->Rank], &tt_vsub_end[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
}

/* ---------------------------------------------------------------------- */ 

/*
 >++
 *
 *   void tt_init_sum (TCB *tcb): (TCB *tcb):
 *       TCB *tcb: Pointer to the thread control block describing this thread.
 *
 *       Set up the TimeTrac file (open the file, define the range events) 
 *       for the output thread (it does no real output, just another math
 *       operation).
 *
 *       Returns: Nothing
 >--
 */

int tt_init_sum (TCB *tcb)
{
  unsigned group_mask = 0xffffffff;
  unsigned group_id = 0x1;
  int use_thread_safe = 0;
  int num_events = 10000;
  int context_depth = 1;
  int status = -1;
  char filename[256];
  
  /* Create the trace file name */
  sprintf (filename, "%02d_Sum", tcb->Rank);
  
  /* Create all neccessary handles */
  status = time_trac_open (filename, use_thread_safe, num_events, group_mask,
			   TT_NO_AUTO_SAVE, TT_CREATE_FILE, context_depth, &Trace_Log[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
  
  status = time_trac_reg_range_event (Trace_Log[tcb->Rank], "a. hello", "Green",
				      group_id, &tt_hello_bgn[tcb->Rank], &tt_hello_end[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
  
  status = time_trac_reg_range_event (Trace_Log[tcb->Rank], "b. vsum", "Red",
				      group_id, &tt_vsum_bgn, &tt_vsum_end);
  VERIFY_TIMETRAC_STATUS (status);
}

/* ---------------------------------------------------------------------- */ 

/*
 >++
 *
 *   void tt_term ():
 *
 *       Save all of the TimeTrac events and then close the TimeTrac file.
 *
 *       Returns: Nothing
 >--
 */

void tt_term (TCB *tcb)
{
  int status;
  
  /* Save the events by overwritting any existing file, close the TimeTrac file. */
  status = time_trac_save (Trace_Log[tcb->Rank], TT_OVERWRITE_FILE);
  VERIFY_TIMETRAC_STATUS (status);

  status = time_trac_close (Trace_Log[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
}

/*--------------------------------------------------------------------------*/

/*
 >++
 *
 *   int t_set_common (int rank, TCB *t, EPB *p):
 *       int rank: of the thread of interest
 *       TCB *t: Task block to be initialized.
 *       EPB *p: Global parameter block containing system wide values
 *
 *       Given the rank, the task block, and the parameter list, initialize the 
 *       common values in the task control block.
 *   
 *       Returns: 0 on success.
 >--
 */

int t_set_common (int rank, TCB *t, EPB *p)
{
  t->Rank = rank;
  t->Comm = 0;
  t->Status = 0;
  t->Faults = 0;
  
  return (0);
}

/*--------------------------------------------------------------------------*/

static int ex_saved_tt = NO;

/*
 >++
 *
 *   void an_dump_tt ():
 *
 *       DEBUG / analysis code, used to save the state of the threads.
 *
 *       Returns: Nothing.
 >--
 */
 
void ex_dump_tt ()
{
  int i;

#ifdef TIME_TRAC
  
  if (ex_saved_tt == NO)
    {
      ex_saved_tt = YES;
      
      /* Save all of the TimeTrac files. */
      for (i = 0; i < NUM_THREADS; i++)
        {
	  if (Trace_Log[i] != NULL)
            {
	      time_trac_save (Trace_Log[i], TT_OVERWRITE_FILE);
	      time_trac_close (Trace_Log[i]);
            }
        }
    }
#endif
}

/* ---------------------------------------------------------------------- */

/*
 >++
 *
 *   void sig_ack (int s):
 *       int s: signal to ba handled.
 *
 *       Signal handler for these examples. Certain signals will save TimeTrac
         files and return.
 *
 *       Returns: Nothing
 >--
 */

void sig_ack (int s)
{ 
  switch (s)
    {
    case SIGSEGV:
      printf ("PID %2d: Signal SIGSEGV, from thread %d.", getpid(), (int)pthread_self());
      ex_dump_tt ();
      break;
      
    case SIGTERM:
      ex_dump_tt ();
      printf ("PID %2d: Signal SIGTERM, Dumped state in \"/home/public\").", getpid());
      break;
      
    case SIGINT:
      printf ("PID %2d: Signal SIGINT, from thread %d.", getpid(), (int)pthread_self());
      ex_dump_tt ();
      break;
      
    default:
      printf ("PID %2d: Saw signal %d  %s.", getpid(), s, strsignal (s));
      break;
    }

  exit (-1);
}

/* ---------------------------------------------------------------------- */

/*
 >++
 *
 *   void take_sigs ():
 *
 *       For selected signals, define the action to be taken.
 *
 *       Returns: Nothing.
 *
 >--
 */

void take_sigs ()
{
  int i;

  for (i = 0; i < NSIG; i++)
    {
      switch (i)
        {
	case SIGKILL:
	  signal (i, sig_ack);
	  break;

	case SIGINT:
	  signal (i, sig_ack);
	  break;

	case SIGCHLD:
	  signal (i, sig_ack);
	  break;

	default:
	  signal (i, sig_ack);
	  break;
        }
    }
}

/*--------------------------------------------------------------------------*/

/*
 >++
 *
 *   int main (int argc, char **argv, char **arge): 
 *       args: not currently used.
 *
 *       This is the master thread that starts all of the other threads. It
 *       then manages the work of those threads.
 *   
 *       Returns: Exits when done.
 >--
 */

main (int argc, char **argv, char **arge)
{
  int i, j;            /* misc loop counters */
  int pass;            /* number of time to execute */
  int rank;            /* 0 based rank of each thread */
  TCB *tcb;
  EPB *epb;
  
  pthread_t ex_thread[NUM_THREADS];
  EPB *ex_params;
  
  rank = 0;
  tt_init_mn (rank);
  time_trac_record (Trace_Log[rank], MN_init_bgn, rank);

  printf ("\n%2d -- Starting application,\n"
	  "    Case #1: Do a ^C after a few seconds to simulate a failure, or\n"
	  "    Case #2: Wait for the counter to stop incrementing then do the ^C.\n", 
	  rank);

  /* Allocate some local buffers. */
  ex_mem_init (rank);
  gTcbBgn = (TCB *) ex_mem_alloc (rank, sizeof(TCB) * NUM_THREADS, 64, "Thread Control Blocks");
  tcb = &gTcbBgn[rank];
  epb = (EPB *) ex_mem_alloc (rank, sizeof(TCB) * NUM_THREADS, 64, "Thread Parameter Block");

  /* Set up the interrupt handles. */
  take_sigs ();
  
  /* 
   *  Start all of the compute threads.
   */
  for (i = 1; i <= NUM_CO_THREADS; i++)
    {
      /* Set parameters for this thread ...  */
      tcb = &gTcbBgn[i];
      t_set_common (i, tcb, ex_params);
      tcb->Rank = i;
      
      /* Set up the TimeTrac parameters. */
      tt_init_co (tcb);
      
      /* ... and create the thread. */
      if (pthread_create (&ex_thread[rank], NULL, (void *) &compute_main, (void *) tcb) != 0)
        {
	  printf ("Main: PANIC -- Th %d Thread Create Failed (%s, line %d).", 
		   i, __FILE__, __LINE__);
	  exit (-1);
        }
    }
  
  /* Set parameters for the "output" thread ...  */
  tcb = &gTcbBgn[i];
  t_set_common (i, tcb, ex_params);
  tcb->Rank = i;
      
  /* Set up the TimeTrac parameters for the output thread ... */
  tt_init_sum (tcb);
      
  /* ... and create the thread. */
  if (pthread_create (&ex_thread[rank], NULL, (void *) &compute_sum, (void *) tcb) != 0)
    {
      printf ("Main: PANIC -- Th %d Thread Create Failed (%s, line %d).", 
	       i, __FILE__, __LINE__);
      exit (-1);
    }
  
  time_trac_record (Trace_Log[0], MN_init_end, rank);
  
  /* 
   *  We have started all threads, now wait for all threads to 
   *  complete their startup by entering the init done state.
   */
  for (i = 1; i < NUM_THREADS; i++)
    {
      tcb = &gTcbBgn[i];
      for (j = 0; tcb->Status == STAT_NONE; j++)
        {
	  if ((j % 1000) == 999)
	    {
	      printf ("%2d -- Waiting for thread %d (now = %s).\n", 
		      rank, i, stat_name[tcb->Status]);
	    }
	  
	  usleep (1000);
        }
    }
  
  /* 
   *  ---- This is the main processing loop for this thread. Here we keep
   *       track of which thread is doing what.
   */
  for (pass = 0; pass < PASSES; pass++)
    {
      /* 
       * Each compute thread is commanded to start its processing.
       */
      time_trac_record (Trace_Log[rank], MN_runa_bgn, pass);
      for (i = 1; i <= NUM_CO_THREADS; i++)
	{
	  tcb = &gTcbBgn[i];
	  tcb->Pass = pass;
	  tcb->Status = STAT_NONE;
	  tcb->Comm = COMM_COMP;
        }

      /* Echo something to the screen once in a while ... */
      if ((pass % 31) == 30)
	{
	  printf ("%2d -- Pass = %d.\r", rank, pass);
	  fflush (stdout);
	}
      	  
      /*
       *  Now wait for each to finish.
       */
      for (i = 1; i <= NUM_CO_THREADS; i++)
	{
	  tcb = &gTcbBgn[i];
	  while (tcb->Status != STAT_DONE)
	    {
	      sched_yield ();
	    }
        }
      time_trac_record (Trace_Log[rank], MN_runa_end, pass);
      time_trac_record (Trace_Log[rank], MN_runb_bgn, pass);

      /* Start the last thread. */
      tcb = &gTcbBgn[1+NUM_CO_THREADS];
      tcb->Pass = pass;
      tcb->Status = STAT_NONE;
      tcb->Comm = COMM_COMP;
      
      /* Wait for it to finish. */
      while (tcb->Status != STAT_DONE)
	{
	  sched_yield ();
	}
      time_trac_record (Trace_Log[rank], MN_runb_end, pass);
    }

  /* 
   *  End of computing. Wait for the threads to finish.
   */
  for (i = 1; i < NUM_THREADS; i++)
    {
      /* Set ..  */
      tcb = &gTcbBgn[i];
      while (tcb->Status == STAT_RUN)
	{
	  usleep (1000);
	}
    }
  
  time_trac_record (Trace_Log[0], MN_init_bgn, 0);

  /* End of testing, do a orderly shutdown. */
  printf ("%2d -- Stopped, doing cleanup.\n", rank);
  
  ex_html_mem_dump (rank);
  
  time_trac_record (Trace_Log[0], MN_init_end, 0);
  
  /*  Tell all threads to exit.  */
  for (i = 0; i < NUM_THREADS; i++)
    {
      tcb = &gTcbBgn[i];
      tt_term (tcb);
      tcb->Comm = COMM_EXIT;
    }
  
  usleep (10000);
  printf ("%2d -- Application exiting normally.\n\n", rank);
  exit (0);
}

/* --------------------------- End of Module ---------------------------- */


