Team-FLY |
10.3 Setting One of Five Single TimersThis 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 objectImplement 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.
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);
int getnumevents(void);
int getrunning(void);
long getvalue(int n);
int removetop(void);
int timerinit(void);
void timerstart(int n, long interval);
void timerstop(int n);
void waitforevent(void);
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);
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 objectThe 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);
int catchsetup(void (*handler)(void));
long gethardwaretimer(void);
int isinterruptblocked(void);
void sethardwaretimer(long interval);
void stophardwaretimer(void);
void unblockinterrupt(void);
void waitforinterrupt(void);
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 implementationWrite 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.
Use scanf to read in the values from standard input. 10.3.4 Instrumentation of the timer code with showCode 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.
Program 10.1 can be used with this project to display messages similar to the one above. Program 10.1 show.cA 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.
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.
Figure 10.9 on page 363 shows sample output from one window of the remote logger. Program 10.2 showremote.cA 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 |