/************** timers.h *************/
/*
* Title: Answer to excersice 10.5 of APUE - Software timer
* By: Leon
*
* This implementation is similar to Don Libes's, and I modify
* it according to my thought. This timer is not very precise.
*/
#ifndef _APUE_TIMER_
#define _APUE_TIMER_
#include
#include
#include
#include
#include
#include
#define TRUE 1
#define FALSE 0
#define MAX_TIMERS 128 /* number of timers */
#define VERY_LONG_TIME INT_MAX /* longest time possible */
/*
* no return value, the function takes
* only one parameter for general purpose.
*/
typedef void timer_handler(void *arg);
struct __apue_timer
{
int inuse; /* TRUE if in use */
int time_wait; /* relative time to wait */
timer_handler *handler; /* called when the timer has expired */
void *arg; /* argument of handler */
};
typedef struct __apue_timer ATIMER;
/*
* initialize the timers array.
* This function must be called first.
* returns 0 on success and -1 on error.
*/
int timer_init(void);
/*
* stop and cancel all timers.
* after this function was called, don't use functions
* declared here before timer_init() was called.
*/
void timer_cancel(void);
/*
* Function : decalre a timer
* Parameter : time_set sets the time, timer_handler is a function pointer
* points to the function wanted to call when the timer has expired.
* Return value : upon successful return, the function returns a pointer points to
* the declared timer. Otherwise, return NULL.
*/
ATIMER *timer_declare(int time_set, timer_handler *handler, void *arg);
/*
* Function : undeclare a timer
* Parameter : a pointer points to a declared timer
* Return value : returns 0 on success and -1 on error
*/
int timer_undeclare(ATIMER *t);
/*
* Function : update timers
* Return value : a pointer points to the timer which will expire next
* or NULL if no timers.
*/
ATIMER *timer_update();
/* called when a timer has expired */
void timer_out_handler(int signo);
#endif
/*********** timers.c *************/
#include "timers.h"
/* I prefer to use array instead of linked list, which is more brevity and simpler */
static ATIMER apue_timers[MAX_TIMERS]; /* array of timers */
static ATIMER *next_timer = NULL; /* point to the next expired timer */
static volatile int time_cur = 0, time_set = 0; /* record the time */
static struct sigaction act; /* new sigaction */
static struct sigaction oldact; /* old sigaction */
static sigset_t set, old_set;
static void disable_interrupt(void)
{
sigfillset(&set);
sigdelset(&set, SIGQUIT);
sigdelset(&set, SIGALRM);
if(sigprocmask(SIG_SETMASK, &set, &old_set) < 0)
fprintf(stderr, "sigprocmask error\n");
}
static void enable_interrupt(void)
{
if(sigprocmask(SIG_SETMASK, &old_set, NULL) < 0)
fprintf(stderr, "sigprocmask error\n");
}
int timer_init(void)
{
int i;
sigset_t mask;
disable_interrupt();
/* initialize timers array */
for(i = 0; i < MAX_TIMERS; i++)
apue_timers[i].inuse = FALSE;
/* initialize sigaction */
sigfillset(&mask);
sigdelset(&mask, SIGQUIT);
sigdelset(&set, SIGALRM);
act.sa_mask = mask;
act.sa_handler = timer_out_handler;
if(sigaction(SIGALRM, &act, &oldact) < 0)
{
fprintf(stderr, "sigaction error\n");
enable_interrupt();
return -1;
}
enable_interrupt();
return 0;
}
void timer_cancel(void)
{
int i;
disable_interrupt();
alarm(0);
for(i = 0; i < MAX_TIMERS; i++)
apue_timers[i].inuse = FALSE;
/* reset sigaction */
if(sigaction(SIGALRM, &oldact, NULL) < 0)
{
fprintf(stderr, "sigaction error\n");
enable_interrupt();
}
enable_interrupt();
}
ATIMER *timer_declare(int time_set, timer_handler *handler, void *arg)
{
ATIMER *new_timer;
disable_interrupt();
/* find a timer not inuse */
for( new_timer = apue_timers; new_timer < &apue_timers[MAX_TIMERS] ; new_timer++)
{
if(!new_timer->inuse)
break;
}
/* no timer available */
if(new_timer == &apue_timers[MAX_TIMERS])
{
enable_interrupt();
return NULL;
}
/* found a timer */
new_timer->inuse = TRUE;
new_timer->time_wait = time_set;
new_timer->handler = handler;
new_timer->arg = arg;
/* update timers */
next_timer = timer_update();
/* set next timer */
if(!next_timer || new_timer->time_wait < next_timer->time_wait)
{
next_timer = new_timer;
alarm(0);
alarm(next_timer->time_wait);
}
enable_interrupt();
return new_timer;
}
int timer_undeclare(ATIMER *t)
{
disable_interrupt();
/* check parameter t */
if(t < apue_timers || t >= &apue_timers[MAX_TIMERS] || !t->inuse)
{
enable_interrupt();
return -1;
}
t->inuse = FALSE;
/* reset alarm if t is next_timer */
if(t == next_timer)
{
alarm(0);
next_timer = timer_update();
}
enable_interrupt();
return 0;
}
ATIMER *timer_update()
{
int decrement;
int flag = FALSE;
ATIMER *t;
ATIMER temp_timer = { 0, VERY_LONG_TIME, NULL };
disable_interrupt();
/* update time */
if(time_set == 0)
{
time_set = times(NULL);
decrement = 0;
}
else
{
time_cur = times(NULL);
/* get decrement and convert to seconds */
decrement = (time_cur - time_set) / sysconf(_SC_CLK_TCK);
/* printf("decrement = %d\n", decrement);/* !!for debug!! */
assert(decrement >= 0); /* someting may happen */
time_set = time_cur; /* reset time_set */
}
/* reset next_timer */
next_timer = &temp_timer;
/* update timers and get next timer */
for(t = apue_timers; t < &apue_timers[MAX_TIMERS]; t++)
{
if(t->inuse)
{
if(decrement < t->time_wait)
t->time_wait -= decrement;
if(t->time_wait < next_timer->time_wait)
next_timer = t;
flag = TRUE;
}
}
/* reset alarm */
alarm(0);
if(flag)
{
alarm(next_timer->time_wait);
}
else
{
next_timer = NULL;
time_cur = time_set = 0;
}
enable_interrupt();
return next_timer;
}
void timer_out_handler(int signo)
{
/* call user hanler */
next_timer->handler(next_timer->arg);
/* update timers */
next_timer->inuse = FALSE;
next_timer = timer_update();
}