base: fdReady(): Fix timeouts > ~49 days overflowing. Fixes #14262.
[ghc.git] / libraries / base / cbits / inputReady.c
1 /*
2 * (c) The GRASP/AQUA Project, Glasgow University, 1994-2002
3 *
4 * hWaitForInput Runtime Support
5 */
6
7 /* FD_SETSIZE defaults to 64 on Windows, which makes even the most basic
8 * programs break that use select() on a socket FD.
9 * Thus we raise it here (before any #include of network-related headers)
10 * to 1024 so that at least those programs would work that would work on
11 * Linux if that used select() (luckily it uses poll() by now).
12 * See https://ghc.haskell.org/trac/ghc/ticket/13497#comment:23
13 * The real solution would be to remove all uses of select()
14 * on Windows, too, and use IO Completion Ports instead.
15 * Note that on Windows, one can simply define FD_SETSIZE to the desired
16 * size before including Winsock2.h, as described here:
17 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms740141(v=vs.85).aspx
18 */
19 #if defined(_WIN32)
20 #define FD_SETSIZE 1024
21 #endif
22
23 /* select and supporting types is not Posix */
24 /* #include "PosixSource.h" */
25 #include <limits.h>
26 #include <stdbool.h>
27 #include "HsBase.h"
28 #include "Rts.h"
29 #if !defined(_WIN32)
30 #include <poll.h>
31 #endif
32
33 /*
34 * Returns a timeout suitable to be passed into poll().
35 *
36 * If `infinite`, `remaining` is ignored.
37 */
38 static inline
39 int
40 compute_poll_timeout(bool infinite, Time remaining)
41 {
42 if (infinite) return -1;
43
44 if (remaining < 0) return 0;
45
46 if (remaining > MSToTime(INT_MAX)) return INT_MAX;
47
48 return TimeToMS(remaining);
49 }
50
51 #if defined(_WIN32)
52 /*
53 * Returns a timeout suitable to be passed into select() on Windows.
54 *
55 * The given `remaining_tv` serves as a storage for the timeout
56 * when needed, but callers should use the returned value instead
57 * as it will not be filled in all cases.
58 *
59 * If `infinite`, `remaining` is ignored and `remaining_tv` not touched
60 * (and may be passed as NULL in that case).
61 */
62 static inline
63 struct timeval *
64 compute_windows_select_timeout(bool infinite, Time remaining,
65 /* out */ struct timeval * remaining_tv)
66 {
67 if (infinite) {
68 return NULL;
69 }
70
71 ASSERT(remaining_tv);
72
73 if (remaining < 0) {
74 remaining_tv->tv_sec = 0;
75 remaining_tv->tv_usec = 0;
76 } else if (remaining > MSToTime(LONG_MAX)) {
77 remaining_tv->tv_sec = LONG_MAX;
78 remaining_tv->tv_usec = LONG_MAX;
79 } else {
80 remaining_tv->tv_sec = TimeToMS(remaining) / 1000;
81 remaining_tv->tv_usec = TimeToUS(remaining) % 1000000;
82 }
83
84 return remaining_tv;
85 }
86
87 /*
88 * Returns a timeout suitable to be passed into WaitForSingleObject() on
89 * Windows.
90 *
91 * If `infinite`, `remaining` is ignored.
92 */
93 static inline
94 DWORD
95 compute_WaitForSingleObject_timeout(bool infinite, Time remaining)
96 {
97 // WaitForSingleObject() has the fascinating delicacy behaviour
98 // that it waits indefinitely if the `DWORD dwMilliseconds`
99 // is set to 0xFFFFFFFF (the maximum DWORD value), which is
100 // 4294967295 seconds == ~49.71 days
101 // (the Windows API calls this constant INFINITE...).
102 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx
103 //
104 // We ensure that if accidentally `remaining == 4294967295`, it does
105 // NOT wait forever, by never passing that value to
106 // WaitForSingleObject() (so, never returning it from this function),
107 // unless `infinite`.
108
109 if (infinite) return INFINITE;
110
111 if (remaining < 0) return 0;
112
113 if (remaining >= MSToTime(INFINITE)) return INFINITE - 1;
114
115 return (DWORD) TimeToMS(remaining);
116 }
117 #endif
118
119 /*
120 * inputReady(fd) checks to see whether input is available on the file
121 * descriptor 'fd' within 'msecs' milliseconds (or indefinitely if 'msecs' is
122 * negative). "Input is available" is defined as 'can I safely read at least a
123 * *character* from this file object without blocking?' (this does not work
124 * reliably on Linux when the fd is a not-O_NONBLOCK socket, so if you pass
125 * socket fds to this function, ensure they have O_NONBLOCK;
126 * see `man 2 poll` and `man 2 select`, and
127 * https://ghc.haskell.org/trac/ghc/ticket/13497#comment:26).
128 *
129 * This function blocks until either `msecs` have passed, or input is
130 * available.
131 *
132 * Returns:
133 * 1 => Input ready, 0 => not ready, -1 => error
134 * On error, sets `errno`.
135 */
136 int
137 fdReady(int fd, int write, int64_t msecs, int isSock)
138 {
139 bool infinite = msecs < 0;
140
141 // if we need to track the time then record the end time in case we are
142 // interrupted.
143 Time endTime = 0;
144 if (msecs > 0) {
145 endTime = getProcessElapsedTime() + MSToTime(msecs);
146 }
147
148 // Invariant of all code below:
149 // If `infinite`, then `remaining` and `endTime` are never used.
150
151 Time remaining = MSToTime(msecs);
152
153 #if !defined(_WIN32)
154 struct pollfd fds[1];
155
156 fds[0].fd = fd;
157 fds[0].events = write ? POLLOUT : POLLIN;
158 fds[0].revents = 0;
159
160 // The code below tries to make as few syscalls as possible;
161 // in particular, it eschews getProcessElapsedTime() calls
162 // when `infinite` or `msecs == 0`.
163
164 // We need to wait in a loop because poll() accepts `int` but `msecs` is
165 // `int64_t`, and because signals can interrupt it.
166
167 while (true) {
168 int res = poll(fds, 1, compute_poll_timeout(infinite, remaining));
169
170 if (res < 0 && errno != EINTR)
171 return (-1); // real error; errno is preserved
172
173 if (res > 0)
174 return 1; // FD has new data
175
176 if (res == 0 && !infinite && remaining <= MSToTime(INT_MAX))
177 return 0; // FD has no new data and we've waited the full msecs
178
179 // Non-exit cases
180 CHECK( ( res < 0 && errno == EINTR ) || // EINTR happened
181 // need to wait more
182 ( res == 0 && (infinite ||
183 remaining > MSToTime(INT_MAX)) ) );
184
185 if (!infinite) {
186 Time now = getProcessElapsedTime();
187 if (now >= endTime) return 0;
188 remaining = endTime - now;
189 }
190 }
191
192 #else
193
194 if (isSock) {
195 int maxfd;
196 fd_set rfd, wfd;
197 struct timeval remaining_tv;
198
199 if ((fd >= (int)FD_SETSIZE) || (fd < 0)) {
200 barf("fdReady: fd is too big: %d but FD_SETSIZE is %d", fd, (int)FD_SETSIZE);
201 }
202 FD_ZERO(&rfd);
203 FD_ZERO(&wfd);
204 if (write) {
205 FD_SET(fd, &wfd);
206 } else {
207 FD_SET(fd, &rfd);
208 }
209
210 /* select() will consider the descriptor set in the range of 0 to
211 * (maxfd-1)
212 */
213 maxfd = fd + 1;
214
215 // We need to wait in a loop because the `timeval` `tv_*` members
216 // passed into select() accept are `long` (which is 32 bits on 32-bit
217 // and 64-bit Windows), but `msecs` is `int64_t`, and because signals
218 // can interrupt it.
219 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740560(v=vs.85).aspx
220 // https://stackoverflow.com/questions/384502/what-is-the-bit-size-of-long-on-64-bit-windows#384672
221
222 while (true) {
223 int res = select(maxfd, &rfd, &wfd, NULL,
224 compute_windows_select_timeout(infinite, remaining,
225 &remaining_tv));
226
227 if (res < 0 && errno != EINTR)
228 return (-1); // real error; errno is preserved
229
230 if (res > 0)
231 return 1; // FD has new data
232
233 if (res == 0 && !infinite && remaining <= MSToTime(INT_MAX))
234 return 0; // FD has no new data and we've waited the full msecs
235
236 // Non-exit cases
237 CHECK( ( res < 0 && errno == EINTR ) || // EINTR happened
238 // need to wait more
239 ( res == 0 && (infinite ||
240 remaining > MSToTime(INT_MAX)) ) );
241
242 if (!infinite) {
243 Time now = getProcessElapsedTime();
244 if (now >= endTime) return 0;
245 remaining = endTime - now;
246 }
247 }
248
249 } else {
250 DWORD rc;
251 HANDLE hFile = (HANDLE)_get_osfhandle(fd);
252 DWORD avail = 0;
253
254 switch (GetFileType(hFile)) {
255
256 case FILE_TYPE_CHAR:
257 {
258 INPUT_RECORD buf[1];
259 DWORD count;
260
261 // nightmare. A Console Handle will appear to be ready
262 // (WaitForSingleObject() returned WAIT_OBJECT_0) when
263 // it has events in its input buffer, but these events might
264 // not be keyboard events, so when we read from the Handle the
265 // read() will block. So here we try to discard non-keyboard
266 // events from a console handle's input buffer and then try
267 // the WaitForSingleObject() again.
268
269 while (1) // keep trying until we find a real key event
270 {
271 rc = WaitForSingleObject(
272 hFile,
273 compute_WaitForSingleObject_timeout(infinite, remaining));
274 switch (rc) {
275 case WAIT_TIMEOUT:
276 // We need to use < here because if remaining
277 // was INFINITE, we'll have waited for
278 // `INFINITE - 1` as per
279 // compute_WaitForSingleObject_timeout(),
280 // so that's 1 ms too little. Wait again then.
281 if (!infinite && remaining < MSToTime(INFINITE))
282 return 0;
283 goto waitAgain;
284 case WAIT_OBJECT_0: break;
285 default: /* WAIT_FAILED */ maperrno(); return -1;
286 }
287
288 while (1) // discard non-key events
289 {
290 BOOL success = PeekConsoleInput(hFile, buf, 1, &count);
291 // printf("peek, rc=%d, count=%d, type=%d\n", rc, count, buf[0].EventType);
292 if (!success) {
293 rc = GetLastError();
294 if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) {
295 return 1;
296 } else {
297 maperrno();
298 return -1;
299 }
300 }
301
302 if (count == 0) break; // no more events => wait again
303
304 // discard console events that are not "key down", because
305 // these will also be discarded by ReadFile().
306 if (buf[0].EventType == KEY_EVENT &&
307 buf[0].Event.KeyEvent.bKeyDown &&
308 buf[0].Event.KeyEvent.uChar.AsciiChar != '\0')
309 {
310 // it's a proper keypress:
311 return 1;
312 }
313 else
314 {
315 // it's a non-key event, a key up event, or a
316 // non-character key (e.g. shift). discard it.
317 BOOL success = ReadConsoleInput(hFile, buf, 1, &count);
318 if (!success) {
319 rc = GetLastError();
320 if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) {
321 return 1;
322 } else {
323 maperrno();
324 return -1;
325 }
326 }
327 }
328 }
329
330 Time now;
331 waitAgain:
332 now = getProcessElapsedTime();
333 remaining = endTime - now;
334 }
335 }
336
337 case FILE_TYPE_DISK:
338 // assume that disk files are always ready:
339 return 1;
340
341 case FILE_TYPE_PIPE: {
342 // WaitForMultipleObjects() doesn't work for pipes (it
343 // always returns WAIT_OBJECT_0 even when no data is
344 // available). If the HANDLE is a pipe, therefore, we try
345 // PeekNamedPipe():
346 //
347 // PeekNamedPipe() does not block, so if it returns that
348 // there is no new data, we have to sleep and try again.
349 while (avail == 0) {
350 BOOL success = PeekNamedPipe( hFile, NULL, 0, NULL, &avail, NULL );
351 if (success) {
352 if (avail != 0) {
353 return 1;
354 } else { // no new data
355 if (infinite) {
356 Sleep(1); // 1 millisecond (smallest possible time on Windows)
357 continue;
358 } else if (msecs == 0) {
359 return 0;
360 } else {
361 Time now = getProcessElapsedTime();
362 if (now >= endTime) return 0;
363 Sleep(1); // 1 millisecond (smallest possible time on Windows)
364 continue;
365 }
366 }
367 } else {
368 rc = GetLastError();
369 if (rc == ERROR_BROKEN_PIPE) {
370 return 1; // this is probably what we want
371 }
372 if (rc != ERROR_INVALID_HANDLE && rc != ERROR_INVALID_FUNCTION) {
373 maperrno();
374 return -1;
375 }
376 }
377 }
378 }
379 /* PeekNamedPipe didn't work - fall through to the general case */
380
381 default:
382 while (true) {
383 rc = WaitForSingleObject(
384 hFile,
385 compute_WaitForSingleObject_timeout(infinite, remaining));
386
387 switch (rc) {
388 case WAIT_TIMEOUT:
389 // We need to use < here because if remaining
390 // was INFINITE, we'll have waited for
391 // `INFINITE - 1` as per
392 // compute_WaitForSingleObject_timeout(),
393 // so that's 1 ms too little. Wait again then.
394 if (!infinite && remaining < MSToTime(INFINITE))
395 return 0;
396 break;
397 case WAIT_OBJECT_0: return 1;
398 default: /* WAIT_FAILED */ maperrno(); return -1;
399 }
400
401 // EINTR or a >(INFINITE - 1) timeout completed
402 if (!infinite) {
403 Time now = getProcessElapsedTime();
404 if (now >= endTime) return 0;
405 remaining = endTime - now;
406 }
407 }
408 }
409 }
410 #endif
411 }