/*
 * 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: timers.c
 * Description: Check that a timer can be programed by different threads
 *
 * Procedure: 1.- Create two tasks.
 *            2.- The first time both arm a timer. Only the last is the owner
 *            and therefore the one that will receive notification of timer expiraition.
 *            3.- The handler for that timer wakes up the other task, allowing it to
 *            program an action for the signal of the timer and set the timer.
 *            4.- The proces is repeated printing the time passed since last timer expiration.
 *            5.- Each time the timer is being programmed by a different thread. 
 * 
 * Author: J. Vidal <jvidal@disca.upv.es>
 */

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

#define MY_SIGNAL RTL_SIGUSR1
#define NTASKS 2
#define MAXITERS 100

pthread_t thread[NTASKS];
timer_t timer;
#define ONESEC 1000*1000*1000
#define ONEMILI 1000*1000
//#define PERIODIC_CLOCK_MODE
#define pthread_kill rtl_pthread_kill

void timer_intr(int sig){
  static int rec=0;

  rtl_printf("Timer handler called for thread %d. Receiving signal:%d %d times\n",pthread_self()->user[0],sig,rec++);
  
  if (pthread_self()->user[0]){
    rtl_printf("I am thread 1, sending the signal to other thread after three shoots.\n"); 
    pthread_wakeup_np(thread[0]);
  } else {
    rtl_printf("I am thread 0, sending the signal to other thread after three shoots.\n"); 
    pthread_wakeup_np(thread[1]);
  }
  
}

void print_info(timer_t timer){
  struct itimerspec current_timer_specs;
  hrtime_t now=0;
  static hrtime_t last_expiration=0;

  now=gethrtime();
  timer_gettime(timer,&current_timer_specs);
  rtl_printf("timer armed. Specs: value:%lu interval:%lu (in nanoseconds)\n",(unsigned long)timespec_to_ns(&(current_timer_specs.it_value)),(unsigned long)timespec_to_ns(&(current_timer_specs.it_interval)));
  rtl_printf("time passed since last expiration:%lu (in nanoseconds)\n",(unsigned long)(now-last_expiration));
  
  last_expiration=now;


}


void *start_routine(void *arg)
{
  struct sched_param p;
  struct itimerspec new_setting,old_setting;
  struct sigaction sa;
  int signal,err=0;
  int count=0;
 
  pthread_self()->user[0]=arg;
  signal=MY_SIGNAL;
  p . sched_priority = 5;
  pthread_setschedparam (pthread_self(), SCHED_FIFO, &p);
  
  sa.sa_handler=timer_intr;
  sa.sa_mask=0;
  sa.sa_flags=0;

  new_setting.it_interval.tv_sec=0;
  new_setting.it_interval.tv_nsec=0;

  new_setting.it_value.tv_sec=0;
  new_setting.it_value.tv_nsec=100*ONEMILI+(unsigned) arg*100*ONEMILI;
  
  if ((err=sigaction(signal,&sa,NULL))<0 ){
    rtl_printf("sigaction(signal,&sa,NULL) FAILING, err:%d.\n",err);
  }
  
  // last that programs succesfully programs it, is the owner.
  err=timer_settime(timer,0,&new_setting,&old_setting);
  rtl_printf("timer_settime(timer,0,&new_setting,&old_setting) by thread %d returns %d\n",(unsigned) arg,err);
  
   while (count ++< MAXITERS) {
     pthread_suspend_np(pthread_self());
     print_info(timer);
     if ((err=sigaction(signal,&sa,NULL))<0 ){
      rtl_printf("sigaction(signal,&sa,NULL) FAILING, err:%d.\n",err);
     } 
    // last that succesfully programs it, is the owner.
    err=timer_settime(timer,0,&new_setting,&old_setting);
    rtl_printf("timer_settime(timer,0,&new_setting,&old_setting) by thread %d returns %d\n",(unsigned) arg,err);
  }


  return (void *)0;  
}


int init_module(void) {
  int ret,i;
  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

  signal.sigev_notify=SIGEV_SIGNAL;
  signal.sigev_signo=MY_SIGNAL;
  ret=timer_create(CLOCK_REALTIME,&signal,&(timer));
  
  if (!ret) {
    rtl_printf("Timer created succesfully\n");
  } else {
    rtl_printf("Error creating timer\n");
    return -1;
  }
  
  // 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;
  
  timer_delete(timer);
  
  for (i=0;i<NTASKS;i++)
    pthread_delete_np (thread[i]);
    
}













