xref: /minix/minix/tests/t40f.c (revision 433d6423)
1 /* t40f.c
2  *
3  * Test timing
4  *
5  * Select works on regular files, (pseudo) terminal devices, streams-based
6  * files, FIFOs, pipes, and sockets. This test verifies selecting with a time
7  * out set.
8  */
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/select.h>
15 #include <sys/wait.h>
16 #include <sys/time.h>
17 #include <time.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <signal.h>
21 
22 #include "common.h"
23 
24 #define DO_HANDLEDATA 1
25 #define DO_PAUSE 3
26 #define DO_TIMEOUT 7
27 #define DO_DELTA 0.5
28 #define MAX_ERROR 5
29 #define DELTA(x,y)  (x.tv_sec - y.tv_sec) * system_hz \
30   + (x.tv_usec - y.tv_usec) * system_hz / 1000000
31 
32 int got_signal = 0;
33 int fd_ap[2];
34 
35 int system_hz;
36 
37 static void catch_signal(int sig_no) {
38   got_signal = 1;
39 }
40 
41 static float compute_diff(struct timeval start, struct timeval end, float compare) {
42   /* Compute time difference. It is assumed that the value of start <= end. */
43   clock_t delta;
44   int seconds, hundreths;
45   float diff;
46 
47   delta = DELTA(end, start); /* delta is in ticks */
48   seconds = (int) (delta / system_hz);
49   hundreths = (int) (delta * 100 / system_hz) - (seconds * 100);
50 
51   diff = seconds + (hundreths / 100.0);
52   diff -= compare;
53   if(diff < 0) diff *= -1; /* Make diff a positive value */
54 
55   return diff;
56 }
57 
58 static void do_child(void) {
59   struct timeval tv;
60 
61   /* Let the parent do initial read and write tests from and to the pipe. */
62   tv.tv_sec = DO_PAUSE + DO_PAUSE + DO_PAUSE + 1;
63   tv.tv_usec = 0;
64   (void) select(0, NULL, NULL, NULL, &tv);
65 
66   /* At this point the parent has a pending select with a DO_TIMEOUT timeout.
67      We're going to interrupt by sending a signal */
68   if(kill(getppid(), SIGUSR1) < 0) perror("Failed to send signal");
69 
70   exit(0);
71 }
72 
73 static void do_parent(int child) {
74   fd_set fds_read;
75   struct timeval tv, start_time, end_time;
76   int retval;
77 
78   /* Install signal handler for SIGUSR1 */
79   signal(SIGUSR1, catch_signal);
80 
81   /* Parent and child share an anonymous pipe. Select for read and wait for the
82    timeout to occur. We wait for DO_PAUSE seconds. Let's see if that's
83    approximately right.*/
84   FD_ZERO(&fds_read);
85   FD_SET(fd_ap[0], &fds_read);
86   tv.tv_sec = DO_PAUSE;
87   tv.tv_usec = 0;
88 
89   (void) gettimeofday(&start_time, NULL);   /* Record starting time */
90   retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
91   (void) gettimeofday(&end_time, NULL);     /* Record ending time */
92 
93   /* Did we time out? */
94   if(retval != 0) em(1, "Should have timed out");
95 
96   /* Approximately right? The standard does not specify how precise the timeout
97      should be. Instead, the granularity is implementation-defined. In this
98      test we assume that the difference should be no more than half a second.*/
99   if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
100     em(2, "Time difference too large");
101 
102   /* Let's wait for another DO_PAUSE seconds, expressed as microseconds */
103   FD_ZERO(&fds_read);
104   FD_SET(fd_ap[0], &fds_read);
105   tv.tv_sec = 0;
106   tv.tv_usec = DO_PAUSE * 1000000L;
107 
108   (void) gettimeofday(&start_time, NULL);   /* Record starting time */
109   retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
110   (void) gettimeofday(&end_time, NULL);     /* Record ending time */
111 
112   if(retval != 0) em(3, "Should have timed out");
113   if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
114     em(4, "Time difference too large");
115 
116   /* Let's wait for another DO_PAUSE seconds, expressed in seconds and micro
117      seconds. */
118   FD_ZERO(&fds_read);
119   FD_SET(fd_ap[0], &fds_read);
120   tv.tv_sec = DO_PAUSE - 1;
121   tv.tv_usec = (DO_PAUSE - tv.tv_sec) * 1000000L;
122 
123   (void) gettimeofday(&start_time, NULL);   /* Record starting time */
124   retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
125   (void) gettimeofday(&end_time, NULL);     /* Record ending time */
126 
127   if(retval != 0) em(5, "Should have timed out");
128   if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
129     em(6, "Time difference too large");
130 
131   /* Finally, we test if our timeout is interrupted by a signal */
132   FD_ZERO(&fds_read);
133   FD_SET(fd_ap[0], &fds_read);
134   tv.tv_sec = DO_TIMEOUT;
135   tv.tv_usec = 0;
136 
137   (void) gettimeofday(&start_time, NULL);   /* Record starting time */
138   retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
139   (void) gettimeofday(&end_time, NULL);     /* Record ending time */
140 
141   if(retval != -1) em(7, "Should have been interrupted");
142   if(compute_diff(start_time, end_time, DO_TIMEOUT) < DO_DELTA)
143     em(8, "Failed to get interrupted by a signal");
144 
145   if(!got_signal) em(9, "Failed to get interrupted by a signal");
146 
147   waitpid(child, &retval, 0);
148   exit(errct);
149 }
150 
151 int main(int argc, char **argv) {
152   int forkres;
153 
154   /* Retrieve actual system frequency. */
155   system_hz = sysconf(_SC_CLK_TCK);
156   /* Get subtest number */
157   if(argc != 2) {
158     printf("Usage: %s subtest_no\n", argv[0]);
159     exit(-2);
160   } else if(sscanf(argv[1], "%d", &subtest) != 1) {
161     printf("Usage: %s subtest_no\n", argv[0]);
162     exit(-2);
163   }
164 
165   /* Set up anonymous pipe */
166   if(pipe(fd_ap) < 0) {
167     perror("Could not create anonymous pipe");
168     exit(-1);
169   }
170 
171   forkres = fork();
172   if(forkres == 0) do_child();
173   else if(forkres > 0)  do_parent(forkres);
174   else { /* Fork failed */
175     perror("Unable to fork");
176     exit(-1);
177   }
178 
179   exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
180 
181 }
182