Add tests for errno after sin/cos calls with ±Inf.
[glibc/history.git] / hurd / hurdselect.c
blob544eee9cb5ba2d93edc98a1b93c40daa1074fbd4
1 /* Guts of both `select' and `poll' for Hurd.
2 Copyright (C) 1991,92,93,94,95,96,97,98,99,2001
3 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
21 #include <sys/types.h>
22 #include <sys/poll.h>
23 #include <hurd.h>
24 #include <hurd/fd.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28 #include <stdint.h>
30 /* All user select types. */
31 #define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG)
33 /* Used to record that a particular select rpc returned. Must be distinct
34 from SELECT_ALL (which better not have the high bit set). */
35 #define SELECT_RETURNED ((SELECT_ALL << 1) & ~SELECT_ALL)
37 /* Check the first NFDS descriptors either in POLLFDS (if nonnnull) or in
38 each of READFDS, WRITEFDS, EXCEPTFDS that is nonnull. If TIMEOUT is not
39 NULL, time out after waiting the interval specified therein. Returns
40 the number of ready descriptors, or -1 for errors. */
41 int
42 _hurd_select (int nfds,
43 struct pollfd *pollfds,
44 fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
45 const struct timespec *timeout, const sigset_t *sigmask)
47 int i;
48 mach_port_t portset;
49 int got;
50 error_t err;
51 fd_set rfds, wfds, xfds;
52 int firstfd, lastfd;
53 mach_msg_timeout_t to = (timeout != NULL ?
54 (timeout->tv_sec * 1000 +
55 timeout->tv_nsec / 1000000) :
56 0);
57 struct
59 struct hurd_userlink ulink;
60 struct hurd_fd *cell;
61 mach_port_t io_port;
62 int type;
63 mach_port_t reply_port;
64 } d[nfds];
65 sigset_t oset;
67 union typeword /* Use this to avoid unkosher casts. */
69 mach_msg_type_t type;
70 uint32_t word;
72 assert (sizeof (union typeword) == sizeof (mach_msg_type_t));
73 assert (sizeof (uint32_t) == sizeof (mach_msg_type_t));
75 if (sigmask && __sigprocmask (SIG_SETMASK, sigmask, &oset))
76 return -1;
78 if (pollfds)
80 /* Collect interesting descriptors from the user's `pollfd' array.
81 We do a first pass that reads the user's array before taking
82 any locks. The second pass then only touches our own stack,
83 and gets the port references. */
85 for (i = 0; i < nfds; ++i)
86 if (pollfds[i].fd >= 0)
88 int type = 0;
89 if (pollfds[i].events & POLLIN)
90 type |= SELECT_READ;
91 if (pollfds[i].events & POLLOUT)
92 type |= SELECT_WRITE;
93 if (pollfds[i].events & POLLPRI)
94 type |= SELECT_URG;
96 d[i].io_port = pollfds[i].fd;
97 d[i].type = type;
99 else
100 d[i].type = 0;
102 HURD_CRITICAL_BEGIN;
103 __mutex_lock (&_hurd_dtable_lock);
105 for (i = 0; i < nfds; ++i)
106 if (d[i].type != 0)
108 const int fd = (int) d[i].io_port;
110 if (fd < _hurd_dtablesize)
112 d[i].cell = _hurd_dtable[fd];
113 d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
114 if (d[i].io_port != MACH_PORT_NULL)
115 continue;
118 /* If one descriptor is bogus, we fail completely. */
119 while (i-- > 0)
120 if (d[i].type != 0)
121 _hurd_port_free (&d[i].cell->port,
122 &d[i].ulink, d[i].io_port);
123 break;
126 __mutex_unlock (&_hurd_dtable_lock);
127 HURD_CRITICAL_END;
129 if (i < nfds)
131 if (sigmask)
132 __sigprocmask (SIG_SETMASK, &oset, NULL);
133 errno = EBADF;
134 return -1;
137 lastfd = i - 1;
138 firstfd = i == 0 ? lastfd : 0;
140 else
142 /* Collect interested descriptors from the user's fd_set arguments.
143 Use local copies so we can't crash from user bogosity. */
145 if (readfds == NULL)
146 FD_ZERO (&rfds);
147 else
148 rfds = *readfds;
149 if (writefds == NULL)
150 FD_ZERO (&wfds);
151 else
152 wfds = *writefds;
153 if (exceptfds == NULL)
154 FD_ZERO (&xfds);
155 else
156 xfds = *exceptfds;
158 HURD_CRITICAL_BEGIN;
159 __mutex_lock (&_hurd_dtable_lock);
161 if (nfds > _hurd_dtablesize)
162 nfds = _hurd_dtablesize;
164 /* Collect the ports for interesting FDs. */
165 firstfd = lastfd = -1;
166 for (i = 0; i < nfds; ++i)
168 int type = 0;
169 if (readfds != NULL && FD_ISSET (i, &rfds))
170 type |= SELECT_READ;
171 if (writefds != NULL && FD_ISSET (i, &wfds))
172 type |= SELECT_WRITE;
173 if (exceptfds != NULL && FD_ISSET (i, &xfds))
174 type |= SELECT_URG;
175 d[i].type = type;
176 if (type)
178 d[i].cell = _hurd_dtable[i];
179 d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
180 if (d[i].io_port == MACH_PORT_NULL)
182 /* If one descriptor is bogus, we fail completely. */
183 while (i-- > 0)
184 if (d[i].type != 0)
185 _hurd_port_free (&d[i].cell->port, &d[i].ulink,
186 d[i].io_port);
187 break;
189 lastfd = i;
190 if (firstfd == -1)
191 firstfd = i;
195 __mutex_unlock (&_hurd_dtable_lock);
196 HURD_CRITICAL_END;
198 if (i < nfds)
200 if (sigmask)
201 __sigprocmask (SIG_SETMASK, &oset, NULL);
202 errno = EBADF;
203 return -1;
208 err = 0;
209 got = 0;
211 /* Send them all io_select request messages. */
213 if (firstfd == -1)
214 /* But not if there were no ports to deal with at all.
215 We are just a pure timeout. */
216 portset = __mach_reply_port ();
217 else
219 portset = MACH_PORT_NULL;
221 for (i = firstfd; i <= lastfd; ++i)
222 if (d[i].type)
224 int type = d[i].type;
225 d[i].reply_port = __mach_reply_port ();
226 err = __io_select (d[i].io_port, d[i].reply_port,
227 /* Poll only if there's a single descriptor. */
228 (firstfd == lastfd) ? to : 0,
229 &type);
230 switch (err)
232 case MACH_RCV_TIMED_OUT:
233 /* No immediate response. This is normal. */
234 err = 0;
235 if (firstfd == lastfd)
236 /* When there's a single descriptor, we don't need a
237 portset, so just pretend we have one, but really
238 use the single reply port. */
239 portset = d[i].reply_port;
240 else if (got == 0)
241 /* We've got multiple reply ports, so we need a port set to
242 multiplex them. */
244 /* We will wait again for a reply later. */
245 if (portset == MACH_PORT_NULL)
246 /* Create the portset to receive all the replies on. */
247 err = __mach_port_allocate (__mach_task_self (),
248 MACH_PORT_RIGHT_PORT_SET,
249 &portset);
250 if (! err)
251 /* Put this reply port in the port set. */
252 __mach_port_move_member (__mach_task_self (),
253 d[i].reply_port, portset);
255 break;
257 default:
258 /* No other error should happen. Callers of select
259 don't expect to see errors, so we simulate
260 readiness of the erring object and the next call
261 hopefully will get the error again. */
262 type = SELECT_ALL;
263 /* FALLTHROUGH */
265 case 0:
266 /* We got an answer. */
267 if ((type & SELECT_ALL) == 0)
268 /* Bogus answer; treat like an error, as a fake positive. */
269 type = SELECT_ALL;
271 /* This port is already ready already. */
272 d[i].type &= type;
273 d[i].type |= SELECT_RETURNED;
274 ++got;
275 break;
277 _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
281 /* Now wait for reply messages. */
282 if (!err && got == 0)
284 /* Now wait for io_select_reply messages on PORT,
285 timing out as appropriate. */
287 union
289 mach_msg_header_t head;
290 #ifdef MACH_MSG_TRAILER_MINIMUM_SIZE
291 struct
293 mach_msg_header_t head;
294 NDR_record_t ndr;
295 error_t err;
296 } error;
297 struct
299 mach_msg_header_t head;
300 NDR_record_t ndr;
301 error_t err;
302 int result;
303 mach_msg_trailer_t trailer;
304 } success;
305 #else
306 struct
308 mach_msg_header_t head;
309 union typeword err_type;
310 error_t err;
311 } error;
312 struct
314 mach_msg_header_t head;
315 union typeword err_type;
316 error_t err;
317 union typeword result_type;
318 int result;
319 } success;
320 #endif
321 } msg;
322 mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT);
323 error_t msgerr;
324 while ((msgerr = __mach_msg (&msg.head,
325 MACH_RCV_MSG | options,
326 0, sizeof msg, portset, to,
327 MACH_PORT_NULL)) == MACH_MSG_SUCCESS)
329 /* We got a message. Decode it. */
330 #define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */
331 #ifdef MACH_MSG_TYPE_BIT
332 const union typeword inttype =
333 { type:
334 { MACH_MSG_TYPE_INTEGER_T, sizeof (integer_t) * 8, 1, 1, 0, 0 }
336 #endif
337 if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID &&
338 msg.head.msgh_size >= sizeof msg.error &&
339 !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
340 #ifdef MACH_MSG_TYPE_BIT
341 msg.error.err_type.word == inttype.word
342 #endif
345 /* This is a properly formatted message so far.
346 See if it is a success or a failure. */
347 if (msg.error.err == EINTR &&
348 msg.head.msgh_size == sizeof msg.error)
350 /* EINTR response; poll for further responses
351 and then return quickly. */
352 err = EINTR;
353 goto poll;
355 if (msg.error.err ||
356 msg.head.msgh_size != sizeof msg.success ||
357 #ifdef MACH_MSG_TYPE_BIT
358 msg.success.result_type.word != inttype.word ||
359 #endif
360 (msg.success.result & SELECT_ALL) == 0)
362 /* Error or bogus reply. Simulate readiness. */
363 __mach_msg_destroy (&msg.head);
364 msg.success.result = SELECT_ALL;
367 /* Look up the respondent's reply port and record its
368 readiness. */
370 int had = got;
371 if (firstfd != -1)
372 for (i = firstfd; i <= lastfd; ++i)
373 if (d[i].type
374 && d[i].reply_port == msg.head.msgh_local_port)
376 d[i].type &= msg.success.result;
377 d[i].type |= SELECT_RETURNED;
378 ++got;
380 assert (got > had);
384 if (msg.head.msgh_remote_port != MACH_PORT_NULL)
385 __mach_port_deallocate (__mach_task_self (),
386 msg.head.msgh_remote_port);
388 if (got)
389 poll:
391 /* Poll for another message. */
392 to = 0;
393 options |= MACH_RCV_TIMEOUT;
397 if (err == MACH_RCV_TIMED_OUT)
398 /* This is the normal value for ERR. We might have timed out and
399 read no messages. Otherwise, after receiving the first message,
400 we poll for more messages. We receive with a timeout of 0 to
401 effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no
402 message waiting. */
403 err = 0;
405 if (got)
406 /* At least one descriptor is known to be ready now, so we will
407 return success. */
408 err = 0;
411 if (firstfd != -1)
412 for (i = firstfd; i <= lastfd; ++i)
413 if (d[i].type)
414 __mach_port_destroy (__mach_task_self (), d[i].reply_port);
415 if (firstfd == -1 || (firstfd != lastfd && portset != MACH_PORT_NULL))
416 /* Destroy PORTSET, but only if it's not actually the reply port for a
417 single descriptor (in which case it's destroyed in the previous loop;
418 not doing it here is just a bit more efficient). */
419 __mach_port_destroy (__mach_task_self (), portset);
421 if (err)
423 if (sigmask)
424 __sigprocmask (SIG_SETMASK, &oset, NULL);
425 return __hurd_fail (err);
428 if (pollfds)
429 /* Fill in the `revents' members of the user's array. */
430 for (i = 0; i < nfds; ++i)
432 int type = d[i].type;
433 int_fast16_t revents = 0;
435 if (type & SELECT_RETURNED)
437 if (type & SELECT_READ)
438 revents |= POLLIN;
439 if (type & SELECT_WRITE)
440 revents |= POLLOUT;
441 if (type & SELECT_URG)
442 revents |= POLLPRI;
445 pollfds[i].revents = revents;
447 else
449 /* Below we recalculate GOT to include an increment for each operation
450 allowed on each fd. */
451 got = 0;
453 /* Set the user bitarrays. We only ever have to clear bits, as all
454 desired ones are initially set. */
455 if (firstfd != -1)
456 for (i = firstfd; i <= lastfd; ++i)
458 int type = d[i].type;
460 if ((type & SELECT_RETURNED) == 0)
461 type = 0;
463 if (type & SELECT_READ)
464 got++;
465 else if (readfds)
466 FD_CLR (i, readfds);
467 if (type & SELECT_WRITE)
468 got++;
469 else if (writefds)
470 FD_CLR (i, writefds);
471 if (type & SELECT_URG)
472 got++;
473 else if (exceptfds)
474 FD_CLR (i, exceptfds);
478 if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL))
479 return -1;
481 return got;