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