xref: /minix/minix/tests/t40f.c (revision fb9c64b2)
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 + 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 != -1) em(3, "Should have failed");
113   if(errno != EINVAL) em(4, "Incorrect error thrown");
114 
115   /* Do a few more tests for invalid timeout values. */
116   tv.tv_sec = 0;
117   tv.tv_usec = 1000000;
118   retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
119   if (retval != -1) em(0, "Should have failed");
120   if (errno != EINVAL) em(0, "Incorrect error thrown");
121 
122   tv.tv_sec = 0;
123   tv.tv_usec = ~0;
124   retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
125   if (retval != -1) em(0, "Should have failed");
126   if (errno != EINVAL) em(0, "Incorrect error thrown");
127 
128   /* Let's wait for another DO_PAUSE seconds, expressed in seconds and micro
129      seconds. */
130   FD_ZERO(&fds_read);
131   FD_SET(fd_ap[0], &fds_read);
132   tv.tv_sec = DO_PAUSE - 1;
133   tv.tv_usec = 999999L; /* close enough */
134 
135   (void) gettimeofday(&start_time, NULL);   /* Record starting time */
136   retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
137   (void) gettimeofday(&end_time, NULL);     /* Record ending time */
138 
139   if(retval != 0) em(5, "Should have timed out");
140   if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
141     em(6, "Time difference too large");
142 
143   /* Finally, we test if our timeout is interrupted by a signal */
144   FD_ZERO(&fds_read);
145   FD_SET(fd_ap[0], &fds_read);
146   tv.tv_sec = DO_TIMEOUT;
147   tv.tv_usec = 0;
148 
149   (void) gettimeofday(&start_time, NULL);   /* Record starting time */
150   retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
151   (void) gettimeofday(&end_time, NULL);     /* Record ending time */
152 
153   if(retval != -1) em(7, "Should have been interrupted");
154   if(compute_diff(start_time, end_time, DO_TIMEOUT) < DO_DELTA)
155     em(8, "Failed to get interrupted by a signal");
156 
157   if(!got_signal) em(9, "Failed to get interrupted by a signal");
158 
159   waitpid(child, &retval, 0);
160   exit(errct);
161 }
162 
163 int main(int argc, char **argv) {
164   int forkres;
165 
166   /* Retrieve actual system frequency. */
167   system_hz = sysconf(_SC_CLK_TCK);
168   /* Get subtest number */
169   if(argc != 2) {
170     printf("Usage: %s subtest_no\n", argv[0]);
171     exit(-2);
172   } else if(sscanf(argv[1], "%d", &subtest) != 1) {
173     printf("Usage: %s subtest_no\n", argv[0]);
174     exit(-2);
175   }
176 
177   /* Set up anonymous pipe */
178   if(pipe(fd_ap) < 0) {
179     perror("Could not create anonymous pipe");
180     exit(-1);
181   }
182 
183   forkres = fork();
184   if(forkres == 0) do_child();
185   else if(forkres > 0)  do_parent(forkres);
186   else { /* Fork failed */
187     perror("Unable to fork");
188     exit(-1);
189   }
190 
191   exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
192 
193 }
194