source: vendor/emx/current/src/os2/select.c

Last change on this file was 18, checked in by bird, 22 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 14.4 KB
Line 
1/* select.c -- Implement select()
2 Copyright (c) 1993-1998 by Eberhard Mattes
3
4This file is part of emx.
5
6emx is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emx is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emx; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.
20
21As special exception, emx.dll can be distributed without source code
22unless it has been changed. If you modify emx.dll, this exception
23no longer applies and you must remove this paragraph from all source
24files for emx.dll. */
25
26
27#define INCL_DOSDEVIOCTL
28#define INCL_DOSSEMAPHORES
29#define INCL_DOSPROCESS
30#define INCL_DOSMISC
31#define INCL_DOSERRORS
32#include <os2emx.h>
33#include <emx/syscalls.h>
34#include <sys/types.h>
35#include <sys/termio.h>
36#include <sys/time.h>
37#include <sys/errno.h>
38#include "emxdll.h"
39#include "files.h"
40#include "tcpip.h"
41#include "select.h"
42#include "clib.h"
43
44/* Note that select() is not thread-safe for various reasons. */
45
46static HEV npipe_sem;
47static BYTE npipe_sem_open;
48static ULONG mem_size;
49static void *mem;
50
51
52static int select_add_read_npipe (struct select_data *d, int fd)
53{
54 ULONG rc;
55
56 if (!d->sem_npipe_flag)
57 {
58 if (d->sem_count >= d->max_sem)
59 return EINVAL;
60 if (!npipe_sem_open)
61 {
62 if (create_event_sem (&npipe_sem, DC_SEM_SHARED) != 0)
63 return EINVAL;
64 npipe_sem_open = TRUE;
65 }
66 if (reset_event_sem (npipe_sem) != 0)
67 return EINVAL;
68 d->list[d->sem_count].hsemCur = (HSEM)npipe_sem;
69 d->list[d->sem_count].ulUser = 0;
70 ++d->sem_count;
71 d->sem_npipe_flag = TRUE;
72 }
73 rc = DosSetNPipeSem (fd, (HSEM)npipe_sem, 0);
74 if (rc != 0)
75 {
76 error (rc, "DosSetNPipeSem");
77 return EACCES;
78 }
79 return 0;
80}
81
82
83static int select_add_read_con (struct select_data *d, int fd)
84{
85 if (IS_VALID_FILE (fd) && !(GET_FILE (fd)->c_lflag & IDEFAULT)
86 && !d->sem_kbd_flag)
87 {
88 if (d->sem_count >= d->max_sem)
89 return EINVAL;
90 if (reset_event_sem (kbd_sem_new) != 0)
91 return EINVAL;
92 d->list[d->sem_count].hsemCur = (HSEM)kbd_sem_new;
93 d->list[d->sem_count].ulUser = 0;
94 ++d->sem_count;
95 d->sem_kbd_flag = TRUE;
96 }
97 return 0;
98}
99
100
101static int select_add_read_socket (struct select_data *d, int fd)
102{
103 if (!IS_VALID_FILE (fd))
104 return EBADF;
105 d->sockets[d->socket_count] = GET_FILE (fd)->x.socket.handle;
106 d->socketh[d->socket_count] = fd;
107 ++d->socket_count; ++d->socket_nread;
108 return 0;
109}
110
111
112static int select_add_read (struct select_data *d, int fd)
113{
114 ULONG ht;
115
116 if (fd >= handle_count)
117 return EBADF;
118 ht = handle_flags[fd];
119 if (!(ht & HF_OPEN))
120 return EBADF;
121 if (ht & HF_NPIPE)
122 return select_add_read_npipe (d, fd);
123 else if (ht & HF_CON)
124 return select_add_read_con (d, fd);
125 else if (ht & HF_ASYNC)
126 d->async_flag = TRUE;
127 else if (ht & HF_SOCKET)
128 return select_add_read_socket (d, fd);
129 else if (ht & HF_XF86SUP)
130 return xf86sup_select_add_read (d, fd);
131 else
132 {
133 /* Ignore other types of handles */
134 }
135 return 0;
136}
137
138
139static int select_add_write (struct select_data *d, int fd)
140{
141 ULONG ht;
142
143 if (fd >= handle_count)
144 return EBADF;
145 ht = handle_flags[fd];
146 if (!(ht & HF_OPEN))
147 return EBADF;
148 if (ht & HF_ASYNC)
149 d->async_flag = TRUE;
150 else if (ht & HF_SOCKET)
151 {
152 if (!IS_VALID_FILE (fd))
153 return EBADF;
154 d->sockets[d->socket_count] = GET_FILE (fd)->x.socket.handle;
155 d->socketh[d->socket_count] = fd;
156 ++d->socket_count; ++d->socket_nwrite;
157 }
158 else
159 {
160 /* Ignore other types of handles */
161 }
162 return 0;
163}
164
165
166static int select_add_except (struct select_data *d, int fd)
167{
168 ULONG ht;
169
170 if (fd >= handle_count)
171 return EBADF;
172 ht = handle_flags[fd];
173 if (!(ht & HF_OPEN))
174 return EBADF;
175 if (ht & HF_SOCKET)
176 {
177 if (!IS_VALID_FILE (fd))
178 return EBADF;
179 d->sockets[d->socket_count] = GET_FILE (fd)->x.socket.handle;
180 d->socketh[d->socket_count] = fd;
181 ++d->socket_count; ++d->socket_nexcept;
182 }
183 else
184 {
185 /* Ignore other types of handles */
186 }
187 return 0;
188}
189
190
191static int select_init (struct select_data *d, struct _select *args)
192{
193 int fd, e;
194 ULONG rc;
195 struct timeval *tv;
196
197 d->timeout = SEM_INDEFINITE_WAIT;
198 tv = args->timeout;
199 if (tv != NULL)
200 {
201 if (tv->tv_sec < 0 || tv->tv_sec >= 4294967 || tv->tv_usec < 0)
202 return EINVAL;
203 d->timeout = tv->tv_sec * 1000;
204 d->timeout += (tv->tv_usec + 999) / 1000;
205 if (d->timeout != 0)
206 d->start_ms = querysysinfo (QSV_MS_COUNT);
207 }
208 if (args->readfds != NULL)
209 for (fd = 0; fd < args->nfds; ++fd)
210 if (FD_ISSET (fd, args->readfds))
211 {
212 e = select_add_read (d, fd);
213 if (e != 0)
214 return e;
215 }
216 if (args->writefds != NULL)
217 for (fd = 0; fd < args->nfds; ++fd)
218 if (FD_ISSET (fd, args->writefds))
219 {
220 e = select_add_write (d, fd);
221 if (e != 0)
222 return e;
223 }
224 if (args->exceptfds != NULL)
225 for (fd = 0; fd < args->nfds; ++fd)
226 if (FD_ISSET (fd, args->exceptfds))
227 {
228 e = select_add_except (d, fd);
229 if (e != 0)
230 return e;
231 }
232
233 if (d->sem_count >= d->max_sem)
234 return EINVAL;
235 d->list[d->sem_count].hsemCur = (HSEM)signal_sem;
236 d->list[d->sem_count].ulUser = 1;
237 ++d->sem_count;
238 rc = create_muxwait_sem (&d->sem_mux, d->sem_count, d->list, DCMW_WAIT_ANY);
239 if (rc != 0)
240 return EINVAL;
241 d->sem_mux_flag = TRUE;
242 return 0;
243}
244
245
246static void select_check_read (struct select_data *d, int fd)
247{
248 ULONG rc, ht;
249 int dummy_errno;
250
251 ht = handle_flags[fd];
252 if (!(ht & HF_OPEN))
253 return;
254 if (ht & HF_NPIPE)
255 {
256 ULONG buffer, nread, state;
257 AVAILDATA avail;
258
259 rc = DosPeekNPipe (fd, &buffer, 0, &nread, &avail, &state);
260 if (rc != 0)
261 {
262 /* Ignore; TODO? */
263 }
264 else if (avail.cbpipe != 0 || state == NP_STATE_CLOSING)
265 {
266 FD_SET (fd, d->rbits);
267 d->ready_flag = TRUE;
268 }
269 }
270 else if (ht & HF_CON)
271 {
272 /* Note that file descriptors of types unsupported by select()
273 are considered to be always ready. */
274
275 if (!IS_VALID_FILE (fd) || (GET_FILE (fd)->c_lflag & IDEFAULT)
276 || termio_avail (fd) != 0)
277 {
278 FD_SET (fd, d->rbits);
279 d->ready_flag = TRUE;
280 }
281 }
282 else if (ht & HF_ASYNC)
283 {
284 if (async_avail (fd) != 0)
285 {
286 FD_SET (fd, d->rbits);
287 d->ready_flag = TRUE;
288 }
289 }
290 else if (ht & HF_XF86SUP)
291 {
292 if (xf86sup_avail (fd, &dummy_errno) > 0)
293 {
294 FD_SET (fd, d->rbits);
295 d->ready_flag = TRUE;
296 }
297 }
298 else if (ht & HF_SOCKET)
299 {
300 /* Socket handles are handled by tcpip_select_poll(). */
301 }
302 else
303 {
304 /* File descriptors of types unsupported by select() are
305 considered to be always ready. */
306
307 FD_SET (fd, d->rbits);
308 d->ready_flag = TRUE;
309 }
310}
311
312
313static void select_check_write (struct select_data *d, int fd)
314{
315 ULONG ht;
316
317 ht = handle_flags[fd];
318 if (!(ht & HF_OPEN))
319 return;
320
321 if (ht & HF_SOCKET)
322 {
323 /* Socket handles are handled by tcpip_select_poll(). */
324 }
325 else if (ht & HF_ASYNC)
326 {
327 if (async_writable (fd) != 0)
328 {
329 FD_SET (fd, d->wbits);
330 d->ready_flag = TRUE;
331 }
332 }
333 else
334 {
335 /* File descriptors of types unsupported by select() are
336 considered to be always ready. */
337
338 FD_SET (fd, d->wbits);
339 d->ready_flag = TRUE;
340 }
341}
342
343
344/* Poll all handles. Return 0 or errno. */
345
346static int select_poll (struct select_data *d, struct _select *args)
347{
348 int fd;
349
350 memset (d->rbits, 0, d->nbytes);
351 memset (d->wbits, 0, d->nbytes);
352 memset (d->ebits, 0, d->nbytes);
353
354 /* Reset those event semaphores which can be set without data being
355 ready (false alarms). This is required because we might want to
356 block after calling select_poll(). Note that the semaphores must
357 be reset before polling to avoid a time window. */
358
359 if (d->sem_npipe_flag && reset_event_sem (npipe_sem) != 0)
360 return EINVAL;
361
362 if (args->readfds != NULL)
363 {
364 for (fd = 0; fd < args->nfds; ++fd)
365 if (FD_ISSET (fd, args->readfds))
366 select_check_read (d, fd);
367 }
368
369 if (args->writefds != NULL)
370 {
371 for (fd = 0; fd < args->nfds; ++fd)
372 if (FD_ISSET (fd, args->writefds))
373 select_check_write (d, fd);
374 }
375
376 if (d->socket_count != 0)
377 {
378 int n, e;
379
380 n = tcpip_select_poll (d, &e);
381 if (n < 0)
382 return e;
383 }
384
385 if (d->ready_flag)
386 {
387 if (args->readfds != NULL)
388 memcpy (args->readfds, d->rbits, d->nbytes);
389 if (args->writefds != NULL)
390 memcpy (args->writefds, d->wbits, d->nbytes);
391 if (args->exceptfds != NULL)
392 memcpy (args->exceptfds, d->ebits, d->nbytes);
393 d->return_value = 0;
394 for (fd = 0; fd < args->nfds; ++fd)
395 if ((args->readfds != NULL && FD_ISSET (fd, args->readfds))
396 || (args->writefds != NULL && FD_ISSET (fd, args->writefds))
397 || (args->exceptfds != NULL && FD_ISSET (fd, args->exceptfds)))
398 d->return_value += 1;
399 }
400 return 0;
401}
402
403
404#define POLL_INTERVAL 10
405
406/* Wait until a semaphore is posted (pipe ready, keyboard data
407 available, signal) or until the timeout elapses, whichever comes
408 first. Call select_poll() unless a timeout or an error occurs.
409 Return 0 if successful, return errno otherwise. */
410
411static int select_block (struct select_data *d, struct _select *args)
412{
413 ULONG rc, user;
414 ULONG timeout, now, elapsed;
415 int n, e;
416 char poll;
417
418 poll = (char)(d->async_flag || (d->socket_count != 0 && d->timeout != 0));
419 for (;;)
420 {
421 timeout = d->timeout;
422 if (timeout != 0 && timeout != SEM_INDEFINITE_WAIT)
423 {
424 /* This works even if QSV_MS_COUNT wrapped around once since
425 we got d->start_ms, thanks to unsigned arithmetic. A
426 struct timeval which could make QSV_MS_COUNT wrap around
427 more than once is rejected by select_init(). */
428
429 now = querysysinfo (QSV_MS_COUNT);
430 elapsed = now - d->start_ms;
431 if (timeout < elapsed)
432 timeout = 0;
433 else
434 timeout -= elapsed;
435 if (poll && timeout == 0)
436 return 0; /* The time is over */
437 }
438 if (!poll)
439 {
440 /* No sockets or async devices involved, we can really
441 block. */
442
443 rc = DosWaitMuxWaitSem (d->sem_mux, timeout, &user);
444 if (rc == ERROR_INTERRUPT || sig_flag)
445 return EINTR;
446 else if (rc == ERROR_TIMEOUT)
447 return 0;
448 else if (rc != 0)
449 {
450 error (rc, "DosWaitMuxWaitSem");
451 return EINVAL;
452 }
453 else
454 {
455 e = select_poll (d, args);
456 if (e != 0 || timeout == 0 || d->ready_flag)
457 return e;
458 }
459 }
460 else
461 {
462 /* Poll sockets. */
463
464 if (d->socket_count != 0)
465 {
466 n = tcpip_select_poll (d, &e);
467 if (n < 0)
468 return e;
469 if (n > 0)
470 return select_poll (d, args);
471 }
472
473 /* Sleep for a few ms, waking up as soon as d->sem_mux is
474 posted. This way we don't have to poll all the other
475 file descriptors. */
476
477 if (timeout > POLL_INTERVAL || timeout == SEM_INDEFINITE_WAIT)
478 timeout = POLL_INTERVAL;
479 rc = DosWaitMuxWaitSem (d->sem_mux, timeout, &user);
480 if (rc == ERROR_INTERRUPT || sig_flag)
481 return EINTR;
482 else if (rc == 0 || (rc == ERROR_TIMEOUT && d->async_flag))
483 {
484 e = select_poll (d, args);
485 if (e != 0 || timeout == 0 || d->ready_flag)
486 return e;
487 }
488 else if (rc != ERROR_TIMEOUT)
489 {
490 error (rc, "DosWaitMuxWaitSem");
491 return EINVAL;
492 }
493 }
494
495 }
496}
497
498
499static void select_cleanup (struct select_data *d)
500{
501 if (d->sem_mux_flag)
502 close_muxwait_sem (d->sem_mux);
503}
504
505
506int do_select (struct _select *args, int *errnop)
507{
508 struct select_data d;
509 int e;
510 char *a;
511 ULONG size, rc;
512
513 sig_block_start ();
514 d.td = get_thread ();
515 d.sem_npipe_flag = FALSE; d.sem_kbd_flag = FALSE; d.sem_mux_flag = FALSE;
516 d.sem_count = 0; d.ready_flag = FALSE; d.return_value = 0;
517 d.socket_count = 0; d.socket_nread = d.socket_nwrite = d.socket_nexcept = 0;
518 d.async_flag = FALSE;
519 d.nbytes = 4 * ((args->nfds + 31) / 32);
520 d.max_sem = args->nfds + 1;
521 if (d.nbytes < 256 / 8)
522 d.nbytes = 256 / 8;
523 size = (3 * d.nbytes + 3 * args->nfds * sizeof (int)
524 + d.max_sem * sizeof (SEMRECORD));
525 if (size > mem_size)
526 {
527 if (mem != NULL)
528 private_free (mem);
529 mem_size = size;
530 rc = private_alloc (&mem, mem_size);
531 if (rc != 0)
532 {
533 *errnop = set_error (rc);
534 return -1;
535 }
536 }
537 a = mem;
538 d.rbits = (fd_set *)a; a += d.nbytes;
539 d.wbits = (fd_set *)a; a += d.nbytes;
540 d.ebits = (fd_set *)a; a += d.nbytes;
541 d.sockets = (int *)a; a += args->nfds * sizeof (int);
542 d.socketh = (int *)a; a += args->nfds * sizeof (int);
543 d.sockett = (int *)a; a += args->nfds * sizeof (int);
544 d.list = (SEMRECORD *)a; /* a += d.max_sem * sizeof (SEMRECORD); */
545 e = select_init (&d, args);
546 if (e == 0)
547 {
548 e = select_poll (&d, args);
549 /* Block if no handle is ready and the requested timeout is > 0
550 or indefinite. */
551 if (e == 0 && !d.ready_flag && d.timeout != 0)
552 e = select_block (&d, args);
553 }
554
555 /* If no handles are ready (time-out), we have to clear all sets. */
556
557 if (e == 0 && !d.ready_flag && !(debug_flags & DEBUG_NOFDZERO))
558 {
559 if (args->readfds != NULL)
560 memset (args->readfds, 0, d.nbytes);
561 if (args->writefds != NULL)
562 memset (args->writefds, 0, d.nbytes);
563 if (args->exceptfds != NULL)
564 memset (args->exceptfds, 0, d.nbytes);
565 }
566
567 select_cleanup (&d);
568 sig_block_end ();
569 *errnop = e;
570 return (e == 0 ? d.return_value : -1);
571}
Note: See TracBrowser for help on using the repository browser.