/*
 * POSIX.4 Timers test program
 *
 * Written by J. Vidal
 * Copyright (C) Dec, 2002 OCERA Consortium.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * published by the Free Software Foundation version 2.
 *
 * Name: simple_test.c
 * Description: Simple program to test timers API. 
 *
 * Procedure: 1.- Create a user-defined number of tasks.
 *            2.- Make periodic them, with a period of 2 minutes.
 *            3.- Arm timers for each task. The firs tasks arms a
 *            one-shot timer and the others repeating timers with 
 *            an interval of 1 second.
 *            4.- Install a handler for each timer that wakes up each
 *            task every timer expiration.
 *            5.- Check that task period changes (for all tasks but the first)
 *            from 2 minutes to 1 second due to timers expirations and handlers executions.
 * 
 * Author: J. Vidal <jvidal@disca.upv.es>  
*/





#include <rtl.h>
#include <pthread.h>
#include <time.h>
#include <signal.h>

#define MY_SIGNAL RTL_SIGUSR1
#define NTASKS 2

pthread_t thread[NTASKS];
timer_t timer[NTASKS];
#define ONESEC 1000*1000*1000
#define ONEMILISEC 1000*1000
#define MILISECS_PER_SEC 1000
#define PERIODIC_CLOCK_MODE
hrtime_t start_time;

void timer_intr(int sig){
  rtl_printf("\nTimer handler called for signal:%d\n",sig);
  pthread_wakeup_np(pthread_self());
}

void *start_routine(void *arg)
{
  struct sched_param p;
  struct itimerspec new_setting,old_setting,current_timer_specs;
  struct sigaction sa;
  long long period=(long long) 120*1000*1000*1000;
  hrtime_t now=0,last_expiration=0;
  int signal,err=0,param=(unsigned)arg;

  signal=MY_SIGNAL+param;
  p . sched_priority = 5;
  pthread_setschedparam (pthread_self(), SCHED_FIFO, &p);
  
  sa.sa_handler=timer_intr;
  sa.sa_mask=0;
  sa.sa_flags=0;

  /* if arg== 0 -> one-shot timer. */
  new_setting.it_interval.tv_sec=1*(unsigned)arg; 
  new_setting.it_interval.tv_nsec=0;

  new_setting.it_value.tv_sec=1;
  new_setting.it_value.tv_nsec=start_time;

  
  if ((err=sigaction(signal,&sa,NULL))<0 ){
    rtl_printf("sigaction(signal,&sa,NULL) FAILING, err:%d.\n",err);
  }
  
  err=timer_settime(timer[param],0,&new_setting,&old_setting);
  rtl_printf("timer_settime(timer[%d],0,&new_setting,&old_setting) returns %d\n",(unsigned) arg,err);
  
  pthread_make_periodic_np (pthread_self(), gethrtime() ,period );

  if (!param) rtl_printf("This is a one-shot timer\n");

  while (1) {
    last_expiration=now;
    now=gethrtime();
    timer_gettime(timer[param],&current_timer_specs);
    rtl_printf("timer %d armed. Specs: value:%d interval:%d (in milis)\n",param,(int)(timespec_to_ns(&current_timer_specs.it_value)/MILISECS_PER_SEC),(int)(timespec_to_ns(&current_timer_specs.it_interval)/MILISECS_PER_SEC));
    rtl_printf("time passed since last expiration:%d (in milis)\n",(int)(now-last_expiration)/MILISECS_PER_SEC);
    
    pthread_wait_np();
   }
  
}


int init_module(void) {
  int i,ret;
  sigevent_t signal; 
#ifdef CLOCK_MODE_PERIODIC
  hrtime_t period=1000*1000; // 1 milisecond.
  clockid_t cl=rtl_getschedclock();
  rtl_setclockmode(cl,RTL_CLOCK_MODE_PERIODIC,period); 
#endif

  for (i=0;i<NTASKS;i++){    
    signal.sigev_notify=SIGEV_SIGNAL;
    if ((signal.sigev_signo=MY_SIGNAL+i)>RTL_MAX_SIGNAL) signal.sigev_signo=RTL_MAX_SIGNAL;
    ret=timer_create(CLOCK_REALTIME,&signal,&(timer[i]));
    
    if (!ret) rtl_printf("Timer %d created succesfully\n",i);
    else 
      {
	rtl_printf("Error creating timer %d.\n",i);
	return -1;
      }
  }  
  
  start_time=ONEMILISEC;

  // Threads creation.
  for (i=0;i<NTASKS;i++)
    pthread_create (&(thread[i]), NULL, start_routine,(void *) i); 
  
  return 0;
}
void cleanup_module(void) {
  int i;
  
  for (i=0;i<NTASKS;i++)
    timer_delete(timer[i]);
  
  for (i=0;i<NTASKS;i++)
    pthread_delete_np (thread[i]);
    
}
