object

Team-FLY

10.3 Setting One of Five Single Timers

This section describes an implementation for setting one of five possible software timers, using the underlying process interval timer ITIMER_REAL . The main program takes the timer number and the timer interval (in microseconds) as command-line arguments and calls the timerstart function. The main program then waits for the timer to expire, prints out a message that the timer has expired , and exits.

10.3.1 The virtualtimers object

Implement the software timers in an object called virtualtimers . Use a static variable called timers of type timerdata_t to hold the internal timer data for the object as shown below.

 #define MAXTIMERS 5 typedef struct timerdata_t {    long active[MAXTIMERS];    int events[MAXTIMERS];    int numevents;    int running; } timerdata_t; 

The members of timerdata_t have the following meanings.

active

is an array with an entry for each timer. Each entry holds the expiration time (in m sec) relative to the starting time of the running timer. A negative value signifies that the timer is not active. (In this part only one timer is ever active.)

events

is an array with an entry for each timer that has expired and has not yet been removed. The entries contain timer numbers and appear in increasing order of expiration time. (There is at most one timer on the list for the program of this section.)

numevents

is the number of entries in the events array.

running

is the number of the timer that is running or 1 if none are active. The running timer is the one that is next to expire. It is the one whose expiration time causes the one real timer (set with sethardwaretimer ) to generate a signal.

The integer representation of the time intervals simplifies the code but limits the length of the intervals to about 2000 seconds (a little more than half an hour ) for 32-bit integers. This should be more than enough time for testing the algorithms of the project.

Place the timers data structure in virtualtimers.c along with the following functions that are callable from outside the object.

 int getevent(int eventnumber); 

Return the timer number associated with a particular entry in the events array. The eventnumber parameter specifies the position in the events array, which is indexed from 0. The getevent functions returns 1 if eventnumber is negative or greater than or equal to numevents .

 int getnumevents(void); 

Return the value of numevents .

 int getrunning(void); 

Return the timer number of the running timer or 1 if there is no running timer.

 long getvalue(int n); 

Return the current value of a timer n from the active array or 1 if the timer is not active or the timer number is invalid.

 int removetop(void); 

Remove the top event from events and return the event's timer number or 1 if events is empty. This function is needed later when multiple timers are handled.

 int timerinit(void); 

Initialize the timers data structure as shown in Figure 10.2. The function also calls catchsetup of the hardwaretimer object and showinit of the show object. If successful, timerinit returns 0. If unsuccessful , timerinit returns 1 and sets errno .

 void timerstart(int n, long interval); 

Start timer n with the time interval given in microseconds. For this part, assume that no timers are active. The interval is the number of microseconds in the future after which the timer should expire. To start timer n , do the following.

  1. Remove timer n from the event list if it is there.

  2. Set running to timer n .

  3. Set active[n] to the appropriate time value.

  4. Start the interval timer by calling the sethardwaretimer function in the hardwaretimer object.

 void timerstop(int n); 

Stop timer n if it is active and remove the timer from events if it is there. This function is needed later when multiple timers are handled.

 void waitforevent(void); 

Wait until there is an event in events and then return without changing events . Do not use busy waiting, but instead, call waitforinterrupt from the hardwaretimer module.

The virtualtimers object also contains the private timerhandler function, which it passes to the hardware timer module by calling catchsetup in timerinit .

 static void timerhandler(void); 

Handle the timer signal. This function is called by the actual signal handler in hardwaretimer to maintain the timers structure when the real hardware timer expires . Do the following steps in timerhandler .

  1. Add the running timer to the end of events .

  2. Make the running timer inactive.

  3. Update the timers data structure.

  4. Reset the interval timer if there is an active timer. (There will not be one in the single-timer case.)

Since the hardwaretimer object handles the signals, it must contain the actual signal handler. The prototype of the signal handler may depend on the implementation and should not be part of the virtualtimers object. Since the timers must be manipulated when the signal is caught, this work should be done in the virtualtimers object. The real signal handler calls timerhandler to do this. Since timerhandler has internal linkage, the timerinit function passes a reference to it when calling catchsetup in the hardwaretimer object. The timerhandler is an example of a callback . Callbacks are frequently used by applications to request that a service call one of the application's functions when some event occurs.

10.3.2 The hardwaretimer object

The hardwaretimer object contains code to handle a single "hardware" timer. The functions that are accessible from outside the object are as follows .

 int blockinterrupt(void); 

Block the SIGALRM signal. The blockinterrupt function returns 1 if the signal was already blocked and 0 otherwise .

 int catchsetup(void (*handler)(void)); 

Set up a signal handler to catch the SIGALRM signal by calling sigaction . If successful, catchsetup returns 0. If unsuccessful, catchsetup returns 1 and sets errno . The handler parameter is the name of the function that does the work of handling the signal. The actual signal handler in hardwaretimer just calls the handler function. The virtualtimers object calls the function catchsetup to set up signal handling.

 long gethardwaretimer(void); 

Return the time remaining on the hardware timer if it is running or 0 if it is not running. If unsuccessful, gethardwaretimer returns 1 and sets errno . Use getitimer to implement this function.

 int isinterruptblocked(void); 

Return 1 if the SIGALRM signal is blocked and 0 otherwise.

 void sethardwaretimer(long interval); 

Start the ITIMER_REAL timer running with the given interval in microseconds. Call sethardwaretimer only when the timer interrupt is blocked or the interval timer is stopped . The interval parameter specifies the interval for setting the timer in microseconds. Use setitimer to implement this function.

 void stophardwaretimer(void); 

Stop the hardware timer if it is running. This function is harder to implement than it might seem. We discuss this later since it is not needed in this section.

 void unblockinterrupt(void); 

Unblock the SIGALRM signal.

 void waitforinterrupt(void); 

Call sigsuspend to wait until a signal is caught. The waitforinterrupt function does not guarantee that the signal was from a timer expiration. This function is normally entered with the timer signal blocked. The signal set used by sigsuspend must not unblock any signals that were already blocked, other than the one being used for the timers. If the main program has blocked SIGINT , the program should not terminate if Ctrl-C is entered.

Some of these functions are not needed until a later part of this project. The interface to the hardware timer is isolated in this file, so using POSIX:TMR timers or a different underlying timer than ITIMER_REAL only requires changing these functions. Define a header file called hardwaretimer.h that has the prototypes of the functions in the hardwaretimer object.

10.3.3 Main program implementation

Write a main program called timermain that initializes everything by calling timerinit and then loops , reading from standard input until an error or end-of-file occurs. Specifically, timermain does the following tasks in the loop.

  1. Read a pair of integers (a timer number and an interval in microseconds) from standard input.

  2. Call timerstart with these values.

  3. Call waitforevent .

  4. Print the return value of waitforevent to standard output.

Use scanf to read in the values from standard input.

10.3.4 Instrumentation of the timer code with show

Code with signal handlers and timers is hard to test because of the unpredictable nature of the events that drive the program. A particular timing of events that causes an error might occur rarely and not be easily reproducible. Furthermore, the behavior of the program depends not only on the input values but also on the rate at which input data is generated.

This section describes how to instrument the code with calls to a show function as a preliminary step in testing. This instrumentation is critical for debugging the later parts of the project. Two versions of the show function are presented here: one outputs to standard output and the other uses remote logging. This subsection explains what show does and how to use it in the program.

The prototype for show is as follows.

 void show(int traceflag, const char *msg, long val1, long val2,              int blockedflag); 

If the traceflag is 0, show does nothing, allowing you to easily remove the debugging output. If traceflag is 1, the show function displays the message in the second parameter and the status of the timer data structure. The show function displays the val1 and val2 parameters if they are nonnegative. Usually, these parameters will represent a timer number and an interval in microseconds, but sometimes they will represent two timers. The blockedflag is 1 if the timer signal is supposed to be blocked when the call is made and 0 if the timer signal should not be blocked. It will be important to keep track of the blocking and unblocking of the signal in the complete timer implementation.

The virtualtimers file should have a traceflag global variable initialized to 1. Insert a call to showinit in the timerinit function of the virtualtimers module. Insert calls to show liberally throughout the virtualtimers module. For example, the first line of timerstart could be the following.

 show(traceflag, "Timer Start Enter", n, interval, 0); 

A call to start timer [3] for 1,000,000 microseconds might then produce the following output.

 ****  4.0067: Timer Start Enter 3 1000000 U(2,5.000) A:(2,5.000) (4,9.010) (1E 4) 

The fields are as follows.

  • 4.0067 is the time in seconds since showinit was called.

  • The message states where the show function was called.

  • 3 is the timer being started.

  • 1000000 is the duration of the timer interval.

  • U indicates that the call was made with the interrupt unblocked.

  • (2,5.000) gives the currently running timer and its interval in seconds.

  • A:(2,5.000) (4,9.010) shows two active timers and their corresponding intervals.

  • (1E 4) indicates one event for timer [4] .

Program 10.1 can be used with this project to display messages similar to the one above.

Program 10.1 show.c

A version of show that prints to standard output.

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> #include "hardwaretimer.h" #include "show.h" #include "virtualtimers.h" #define MILLION 1000000L static double initialtod = 0.0; static int maxtimers; static double gettime(void); static double timetodouble(long interval); static double getrelativetime(void) {    /* seconds since showinit was called */    return gettime() - initialtod; } static double gettime(void) {    /* seconds since January 1, 1970 as a double */    double thistime = 0.0;    struct timeval tval;    if (gettimeofday(&tval, NULL))       fprintf(stderr, "Failed to get time of day\n");    else       thistime = tval.tv_sec + (double)tval.tv_usec/MILLION;    return thistime; } static void showtimerdata(void) {       /* display the timers data structure */    int i;    printf("(%d,%.3f) A:", getrunning(),       timetodouble(getvalue(getrunning())));    for (i = 0; i < maxtimers; i++)       if (getvalue(i) >= 0)          printf("(%d,%.3f) ", i, timetodouble(getvalue(i)));    printf(" (%dE", getnumberevents());    for (i = 0; i < getnumberevents(); i++)       printf(" %d", getevent(i));    printf(")\n"); } static double timetodouble(long interval) {        /* microseconds to seconds */    return (double)interval/MILLION; } /* ------------------------Public Functions --------------------------------- */ void show(int traceflag, const char *msg, long val1, long val2,              int blockedflag) {    /* displays timers with message for evtype */    int wasblockedflag;    if (!traceflag)       return;    wasblockedflag = blockinterrupt();    printf("**** %8.4f: ", getrelativetime());    printf("%s ",msg);    if (val1 >= 0)       printf("%ld ", val1);    if (val2 >= 0)       printf("%ld ", val2);    if (blockedflag)       printf("B");    else       printf("U");    if (blockedflag != wasblockedflag)       printf("***");    showtimerdata();    fflush(stdout);    if (!wasblockedflag)       unblockinterrupt(); } void showinit(int maxt) {      /* set initialtod to seconds since Jan 1, 1970 */    initialtod = gettime();    maxtimers = maxt; } 

Put the code of Program 10.1 in a separate file. Instrument the timer functions so that each time something of interest occurs, the program calls show with the appropriate parameters. For this part, just insert the following four lines.

  • In the first line of timerhandler insert the following.

     show(traceflag, "Timer Handler Enter", timers.running, -1, 1); 
  • Before returning from timerhandler insert the following.

     show(traceflag, "Timer Handler Exit", timers.running, -1, 1); 
  • Before the first line of timerstart insert the following.

     show(traceflag, "Timer Start Enter", n, interval, 0); 
  • Before returning from timerstart insert the following.

     show(traceflag, "Timer Start Exit", n, interval, 0); 

Test the program with a variety of appropriate inputs and observe the output of show . Remember that printf is not async-signal safe. The calls to show in timerhandler cause a problem if timermain also uses the standard I/O library without blocking the signals during the calls. The show function blocks the timer interrupt before producing any output to avoid this problem as well as to protect the shared timers data structure.

Program 10.2 gives an alternative implementation of show that uses the remote logging facility described in Appendix D.2. It avoids a possible buffer overflow by calling snprintfappend to add to the message. This function takes parameters similar to those of snprintf but appends to a string given by the first parameter. The second parameter is a limit on the total size of the buffer used to hold the string.

In this version, the showinit function opens a connection to the remote logger, using the default parameters. Each output message is associated with a generator string indicating the source of the message. The generator is just the timer gotten from the val1 parameter. The output message has the following fields separated by tabs so they can be displayed in a table.

  • The message from the msg parameter.

  • val1 (the timer).

  • val2 (a second timer or an interval).

  • The letter U if the blockedflag parameter is 0 and the letter B otherwise. If this does not correspond to the actual blocked state of the timer signal, this is followed by three asterisks as a warning.

  • The number of the currently running timer if any.

  • A list of all active timers, each being represented by an ordered pair consisting of the timer number and the remaining time relative to the running timer.

  • The number of events followed by the list of events.

Figure 10.9 on page 363 shows sample output from one window of the remote logger.

Program 10.2 showremote.c

A version of show that uses a remote logging facility.

 #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> #include "hardwaretimer.h" #include "rlogging.h" #include "show.h" #include "virtualtimers.h" #define MILLION 1000000L #define MSGBUFSIZE 256 static double initialtod = 0.0; static LFILE *lf; static int maxtimers; static double gettime(void); static double timetodouble(long interval); static void snprintfappend(char *s, size_t n, const char *fmt, ...) {    va_list ap;    int sizeleft;    sizeleft = n - strlen(s) - 1;    if (sizeleft <= 0)       return;    va_start(ap, fmt);    vsnprintf(s + strlen(s), sizeleft, fmt, ap); } static void createlogstring(char *msg, int n) {       /* create string to log */    int i;    if (getrunning() >= 0)       snprintfappend(msg, n, "\t%d\t", getrunning());    else       snprintfappend(msg, n, "\t\t");    for (i = 0; i < maxtimers; i++)       if (getvalue(i) >= 0)          snprintfappend(msg, n, "(%d,%.3f) ",                  i, timetodouble(getvalue(i)));    snprintfappend(msg, n, "\t (%dE", getnumberevents());    for (i = 0; i < getnumberevents(); i++)       snprintfappend(msg, n, " %d", getevent(i));    snprintfappend(msg, n, ")\n"); } static double getrelativetime(void) {    /* seconds since showinit was called */    return gettime() - initialtod; } static double gettime(void) {    /* seconds since January 1, 1970 as a double */    double thistime = 0.0;    struct timeval tval;    if (gettimeofday(&tval, NULL))       fprintf(stderr, "Warning, cannot get time of day\n");    else       thistime = tval.tv_sec + (double)tval.tv_usec/MILLION;    return thistime; } static double timetodouble(long interval) {        /* microseconds to seconds */    return (double)interval/MILLION; } /* ------------------------Public Functions --------------------------------- */ void showinit(int maxt) {      /* set initialtod to seconds since Jan 1, 1970 */    initialtod = gettime();    maxtimers = maxt;    lf = lopen(NULL, 0);    if (lf == NULL)       fprintf(stderr,"Cannot open remote logger\n");    else       lsendtime(lf); } void show(int traceflag, const char *msg, long val1, long val2,              int blockedflag) {         /* log timers with message for evtype */    char genbuf[20];    char msgbuf[MSGBUFSIZE];    int wasblockedflag;    if (!traceflag)       return;    wasblockedflag = blockinterrupt();    if (val1 < 0)       genbuf[0] = 0;    else       sprintf(genbuf, "Timer %ld", val1);    snprintf(msgbuf, MSGBUFSIZE, "%8.4f: ", getrelativetime());    snprintfappend(msgbuf, MSGBUFSIZE, "%s", msg);    if (val1 >= 0)       snprintfappend(msgbuf, MSGBUFSIZE, "\t%ld", val1);    else       snprintfappend(msgbuf, MSGBUFSIZE, "%s", "\t");    if (val2 >= 0)       snprintfappend(msgbuf, MSGBUFSIZE, "\t%ld", val2);    else       snprintfappend(msgbuf, MSGBUFSIZE, "%s", "\t");    if (blockedflag)       snprintfappend(msgbuf, MSGBUFSIZE, "%s", "\tB");    else       snprintfappend(msgbuf, MSGBUFSIZE, "%s", "\tU");    if (blockedflag != wasblockedflag)       snprintfappend(msgbuf, MSGBUFSIZE, "%s", "***");    createlogstring(msgbuf, MSGBUFSIZE);    lprintfg(lf, genbuf, msgbuf);    if (!wasblockedflag)       unblockinterrupt(); } 
Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net