    /*********************************************************************

                   Copyright (c) 2007 SKY Computers, Inc.

    Including this file, and any software and its file formats and  visual
    displays described herein, all  rights  reserved,  may  only  be  used
    pursuant  to  the  applicable  software  license  agreement;  contains
    confidential and proprietary information  of SKY  and/or third parties
    which is protected by copyright trade secret and trademark law and may
    not  be  provided or otherwise made available  without  prior  written
    authorization.

                       Restricted Rights Legend

    Use,  duplication,  or disclosure  by  the  Government  is  subject to
    restrictions as set  forth in subdivision  (c)(1)(ii) of the Rights in
    Technical Data and Computer  Software  clauses at  DFARS  252.227-7013
    (October 1988) and FAR 52.227-19(c) (June 1987).

    *********************************************************************/

/*
 *--------------------------------------------------------------------------
 *
 >++
 *
 *   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 "re_tcb.h"

#define PASSES  (500)

/* Globals */
TCB *TcbBgn;

/*
 *  TimeTrac variables -- Use ifdef's if we want to delete these when
 *  TimeTrac is not compiled into the executable. See Makefile
 */
#ifdef TIME_TRAC
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_wait_bgn, tt_wait_end;
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;
TTEventHandle MN_thread_cnt;
#endif  /* TIME_TRAC */

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

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

/*
 >++
 *
 *   void tt_init_mn (iMyRank): 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[80];
  
  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], "c. Sum", "Red", 1, &MN_runb_bgn, &MN_runb_end);
  time_trac_reg_single_event (Trace_Log[rank], "x. Thread", "Blue", 1, &MN_thread_cnt);
}

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

/*
 >++
 *
 *   void tt_init_co (TCB *tcb): 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], "send_tt", "Blue",
				      group_id, &tt_vsub_bgn[tcb->Rank], &tt_vsub_end[tcb->Rank]);
  VERIFY_TIMETRAC_STATUS (status);
}

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

/*
 >++
 *
 *   void tt_init_sum (TCB *tcb): Set up the TimeTrac file (open the file, 
 *       define the range events) for the output thread (it does no real
 *       output, just anothe 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], "recv_tt", "Green",
				      group_id, &tt_wait_bgn, &tt_wait_end);
  VERIFY_TIMETRAC_STATUS (status);

  status = time_trac_reg_range_event (Trace_Log[tcb->Rank], "x. Do sum", "Blue",
				      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, struct task_block *t, RCS_params *p): 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;
  
  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]);
            }
        }
    }
}

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

/*
 >++
 *
 *   void sig_ack (int s): Signal handler for these examples. Some signals
 *       go into an endless spin loop. Other 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): This is the master thread
 *   that starts all of the other 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;
  char ErrStr[128];
  
  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 ("%2d -- Starting application.\n", rank);

  ex_mem_init (rank);
  TcbBgn = (TCB *) ex_mem_alloc (rank, sizeof(TCB) * NUM_THREADS, 64, "Thread Control Blocks");
  tcb = &TcbBgn[rank];
  t_set_common (0, tcb, ex_params);
  epb = (EPB *) ex_mem_alloc (rank, sizeof(TCB) * NUM_THREADS, 64, "Thread Parameter Block");

  /* Set up the interrupy handles. */
  take_sigs ();
  
  /* 
   *  Start all of the compute threads.
   */
  for (i = 1; i <= NUM_CO_THREADS; i++)
    {
      /* Set parameters for this thread ...  */
      tcb = &TcbBgn[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 = &TcbBgn[i];
  t_set_common (i, tcb, ex_params);
  tcb->Rank = i;
      
  /* Set up the TimeTrac parameters. */
  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 startup by entering the init done (INITD) state.
   */
  for (i = 1; i < NUM_THREADS; i++)
    {
      tcb = &TcbBgn[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 = &TcbBgn[i];
	  tcb->Pass = pass;
	  tcb->Status = STAT_NONE;
	  tcb->Comm = COMM_COMP;
        }

      if ((pass % 31) == 30)
	{
	  printf ("%2d -- Pass = %d.\r", rank, pass);
	  fflush (stdout);
	}
      	  
      /*
       *  Now wait for each compute thread to finish.
       */
      for (i = 1; i <= NUM_CO_THREADS; i++)
	{
	  tcb = &TcbBgn[i];
	  time_trac_record (Trace_Log[rank], MN_thread_cnt, i);
	  if (tcb->Status != STAT_DONE)
	    {

	      while (tcb->Status != STAT_DONE)
		{
		  sched_yield ();
		}
	    }
	}
      time_trac_record (Trace_Log[rank], MN_thread_cnt, 0);
      
      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 = &TcbBgn[1+NUM_CO_THREADS];
      tcb->Pass = pass;
      tcb->Status = STAT_NONE;
      tcb->Comm = COMM_COMP;
      
      /* Wait for it to finish computing, .... */
      while (tcb->Status != STAT_DONE)
	{
	  sched_yield ();
	}

      /* Insert a bug into the code .... */
      if (pass == 123)
	{
	  /* Start the last thread. */
	  tcb = &TcbBgn[1+NUM_CO_THREADS];
	  tcb->Pass = pass;
	  tcb->Status = STAT_NONE;
	  tcb->Comm = COMM_COMP;
	  
	  /* Wait for it to finish computing, .... */
	  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.
   */
  time_trac_record (Trace_Log[0], MN_init_bgn, 0);

  for (i = 1; i < NUM_THREADS; i++)
    {
      /* Set ..  */
      tcb = &TcbBgn[i];
      while (tcb->Status == STAT_RUN)
	{
	  usleep (1000);
	}
    }
  
  /* 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);
  
  /*  Save the TimeTrac files and tell all threads to exit.  */
  for (i = 0; i < NUM_THREADS; i++)
    {
      tcb = &TcbBgn[i];
      tt_term (tcb);
      tcb->Comm = COMM_EXIT;
    }
  
  usleep (10000);
  printf ("%2d -- Application exiting normally.\n", rank);
  usleep (10000);
  exit (0);
}

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


