summaryrefslogtreecommitdiff
path: root/minix/tests/t40f.c
blob: 47f97491483cd450808bffd28eee9cec9bf92748 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* t40f.c
 *
 * Test timing
 *
 * Select works on regular files, (pseudo) terminal devices, streams-based
 * files, FIFOs, pipes, and sockets. This test verifies selecting with a time
 * out set. 
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#include "common.h"

#define DO_HANDLEDATA 1
#define DO_PAUSE 3
#define DO_TIMEOUT 7
#define DO_DELTA 0.5
#define MAX_ERROR 5
#define DELTA(x,y)  (x.tv_sec - y.tv_sec) * system_hz \
  + (x.tv_usec - y.tv_usec) * system_hz / 1000000

int got_signal = 0;
int fd_ap[2];

int system_hz;

static void catch_signal(int sig_no) {
  got_signal = 1;
}

static float compute_diff(struct timeval start, struct timeval end, float compare) {
  /* Compute time difference. It is assumed that the value of start <= end. */
  clock_t delta;
  int seconds, hundreths;
  float diff;

  delta = DELTA(end, start); /* delta is in ticks */
  seconds = (int) (delta / system_hz);
  hundreths = (int) (delta * 100 / system_hz) - (seconds * 100);

  diff = seconds + (hundreths / 100.0);
  diff -= compare;
  if(diff < 0) diff *= -1; /* Make diff a positive value */

  return diff;
}

static void do_child(void) {
  struct timeval tv;
 
  /* Let the parent do initial read and write tests from and to the pipe. */
  tv.tv_sec = DO_PAUSE + DO_PAUSE + 1;
  tv.tv_usec = 0;
  (void) select(0, NULL, NULL, NULL, &tv);

  /* At this point the parent has a pending select with a DO_TIMEOUT timeout.
     We're going to interrupt by sending a signal */
  if(kill(getppid(), SIGUSR1) < 0) perror("Failed to send signal");
  
  exit(0);
}

static void do_parent(int child) {
  fd_set fds_read;
  struct timeval tv, start_time, end_time;
  int retval;

  /* Install signal handler for SIGUSR1 */
  signal(SIGUSR1, catch_signal);

  /* Parent and child share an anonymous pipe. Select for read and wait for the
   timeout to occur. We wait for DO_PAUSE seconds. Let's see if that's
   approximately right.*/
  FD_ZERO(&fds_read);
  FD_SET(fd_ap[0], &fds_read);
  tv.tv_sec = DO_PAUSE;
  tv.tv_usec = 0;

  (void) gettimeofday(&start_time, NULL);   /* Record starting time */
  retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv); 
  (void) gettimeofday(&end_time, NULL);     /* Record ending time */
  
  /* Did we time out? */
  if(retval != 0) em(1, "Should have timed out");
  
  /* Approximately right? The standard does not specify how precise the timeout
     should be. Instead, the granularity is implementation-defined. In this
     test we assume that the difference should be no more than half a second.*/
  if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
    em(2, "Time difference too large");
  
  /* Let's wait for another DO_PAUSE seconds, expressed as microseconds */
  FD_ZERO(&fds_read);
  FD_SET(fd_ap[0], &fds_read);
  tv.tv_sec = 0;
  tv.tv_usec = DO_PAUSE * 1000000L;
  
  (void) gettimeofday(&start_time, NULL);   /* Record starting time */
  retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv); 
  (void) gettimeofday(&end_time, NULL);     /* Record ending time */
  
  if(retval != -1) em(3, "Should have failed");
  if(errno != EINVAL) em(4, "Incorrect error thrown");

  /* Do a few more tests for invalid timeout values. */
  tv.tv_sec = 0;
  tv.tv_usec = 1000000;
  retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
  if (retval != -1) em(0, "Should have failed");
  if (errno != EINVAL) em(0, "Incorrect error thrown");

  tv.tv_sec = 0;
  tv.tv_usec = ~0;
  retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
  if (retval != -1) em(0, "Should have failed");
  if (errno != EINVAL) em(0, "Incorrect error thrown");

  /* Let's wait for another DO_PAUSE seconds, expressed in seconds and micro
     seconds. */
  FD_ZERO(&fds_read);
  FD_SET(fd_ap[0], &fds_read);
  tv.tv_sec = DO_PAUSE - 1;
  tv.tv_usec = 999999L; /* close enough */
  
  (void) gettimeofday(&start_time, NULL);   /* Record starting time */
  retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv); 
  (void) gettimeofday(&end_time, NULL);     /* Record ending time */
  
  if(retval != 0) em(5, "Should have timed out");
  if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
    em(6, "Time difference too large");

  /* Finally, we test if our timeout is interrupted by a signal */
  FD_ZERO(&fds_read);
  FD_SET(fd_ap[0], &fds_read);
  tv.tv_sec = DO_TIMEOUT;
  tv.tv_usec = 0;

  (void) gettimeofday(&start_time, NULL);   /* Record starting time */
  retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv); 
  (void) gettimeofday(&end_time, NULL);     /* Record ending time */
  
  if(retval != -1) em(7, "Should have been interrupted");
  if(compute_diff(start_time, end_time, DO_TIMEOUT) < DO_DELTA)
    em(8, "Failed to get interrupted by a signal");

  if(!got_signal) em(9, "Failed to get interrupted by a signal");

  waitpid(child, &retval, 0);
  exit(errct);
}
  
int main(int argc, char **argv) {
  int forkres;

  /* Retrieve actual system frequency. */
  system_hz = sysconf(_SC_CLK_TCK);
  /* Get subtest number */
  if(argc != 2) {
    printf("Usage: %s subtest_no\n", argv[0]);
    exit(-2);
  } else if(sscanf(argv[1], "%d", &subtest) != 1) {
    printf("Usage: %s subtest_no\n", argv[0]);
    exit(-2);
  }
  
  /* Set up anonymous pipe */
  if(pipe(fd_ap) < 0) {
    perror("Could not create anonymous pipe");
    exit(-1);
  }

  forkres = fork();
  if(forkres == 0) do_child();
  else if(forkres > 0)  do_parent(forkres);
  else { /* Fork failed */
    perror("Unable to fork");
    exit(-1);
  }

  exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
  
}