(__select): Include LASTFD in the final loop.
[glibc.git] / sysdeps / mach / hurd / select.c
blob8e78adb1bc538e3883cb023caf78fe98cf2aeb1a
1 /* Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA. */
19 #include <ansidecl.h>
20 #include <sys/types.h>
21 #include <hurd.h>
22 #include <hurd/fd.h>
23 #include <stdlib.h>
24 #include <string.h>
26 /* All user select types. */
27 #define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG)
29 /* Used to record that a particular select rpc returned. Must be distinct
30 from SELECT_ALL (which better not have the high bit set). */
31 #define SELECT_RETURNED ((SELECT_ALL << 1) & ~SELECT_ALL)
33 /* Check the first NFDS descriptors each in READFDS (if not NULL) for read
34 readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
35 (if not NULL) for exceptional conditions. If TIMEOUT is not NULL, time out
36 after waiting the interval specified therein. Returns the number of ready
37 descriptors, or -1 for errors. */
38 int
39 DEFUN(__select, (nfds, readfds, writefds, exceptfds, timeout),
40 int nfds AND fd_set *readfds AND fd_set *writefds AND
41 fd_set *exceptfds AND struct timeval *timeout)
43 int i;
44 mach_port_t port;
45 int got;
46 int *types;
47 struct hurd_userlink *ulink;
48 mach_port_t *ports;
49 struct hurd_fd **cells;
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_usec / 1000) :
56 0);
58 /* Use local copies so we can't crash from user bogosity. */
59 if (readfds == NULL)
60 FD_ZERO (&rfds);
61 else
62 rfds = *readfds;
63 if (writefds == NULL)
64 FD_ZERO (&wfds);
65 else
66 wfds = *writefds;
67 if (exceptfds == NULL)
68 FD_ZERO (&xfds);
69 else
70 xfds = *exceptfds;
72 HURD_CRITICAL_BEGIN;
73 __mutex_lock (&_hurd_dtable_lock);
75 if (nfds > _hurd_dtablesize)
76 nfds = _hurd_dtablesize;
78 /* Collect the ports for interesting FDs. */
79 cells = __alloca (nfds * sizeof (*cells));
80 ports = __alloca (nfds * sizeof (*ports));
81 types = __alloca (nfds * sizeof (*types));
82 ulink = __alloca (nfds * sizeof (*ulink));
83 firstfd = lastfd = -1;
84 for (i = 0; i < nfds; ++i)
86 int type = 0;
87 if (readfds != NULL && FD_ISSET (i, &rfds))
88 type |= SELECT_READ;
89 if (writefds != NULL && FD_ISSET (i, &wfds))
90 type |= SELECT_WRITE;
91 if (exceptfds != NULL && FD_ISSET (i, &xfds))
92 type |= SELECT_URG;
93 types[i] = type;
94 if (type)
96 cells[i] = _hurd_dtable[i];
97 ports[i] = _hurd_port_get (&cells[i]->port, &ulink[i]);
98 if (ports[i] == MACH_PORT_NULL)
100 /* If one descriptor is bogus, we fail completely. */
101 while (i-- > 0)
102 _hurd_port_free (&cells[i]->port, &ulink[i], ports[i]);
103 errno = EBADF;
104 break;
106 lastfd = i;
107 if (firstfd == -1)
108 firstfd = i;
112 __mutex_unlock (&_hurd_dtable_lock);
113 HURD_CRITICAL_END;
115 if (i < nfds)
116 return -1;
118 /* Get a port to receive the io_select_reply messages on. */
119 port = __mach_reply_port ();
121 /* Send them all io_select request messages. */
122 got = 0;
123 err = 0;
124 for (i = firstfd; i <= lastfd; ++i)
125 if (types[i])
127 if (!err)
129 int tag = i;
130 int type = types[i];
131 err = __io_select (ports[i], port,
132 /* Poll for each but the last. */
133 (i == lastfd && got == 0) ? to : 0,
134 &type, &tag);
135 switch (err)
137 case MACH_RCV_TIMED_OUT:
138 /* No immediate response. This is normal. */
139 err = 0;
140 break;
142 case 0:
143 /* We got an answer. This is not necessarily the answer to
144 the query we sent just now. It may correspond to any
145 prior query which timed out before its answer arrived. */
146 if (tag < 0 || tag > i || (type & SELECT_ALL) == 0)
147 /* This is not a proper answer to any query we have yet
148 made. */
149 err = EGRATUITOUS;
150 else
152 /* Some port is ready. TAG tells us which. */
153 types[tag] &= type;
154 types[tag] |= SELECT_RETURNED;
155 ++got;
157 break;
159 default:
160 /* Any other error kills us.
161 But we must continue to loop to free the ports. */
162 break;
165 _hurd_port_free (&cells[i]->port, &ulink[i], ports[i]);
168 /* Now wait for reply messages. */
169 if (!err && got == 0 && port != MACH_PORT_NULL)
171 /* Now wait for io_select_reply messages on PORT,
172 timing out as appropriate. */
174 union
176 mach_msg_header_t head;
177 struct
179 mach_msg_header_t head;
180 mach_msg_type_t err_type;
181 error_t err;
182 } error;
183 struct
185 mach_msg_header_t head;
186 mach_msg_type_t err_type;
187 error_t err;
188 mach_msg_type_t result_type;
189 int result;
190 mach_msg_type_t tag_type;
191 int tag;
192 } success;
193 } msg;
194 mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT);
195 error_t msgerr;
196 while ((msgerr = __mach_msg (&msg.head,
197 MACH_RCV_MSG | options,
198 0, sizeof msg, port, to,
199 MACH_PORT_NULL)) == MACH_MSG_SUCCESS)
201 /* We got a message. Decode it. */
202 #define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */
203 const mach_msg_type_t inttype =
204 { MACH_MSG_TYPE_INTEGER_32, 32, 1, 1, 0, 0 };
205 if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID &&
206 msg.head.msgh_size >= sizeof msg.error &&
207 !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
208 *(int *) &msg.error.err_type == *(int *) &inttype)
210 /* This is a properly formatted message so far.
211 See if it is a success or a failure. */
212 if (msg.error.err)
214 err = msg.error.err;
215 if (msg.head.msgh_size != sizeof msg.error)
216 __mach_msg_destroy (&msg);
218 else if (msg.head.msgh_size != sizeof msg.success ||
219 *(int *) &msg.success.tag_type != *(int *) &inttype ||
220 *(int *) &msg.success.result_type != *(int *) &inttype)
221 __mach_msg_destroy (&msg);
222 else if ((msg.success.result & SELECT_ALL) == 0 ||
223 msg.success.tag < firstfd || msg.success.tag > lastfd)
224 err = EGRATUITOUS;
225 else
227 /* This is a winning io_select_reply message!
228 Record the readiness it indicates and send a reply. */
229 types[msg.success.tag] &= msg.success.result;
230 types[msg.success.tag] |= SELECT_RETURNED;
231 ++got;
235 if (msg.head.msgh_remote_port != MACH_PORT_NULL)
236 __mach_port_deallocate (__mach_task_self (),
237 msg.head.msgh_remote_port);
239 if (got || err == EINTR)
241 /* Poll for another message. */
242 to = 0;
243 options |= MACH_RCV_TIMEOUT;
247 if (err == MACH_RCV_TIMED_OUT)
248 /* This is the normal value for ERR. We might have timed out and
249 read no messages. Otherwise, after receiving the first message,
250 we poll for more messages. We receive with a timeout of 0 to
251 effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no
252 message waiting. */
253 err = 0;
255 if (got && err == EINTR)
256 /* Some calls were interrupted, but at least one descriptor
257 is known to be ready now, so we will return success. */
258 err = 0;
261 if (port != MACH_PORT_NULL)
262 /* We must destroy the port if we made some select requests
263 that might send notification on that port after we no longer care.
264 If the port were reused, that notification could confuse the next
265 select call to use the port. The notification might be valid,
266 but the descriptor may have changed to a different server. */
267 __mach_port_destroy (__mach_task_self (), port);
269 if (err)
270 return __hurd_fail (err);
272 /* Below we recalculate GOT to include an increment for each operation
273 allowed on each fd. */
274 got = 0;
276 /* Set the user bitarrays. We only ever have to clear bits, as all desired
277 ones are initially set. */
278 for (i = firstfd; i <= lastfd; ++i)
280 int type = types[i];
282 if ((type & SELECT_RETURNED) == 0)
283 type = 0;
285 if (type & SELECT_READ)
286 got++;
287 else if (readfds)
288 FD_CLR (i, readfds);
289 if (type & SELECT_WRITE)
290 got++;
291 else if (writefds)
292 FD_CLR (i, writefds);
293 if (type & SELECT_URG)
294 got++;
295 else if (exceptfds)
296 FD_CLR (i, exceptfds);
299 return got;
302 weak_alias (__select, select)