30327ba8110e467b574b06bb803ffd9e2cfe832c
[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 "HsBase.h"
26 #include "Rts.h"
27 #if !defined(_WIN32)
28 #include <poll.h>
29 #endif
30
31 /*
32 * inputReady(fd) checks to see whether input is available on the file
33 * descriptor 'fd' within 'msecs' milliseconds (or indefinitely if 'msecs' is
34 * negative). "Input is available" is defined as 'can I safely read at least a
35 * *character* from this file object without blocking?' (this does not work
36 * reliably on Linux when the fd is a not-O_NONBLOCK socket, so if you pass
37 * socket fds to this function, ensure they have O_NONBLOCK;
38 * see `man 2 poll` and `man 2 select`, and
39 * https://ghc.haskell.org/trac/ghc/ticket/13497#comment:26).
40 *
41 * This function blocks until either `msecs` have passed, or input is
42 * available.
43 *
44 * Returns:
45 * 1 => Input ready, 0 => not ready, -1 => error
46 * On error, sets `errno`.
47 */
48 int
49 fdReady(int fd, int write, int msecs, int isSock)
50 {
51 // if we need to track the time then record the end time in case we are
52 // interrupted.
53 Time endTime = 0;
54 if (msecs > 0) {
55 endTime = getProcessElapsedTime() + MSToTime(msecs);
56 }
57
58 #if !defined(_WIN32)
59 struct pollfd fds[1];
60
61 fds[0].fd = fd;
62 fds[0].events = write ? POLLOUT : POLLIN;
63 fds[0].revents = 0;
64
65 Time remaining = MSToTime(msecs);
66
67 int res;
68 while ((res = poll(fds, 1, TimeToMS(remaining))) < 0) {
69 if (errno == EINTR) {
70 if (msecs > 0) {
71 Time now = getProcessElapsedTime();
72 if (now >= endTime) return 0;
73 remaining = endTime - now;
74 }
75 } else {
76 return (-1);
77 }
78 }
79
80 // res is the number of FDs with events
81 return (res > 0);
82
83 #else
84
85 if (isSock) {
86 int maxfd, ready;
87 fd_set rfd, wfd;
88 struct timeval remaining_tv;
89
90 if ((fd >= (int)FD_SETSIZE) || (fd < 0)) {
91 barf("fdReady: fd is too big: %d but FD_SETSIZE is %d", fd, (int)FD_SETSIZE);
92 }
93 FD_ZERO(&rfd);
94 FD_ZERO(&wfd);
95 if (write) {
96 FD_SET(fd, &wfd);
97 } else {
98 FD_SET(fd, &rfd);
99 }
100
101 /* select() will consider the descriptor set in the range of 0 to
102 * (maxfd-1)
103 */
104 maxfd = fd + 1;
105
106 Time remaining = MSToTime(msecs);
107 remaining_tv.tv_sec = TimeToMS(remaining) / 1000;
108 remaining_tv.tv_usec = TimeToUS(remaining) % 1000000;
109
110 while ((ready = select(maxfd, &rfd, &wfd, NULL, &remaining_tv)) < 0 ) {
111 if (errno == EINTR) {
112 if (msecs > 0) {
113 Time now = getProcessElapsedTime();
114 if (now >= endTime) return 0;
115 remaining = endTime - now;
116 remaining_tv.tv_sec = TimeToMS(remaining) / 1000;
117 remaining_tv.tv_usec = TimeToUS(remaining) % 1000000;
118 }
119 } else {
120 return (-1);
121 }
122 }
123
124 return (ready > 0);
125 } else {
126 DWORD rc;
127 HANDLE hFile = (HANDLE)_get_osfhandle(fd);
128 DWORD avail = 0;
129
130 Time remaining = MSToTime(msecs);
131
132 switch (GetFileType(hFile)) {
133
134 case FILE_TYPE_CHAR:
135 {
136 INPUT_RECORD buf[1];
137 DWORD count;
138
139 // nightmare. A Console Handle will appear to be ready
140 // (WaitForSingleObject() returned WAIT_OBJECT_0) when
141 // it has events in its input buffer, but these events might
142 // not be keyboard events, so when we read from the Handle the
143 // read() will block. So here we try to discard non-keyboard
144 // events from a console handle's input buffer and then try
145 // the WaitForSingleObject() again.
146
147 while (1) // keep trying until we find a real key event
148 {
149 // WaitForSingleObject takes an unsigned number,
150 // `remaining` can be negative. Wait 0 if so.
151 DWORD wait_ms = (DWORD) max(0, TimeToMS(remaining));
152
153 rc = WaitForSingleObject( hFile, wait_ms );
154 switch (rc) {
155 case WAIT_TIMEOUT: return 0;
156 case WAIT_OBJECT_0: break;
157 default: /* WAIT_FAILED */ maperrno(); return -1;
158 }
159
160 while (1) // discard non-key events
161 {
162 BOOL success = PeekConsoleInput(hFile, buf, 1, &count);
163 // printf("peek, rc=%d, count=%d, type=%d\n", rc, count, buf[0].EventType);
164 if (!success) {
165 rc = GetLastError();
166 if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) {
167 return 1;
168 } else {
169 maperrno();
170 return -1;
171 }
172 }
173
174 if (count == 0) break; // no more events => wait again
175
176 // discard console events that are not "key down", because
177 // these will also be discarded by ReadFile().
178 if (buf[0].EventType == KEY_EVENT &&
179 buf[0].Event.KeyEvent.bKeyDown &&
180 buf[0].Event.KeyEvent.uChar.AsciiChar != '\0')
181 {
182 // it's a proper keypress:
183 return 1;
184 }
185 else
186 {
187 // it's a non-key event, a key up event, or a
188 // non-character key (e.g. shift). discard it.
189 BOOL success = ReadConsoleInput(hFile, buf, 1, &count);
190 if (!success) {
191 rc = GetLastError();
192 if (rc == ERROR_INVALID_HANDLE || rc == ERROR_INVALID_FUNCTION) {
193 return 1;
194 } else {
195 maperrno();
196 return -1;
197 }
198 }
199 }
200 }
201
202 Time now = getProcessElapsedTime();
203 remaining = endTime - now;
204 }
205 }
206
207 case FILE_TYPE_DISK:
208 // assume that disk files are always ready:
209 return 1;
210
211 case FILE_TYPE_PIPE:
212 // WaitForMultipleObjects() doesn't work for pipes (it
213 // always returns WAIT_OBJECT_0 even when no data is
214 // available). If the HANDLE is a pipe, therefore, we try
215 // PeekNamedPipe():
216 //
217 // PeekNamedPipe() does not block, so if it returns that
218 // there is no new data, we have to sleep and try again.
219 while (avail == 0) {
220 BOOL success = PeekNamedPipe( hFile, NULL, 0, NULL, &avail, NULL );
221 if (success) {
222 if (avail != 0) {
223 return 1;
224 } else { // no new data
225 if (msecs > 0) {
226 Time now = getProcessElapsedTime();
227 if (now >= endTime) return 0;
228 Sleep(1); // 1 millisecond (smallest possible time on Windows)
229 continue;
230 } else {
231 return 0;
232 }
233 }
234 } else {
235 rc = GetLastError();
236 if (rc == ERROR_BROKEN_PIPE) {
237 return 1; // this is probably what we want
238 }
239 if (rc != ERROR_INVALID_HANDLE && rc != ERROR_INVALID_FUNCTION) {
240 maperrno();
241 return -1;
242 }
243 }
244 }
245 /* PeekNamedPipe didn't work - fall through to the general case */
246
247 default:
248 // This cast is OK because we assert against `msecs < 0` above.
249 rc = WaitForSingleObject( hFile, (DWORD) msecs );
250
251 /* 1 => Input ready, 0 => not ready, -1 => error */
252 switch (rc) {
253 case WAIT_TIMEOUT: return 0;
254 case WAIT_OBJECT_0: return 1;
255 default: /* WAIT_FAILED */ maperrno(); return -1;
256 }
257 }
258 }
259 #endif
260 }