APUE 习题10.5 - Implementing Software Timer

来源:互联网 发布:安知鸿鹄之志上一句 编辑:程序博客网 时间:2024/06/04 17:49

这是我自己实现的版本,参考 Don Libes 的 Implementing Software Timer

我是用alarm实现的,好像不太精确,还有一个版本可以设定定时器的次数,可是随着次数增多精确度就成了不可忽略的问题了。

不排除有很多BUG, 而且有些细节没考虑。

/************** 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 <stdio.h>#include <limits.h>#include <signal.h>#include <sys/times.h>#include <time.h>#include <assert.h>#include <unistd.h>#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();}