/*
 * 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: signals_bandwidth.c
 * Description: Measures POSIX.1 Signals bandwidth.
 *                                                  
 * Procedure: 1.- Create two threads, a parent and a child.
 *            2.- During an interval of time send signals between them.
 *            3.- Measure signals send/received per miliseconds.
 *
 * Author: J. Vidal <jvidal@disca.upv.es>
 * Project: OCERA.
 * Further details about this project can be obtained at
 *    http://www.ocera.org

 */     

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

static pthread_t th_child, th_parent;
static timer_t timer;

unsigned int nsigs_sent_by_child= 0, nsigs_recv_by_child =0;
unsigned int nsigs_sent_by_parent= 0, nsigs_recv_by_parent =0;

#define MY_CLOCK CLOCK_REALTIME
#define MAXTIME_SENDING_SIGNALS 1*1000*1000LL
// one minit.
#define MAXITERS 1
struct itimerspec new_setting;
//#define pthread_kill rtl_pthread_kill
/* 
   Sent signals between two threads using POSIX signals 
   (no POSIX.4 real-time queued signals). 
*/
/* handlers */
static void child_handler(int signo){
  nsigs_recv_by_child++;
}

static void parent_handler(int signo){
  nsigs_recv_by_parent++;
}

static void child_terminate(int signo){
  rtl_printf("child-> send:%d, received:%d\n received/milisec:%d \n", nsigs_sent_by_child, nsigs_recv_by_child ,nsigs_recv_by_child);
  rtl_printf("Child thread, about to end\n");
  pthread_exit(NULL);
}

static void alarm_handler(int signo){  
  rtl_sigset_t sigset,oset;
  
  rtl_sigfillset(&sigset);
  pthread_sigmask(SIG_SETMASK,&sigset,&oset);
  pthread_wait_np();

  rtl_printf("TEST ENDING. DISARMING TIMER AND TERMINATING THREADS.\n");
  new_setting.it_value.tv_sec=0;
  new_setting.it_value.tv_nsec=0;
  timer_settime(timer,0,&new_setting,NULL);
  pthread_kill(th_child,RTL_SIGRTMIN+1);
  rtl_printf("Parent thread, about to end\n");
  pthread_exit(NULL);
  
}

void *be_a_child(void *arg){
  struct sigaction sa;
  rtl_sigset_t sigset;
  struct sched_param p;
  int param=(unsigned) arg;
  
  p . sched_priority =param;
  pthread_setschedparam (pthread_self(), SCHED_FIFO, &p);
  
  rtl_sigfillset(&sigset);
  rtl_sigdelset(&sigset,RTL_SIGRTMIN);
  rtl_sigdelset(&sigset,RTL_SIGRTMIN+1);
  pthread_sigmask(SIG_SETMASK,&sigset,NULL);
  
  sa.sa_handler=child_handler;

  if (sigaction(RTL_SIGRTMIN,&sa,NULL)<0){
    rtl_printf("Error in sigaction(RTL_SIGRTMIN,..);\n");
    return (void *)-1;
  }
  
  sa.sa_handler=child_terminate;

  if (sigaction(RTL_SIGRTMIN+1,&sa,NULL)<0){
    rtl_printf("Error in sigaction(RTL_SIGRTMIN+1,..);\n");
    return (void *)-1;
  }

  
  while(1){
    sigsuspend(&sigset);
    pthread_kill(th_parent,RTL_SIGUSR1);
    nsigs_sent_by_child++;
  }
  
  return (void *)0;

}

void *be_a_parent(void *arg){
  struct sigaction sa;
  rtl_sigset_t sigset;
  struct sched_param p;
  int param=(unsigned) arg;
  
  p . sched_priority =param;
  pthread_setschedparam (pthread_self(), SCHED_FIFO, &p);

  rtl_sigfillset(&sigset);
  rtl_sigdelset(&sigset,RTL_SIGUSR1);
  rtl_sigdelset(&sigset,RTL_SIGUSR2);
  pthread_sigmask(SIG_SETMASK,&sigset,NULL);
  
  sa.sa_handler=parent_handler;

  if (sigaction(RTL_SIGUSR1,&sa,NULL)<0){
    rtl_printf("Error in sigaction(RTL_SIGUSR1,..);\n");
    return (void *)-1;
  }
  
  sa.sa_handler=alarm_handler;

  if (sigaction(RTL_SIGUSR2,&sa,NULL)<0){
    rtl_printf("Error in sigaction(RTL_SIGUSR2,..);\n");
    return (void *)-1;
  }

 
  pthread_make_periodic_np(pthread_self(),gethrtime(),MAXTIME_SENDING_SIGNALS*2);
  pthread_wait_np();

  timer_settime(timer,0,&new_setting,NULL);
  while (1){
    pthread_kill(th_child,RTL_SIGRTMIN);
    sigsuspend(&sigset);
    nsigs_sent_by_parent++;
  }
  
  return (void *)0;

}


int init_module(void){
  sigevent_t signal; 
  int ret=0;

  new_setting.it_interval.tv_sec=0;
  new_setting.it_interval.tv_nsec=MAXTIME_SENDING_SIGNALS;;
  new_setting.it_value.tv_sec=0;
  new_setting.it_value.tv_nsec=MAXTIME_SENDING_SIGNALS;
  
  
  signal.sigev_notify=SIGEV_SIGNAL;
  signal.sigev_signo=RTL_SIGUSR2;
  ret=timer_create(MY_CLOCK,&signal,&(timer));
  
  if (!ret) rtl_printf("Timer created succesfully\n");
  else {
    rtl_printf("Error creating timer. errno=%d\n",errno);
    return -1;
  }
  
  pthread_create(&th_child,NULL,be_a_child,(int *) 1);
  pthread_create(&th_parent,NULL,be_a_parent,(int *) 1);
  
  return ret;
}



void cleanup_module(void) {
  timer_delete(timer);
  pthread_delete_np (th_child);
  pthread_delete_np (th_parent);
}

