Merge from vendor branch BINUTILS:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / bind / isc / eventlib.c
1 /*
2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1995-1999 by Internet Software Consortium
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* eventlib.c - implement glue for the eventlib
19  * vix 09sep95 [initial]
20  */
21
22 #if !defined(LINT) && !defined(CODECENTER)
23 static const char rcsid[] = "$Id: eventlib.c,v 1.2.2.3 2004/03/17 01:54:22 marka Exp $";
24 #endif
25
26 #include "port_before.h"
27 #include "fd_setsize.h"
28
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <sys/stat.h>
32
33 #include <errno.h>
34 #include <signal.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38
39 #include <isc/eventlib.h>
40 #include <isc/assertions.h>
41 #include "eventlib_p.h"
42
43 #include "port_after.h"
44
45 /* Forward. */
46
47 #ifdef NEED_PSELECT
48 static int              pselect(int, void *, void *, void *,
49                                 struct timespec *,
50                                 const sigset_t *);
51 #endif
52
53 /* Public. */
54
55 int
56 evCreate(evContext *opaqueCtx) {
57         evContext_p *ctx;
58
59         /* Make sure the memory heap is initialized. */
60         if (meminit(0, 0) < 0 && errno != EEXIST)
61                 return (-1);
62
63         OKNEW(ctx);
64
65         /* Global. */
66         ctx->cur = NULL;
67
68         /* Debugging. */
69         ctx->debug = 0;
70         ctx->output = NULL;
71
72         /* Connections. */
73         ctx->conns = NULL;
74         INIT_LIST(ctx->accepts);
75
76         /* Files. */
77         ctx->files = NULL;
78         FD_ZERO(&ctx->rdNext);
79         FD_ZERO(&ctx->wrNext);
80         FD_ZERO(&ctx->exNext);
81         FD_ZERO(&ctx->nonblockBefore);
82         ctx->fdMax = -1;
83         ctx->fdNext = NULL;
84         ctx->fdCount = 0;       /* Invalidate {rd,wr,ex}Last. */
85         ctx->highestFD = FD_SETSIZE - 1;
86 #ifdef EVENTLIB_TIME_CHECKS
87         ctx->lastFdCount = 0;
88 #endif
89         memset(ctx->fdTable, 0, sizeof ctx->fdTable);
90
91         /* Streams. */
92         ctx->streams = NULL;
93         ctx->strDone = NULL;
94         ctx->strLast = NULL;
95
96         /* Timers. */
97         ctx->lastEventTime = evNowTime();
98 #ifdef EVENTLIB_TIME_CHECKS
99         ctx->lastSelectTime = ctx->lastEventTime;
100 #endif
101         ctx->timers = evCreateTimers(ctx);
102         if (ctx->timers == NULL)
103                 return (-1);
104
105         /* Waits. */
106         ctx->waitLists = NULL;
107         ctx->waitDone.first = ctx->waitDone.last = NULL;
108         ctx->waitDone.prev = ctx->waitDone.next = NULL;
109
110         opaqueCtx->opaque = ctx;
111         return (0);
112 }
113
114 void
115 evSetDebug(evContext opaqueCtx, int level, FILE *output) {
116         evContext_p *ctx = opaqueCtx.opaque;
117
118         ctx->debug = level;
119         ctx->output = output;
120 }
121
122 int
123 evDestroy(evContext opaqueCtx) {
124         evContext_p *ctx = opaqueCtx.opaque;
125         int revs = 424242;      /* Doug Adams. */
126         evWaitList *this_wl, *next_wl;
127         evWait *this_wait, *next_wait;
128
129         /* Connections. */
130         while (revs-- > 0 && ctx->conns != NULL) {
131                 evConnID id;
132
133                 id.opaque = ctx->conns;
134                 (void) evCancelConn(opaqueCtx, id);
135         }
136         INSIST(revs >= 0);
137
138         /* Streams. */
139         while (revs-- > 0 && ctx->streams != NULL) {
140                 evStreamID id;
141
142                 id.opaque = ctx->streams;
143                 (void) evCancelRW(opaqueCtx, id);
144         }
145
146         /* Files. */
147         while (revs-- > 0 && ctx->files != NULL) {
148                 evFileID id;
149
150                 id.opaque = ctx->files;
151                 (void) evDeselectFD(opaqueCtx, id);
152         }
153         INSIST(revs >= 0);
154
155         /* Timers. */
156         evDestroyTimers(ctx);
157
158         /* Waits. */
159         for (this_wl = ctx->waitLists;
160              revs-- > 0 && this_wl != NULL;
161              this_wl = next_wl) {
162                 next_wl = this_wl->next;
163                 for (this_wait = this_wl->first;
164                      revs-- > 0 && this_wait != NULL;
165                      this_wait = next_wait) {
166                         next_wait = this_wait->next;
167                         FREE(this_wait);
168                 }
169                 FREE(this_wl);
170         }
171         for (this_wait = ctx->waitDone.first;
172              revs-- > 0 && this_wait != NULL;
173              this_wait = next_wait) {
174                 next_wait = this_wait->next;
175                 FREE(this_wait);
176         }
177
178         FREE(ctx);
179         return (0);
180 }
181
182 int
183 evGetNext(evContext opaqueCtx, evEvent *opaqueEv, int options) {
184         evContext_p *ctx = opaqueCtx.opaque;
185         struct timespec nextTime;
186         evTimer *nextTimer;
187         evEvent_p *new;
188         int x, pselect_errno, timerPast;
189 #ifdef EVENTLIB_TIME_CHECKS
190         struct timespec interval;
191 #endif
192
193         /* Ensure that exactly one of EV_POLL or EV_WAIT was specified. */
194         x = ((options & EV_POLL) != 0) + ((options & EV_WAIT) != 0);
195         if (x != 1)
196                 EV_ERR(EINVAL);
197
198         /* Get the time of day.  We'll do this again after select() blocks. */
199         ctx->lastEventTime = evNowTime();
200
201  again:
202         /* Finished accept()'s do not require a select(). */
203         if (!EMPTY(ctx->accepts)) {
204                 OKNEW(new);
205                 new->type = Accept;
206                 new->u.accept.this = HEAD(ctx->accepts);
207                 UNLINK(ctx->accepts, HEAD(ctx->accepts), link);
208                 opaqueEv->opaque = new;
209                 return (0);
210         }
211
212         /* Stream IO does not require a select(). */
213         if (ctx->strDone != NULL) {
214                 OKNEW(new);
215                 new->type = Stream;
216                 new->u.stream.this = ctx->strDone;
217                 ctx->strDone = ctx->strDone->nextDone;
218                 if (ctx->strDone == NULL)
219                         ctx->strLast = NULL;
220                 opaqueEv->opaque = new;
221                 return (0);
222         }
223
224         /* Waits do not require a select(). */
225         if (ctx->waitDone.first != NULL) {
226                 OKNEW(new);
227                 new->type = Wait;
228                 new->u.wait.this = ctx->waitDone.first;
229                 ctx->waitDone.first = ctx->waitDone.first->next;
230                 if (ctx->waitDone.first == NULL)
231                         ctx->waitDone.last = NULL;
232                 opaqueEv->opaque = new;
233                 return (0);
234         }
235
236         /* Get the status and content of the next timer. */
237         if ((nextTimer = heap_element(ctx->timers, 1)) != NULL) {
238                 nextTime = nextTimer->due;
239                 timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
240         } else
241                 timerPast = 0;  /* Make gcc happy. */
242
243         evPrintf(ctx, 9, "evGetNext: fdCount %d\n", ctx->fdCount);
244         if (ctx->fdCount == 0) {
245                 static const struct timespec NoTime = {0, 0L};
246                 enum { JustPoll, Block, Timer } m;
247                 struct timespec t, *tp;
248
249                 /* Are there any events at all? */
250                 if ((options & EV_WAIT) != 0 && !nextTimer && ctx->fdMax == -1)
251                         EV_ERR(ENOENT);
252
253                 /* Figure out what select()'s timeout parameter should be. */
254                 if ((options & EV_POLL) != 0) {
255                         m = JustPoll;
256                         t = NoTime;
257                         tp = &t;
258                 } else if (nextTimer == NULL) {
259                         m = Block;
260                         /* ``t'' unused. */
261                         tp = NULL;
262                 } else if (timerPast) {
263                         m = JustPoll;
264                         t = NoTime;
265                         tp = &t;
266                 } else {
267                         m = Timer;
268                         /* ``t'' filled in later. */
269                         tp = &t;
270                 }
271 #ifdef EVENTLIB_TIME_CHECKS
272                 if (ctx->debug > 0) {
273                         interval = evSubTime(ctx->lastEventTime,
274                                              ctx->lastSelectTime);
275                         if (interval.tv_sec > 0 || interval.tv_nsec > 0)
276                                 evPrintf(ctx, 1,
277                                    "time between pselect() %u.%09u count %d\n",
278                                          interval.tv_sec, interval.tv_nsec,
279                                          ctx->lastFdCount);
280                 }
281 #endif
282                 do {
283                         /* XXX need to copy only the bits we are using. */
284                         ctx->rdLast = ctx->rdNext;
285                         ctx->wrLast = ctx->wrNext;
286                         ctx->exLast = ctx->exNext;
287
288                         if (m == Timer) {
289                                 INSIST(tp == &t);
290                                 t = evSubTime(nextTime, ctx->lastEventTime);
291                         }
292
293                         evPrintf(ctx, 4,
294                                 "pselect(%d, 0x%lx, 0x%lx, 0x%lx, %ld.%09ld)\n",
295                                  ctx->fdMax+1,
296                                  (u_long)ctx->rdLast.fds_bits[0],
297                                  (u_long)ctx->wrLast.fds_bits[0],
298                                  (u_long)ctx->exLast.fds_bits[0],
299                                  tp ? (long)tp->tv_sec : -1L,
300                                  tp ? tp->tv_nsec : -1);
301
302                         /* XXX should predict system's earliness and adjust. */
303                         x = pselect(ctx->fdMax+1,
304                                     &ctx->rdLast, &ctx->wrLast, &ctx->exLast,
305                                     tp, NULL);
306                         pselect_errno = errno;
307
308                         evPrintf(ctx, 4, "select() returns %d (err: %s)\n",
309                                  x, (x == -1) ? strerror(errno) : "none");
310
311                         /* Anything but a poll can change the time. */
312                         if (m != JustPoll)
313                                 ctx->lastEventTime = evNowTime();
314
315                         /* Select() likes to finish about 10ms early. */
316                 } while (x == 0 && m == Timer &&
317                          evCmpTime(ctx->lastEventTime, nextTime) < 0);
318 #ifdef EVENTLIB_TIME_CHECKS
319                 ctx->lastSelectTime = ctx->lastEventTime;
320 #endif
321                 if (x < 0) {
322                         if (pselect_errno == EINTR) {
323                                 if ((options & EV_NULL) != 0)
324                                         goto again;
325                                 OKNEW(new);
326                                 new->type = Null;
327                                 /* No data. */
328                                 opaqueEv->opaque = new;
329                                 return (0);
330                         }
331                         if (pselect_errno == EBADF) {
332                                 for (x = 0; x <= ctx->fdMax; x++) {
333                                         struct stat sb;
334
335                                         if (FD_ISSET(x, &ctx->rdNext) == 0 &&
336                                             FD_ISSET(x, &ctx->wrNext) == 0 &&
337                                             FD_ISSET(x, &ctx->exNext) == 0)
338                                                 continue;
339                                         if (fstat(x, &sb) == -1 &&
340                                             errno == EBADF)
341                                                 evPrintf(ctx, 1, "EBADF: %d\n",
342                                                          x);
343                                 }
344                                 abort();
345                         }
346                         EV_ERR(pselect_errno);
347                 }
348                 if (x == 0 && (nextTimer == NULL || !timerPast) &&
349                     (options & EV_POLL))
350                         EV_ERR(EWOULDBLOCK);
351                 ctx->fdCount = x;
352 #ifdef EVENTLIB_TIME_CHECKS
353                 ctx->lastFdCount = x;
354 #endif
355         }
356         INSIST(nextTimer || ctx->fdCount);
357
358         /* Timers go first since we'd like them to be accurate. */
359         if (nextTimer && !timerPast) {
360                 /* Has anything happened since we blocked? */
361                 timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
362         }
363         if (nextTimer && timerPast) {
364                 OKNEW(new);
365                 new->type = Timer;
366                 new->u.timer.this = nextTimer;
367                 opaqueEv->opaque = new;
368                 return (0);
369         }
370
371         /* No timers, so there should be a ready file descriptor. */
372         x = 0;
373         while (ctx->fdCount > 0) {
374                 evFile *fid;
375                 int fd, eventmask;
376
377                 if (ctx->fdNext == NULL) {
378                         if (++x == 2) {
379                                 /*
380                                  * Hitting the end twice means that the last
381                                  * select() found some FD's which have since
382                                  * been deselected.
383                                  *
384                                  * On some systems, the count returned by
385                                  * selects is the total number of bits in
386                                  * all masks that are set, and on others it's
387                                  * the number of fd's that have some bit set,
388                                  * and on others, it's just broken.  We 
389                                  * always assume that it's the number of
390                                  * bits set in all masks, because that's what
391                                  * the man page says it should do, and
392                                  * the worst that can happen is we do an
393                                  * extra select().
394                                  */
395                                 ctx->fdCount = 0;
396                                 break;
397                         }
398                         ctx->fdNext = ctx->files;
399                 }
400                 fid = ctx->fdNext;
401                 ctx->fdNext = fid->next;
402
403                 fd = fid->fd;
404                 eventmask = 0;
405                 if (FD_ISSET(fd, &ctx->rdLast))
406                         eventmask |= EV_READ;
407                 if (FD_ISSET(fd, &ctx->wrLast))
408                         eventmask |= EV_WRITE;
409                 if (FD_ISSET(fd, &ctx->exLast))
410                         eventmask |= EV_EXCEPT;
411                 eventmask &= fid->eventmask;
412                 if (eventmask != 0) {
413                         if ((eventmask & EV_READ) != 0) {
414                                 FD_CLR(fd, &ctx->rdLast);
415                                 ctx->fdCount--;
416                         }
417                         if ((eventmask & EV_WRITE) != 0) {
418                                 FD_CLR(fd, &ctx->wrLast);
419                                 ctx->fdCount--;
420                         }
421                         if ((eventmask & EV_EXCEPT) != 0) {
422                                 FD_CLR(fd, &ctx->exLast);
423                                 ctx->fdCount--;
424                         }
425                         OKNEW(new);
426                         new->type = File;
427                         new->u.file.this = fid;
428                         new->u.file.eventmask = eventmask;
429                         opaqueEv->opaque = new;
430                         return (0);
431                 }
432         }
433         if (ctx->fdCount < 0) {
434                 /*
435                  * select()'s count is off on a number of systems, and
436                  * can result in fdCount < 0.
437                  */
438                 evPrintf(ctx, 4, "fdCount < 0 (%d)\n", ctx->fdCount);
439                 ctx->fdCount = 0;
440         }
441
442         /* We get here if the caller deselect()'s an FD. Gag me with a goto. */
443         goto again;
444 }
445
446 int
447 evDispatch(evContext opaqueCtx, evEvent opaqueEv) {
448         evContext_p *ctx = opaqueCtx.opaque;
449         evEvent_p *ev = opaqueEv.opaque;
450 #ifdef EVENTLIB_TIME_CHECKS
451         void *func;
452         struct timespec start_time;
453         struct timespec interval;
454 #endif
455
456 #ifdef EVENTLIB_TIME_CHECKS
457         if (ctx->debug > 0)
458                 start_time = evNowTime();
459 #endif
460         ctx->cur = ev;
461         switch (ev->type) {
462             case Accept: {
463                 evAccept *this = ev->u.accept.this;
464
465                 evPrintf(ctx, 5,
466                         "Dispatch.Accept: fd %d -> %d, func %p, uap %p\n",
467                          this->conn->fd, this->fd,
468                          this->conn->func, this->conn->uap);
469                 errno = this->ioErrno;
470                 (this->conn->func)(opaqueCtx, this->conn->uap, this->fd,
471                                    &this->la, this->lalen,
472                                    &this->ra, this->ralen);
473 #ifdef EVENTLIB_TIME_CHECKS
474                 func = this->conn->func;
475 #endif
476                 break;
477             }
478             case File: {
479                 evFile *this = ev->u.file.this;
480                 int eventmask = ev->u.file.eventmask;
481
482                 evPrintf(ctx, 5,
483                         "Dispatch.File: fd %d, mask 0x%x, func %p, uap %p\n",
484                          this->fd, this->eventmask, this->func, this->uap);
485                 (this->func)(opaqueCtx, this->uap, this->fd, eventmask);
486 #ifdef EVENTLIB_TIME_CHECKS
487                 func = this->func;
488 #endif
489                 break;
490             }
491             case Stream: {
492                 evStream *this = ev->u.stream.this;
493
494                 evPrintf(ctx, 5,
495                          "Dispatch.Stream: fd %d, func %p, uap %p\n",
496                          this->fd, this->func, this->uap);
497                 errno = this->ioErrno;
498                 (this->func)(opaqueCtx, this->uap, this->fd, this->ioDone);
499 #ifdef EVENTLIB_TIME_CHECKS
500                 func = this->func;
501 #endif
502                 break;
503             }
504             case Timer: {
505                 evTimer *this = ev->u.timer.this;
506
507                 evPrintf(ctx, 5, "Dispatch.Timer: func %p, uap %p\n",
508                          this->func, this->uap);
509                 (this->func)(opaqueCtx, this->uap, this->due, this->inter);
510 #ifdef EVENTLIB_TIME_CHECKS
511                 func = this->func;
512 #endif
513                 break;
514             }
515             case Wait: {
516                 evWait *this = ev->u.wait.this;
517
518                 evPrintf(ctx, 5,
519                          "Dispatch.Wait: tag %p, func %p, uap %p\n",
520                          this->tag, this->func, this->uap);
521                 (this->func)(opaqueCtx, this->uap, this->tag);
522 #ifdef EVENTLIB_TIME_CHECKS
523                 func = this->func;
524 #endif
525                 break;
526             }
527             case Null: {
528                 /* No work. */
529 #ifdef EVENTLIB_TIME_CHECKS
530                 func = NULL;
531 #endif
532                 break;
533             }
534             default: {
535                 abort();
536             }
537         }
538 #ifdef EVENTLIB_TIME_CHECKS
539         if (ctx->debug > 0) {
540                 interval = evSubTime(evNowTime(), start_time);
541                 /* 
542                  * Complain if it took longer than 50 milliseconds.
543                  *
544                  * We call getuid() to make an easy to find mark in a kernel
545                  * trace.
546                  */
547                 if (interval.tv_sec > 0 || interval.tv_nsec > 50000000)
548                         evPrintf(ctx, 1,
549                          "dispatch interval %u.%09u uid %d type %d func %p\n",
550                                  interval.tv_sec, interval.tv_nsec,
551                                  getuid(), ev->type, func);
552         }
553 #endif
554         ctx->cur = NULL;
555         evDrop(opaqueCtx, opaqueEv);
556         return (0);
557 }
558
559 void
560 evDrop(evContext opaqueCtx, evEvent opaqueEv) {
561         evContext_p *ctx = opaqueCtx.opaque;
562         evEvent_p *ev = opaqueEv.opaque;
563
564         switch (ev->type) {
565             case Accept: {
566                 FREE(ev->u.accept.this);
567                 break;
568             }
569             case File: {
570                 /* No work. */
571                 break;
572             }
573             case Stream: {
574                 evStreamID id;
575
576                 id.opaque = ev->u.stream.this;
577                 (void) evCancelRW(opaqueCtx, id);
578                 break;
579             }
580             case Timer: {
581                 evTimer *this = ev->u.timer.this;
582                 evTimerID opaque;
583
584                 /* Check to see whether the user func cleared the timer. */
585                 if (heap_element(ctx->timers, this->index) != this) {
586                         evPrintf(ctx, 5, "Dispatch.Timer: timer rm'd?\n");
587                         break;
588                 }
589                 /*
590                  * Timer is still there.  Delete it if it has expired,
591                  * otherwise set it according to its next interval.
592                  */
593                 if (this->inter.tv_sec == (time_t)0 &&
594                     this->inter.tv_nsec == 0L) {
595                         opaque.opaque = this;                   
596                         (void) evClearTimer(opaqueCtx, opaque);
597                 } else {
598                         opaque.opaque = this;
599                         (void) evResetTimer(opaqueCtx, opaque, this->func,
600                                             this->uap,
601                                             evAddTime((this->mode & EV_TMR_RATE) ?
602                                                       this->due :
603                                                       ctx->lastEventTime,
604                                                       this->inter),
605                                             this->inter);
606                 }
607                 break;
608             }
609             case Wait: {
610                 FREE(ev->u.wait.this);
611                 break;
612             }
613             case Null: {
614                 /* No work. */
615                 break;
616             }
617             default: {
618                 abort();
619             }
620         }
621         FREE(ev);
622 }
623
624 int
625 evMainLoop(evContext opaqueCtx) {
626         evEvent event;
627         int x;
628
629         while ((x = evGetNext(opaqueCtx, &event, EV_WAIT)) == 0)
630                 if ((x = evDispatch(opaqueCtx, event)) < 0)
631                         break;
632         return (x);
633 }
634
635 int
636 evHighestFD(evContext opaqueCtx) {
637         evContext_p *ctx = opaqueCtx.opaque;
638
639         return (ctx->highestFD);
640 }
641
642 void
643 evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) {
644         va_list ap;
645
646         va_start(ap, fmt);
647         if (ctx->output != NULL && ctx->debug >= level) {
648                 vfprintf(ctx->output, fmt, ap);
649                 fflush(ctx->output);
650         }
651         va_end(ap);
652 }
653
654 int
655 evSetOption(evContext *opaqueCtx, const char *option, int value) {
656         /* evContext_p *ctx = opaqueCtx->opaque; */
657
658         UNUSED(opaqueCtx);
659         UNUSED(value);
660 #ifndef CLOCK_MONOTONIC
661         UNUSED(option);
662 #endif 
663
664 #ifdef CLOCK_MONOTONIC
665         if (strcmp(option, "monotime") == 0) {
666                 if (opaqueCtx  != NULL)
667                         errno = EINVAL;
668                 if (value == 0 || value == 1) {
669                         __evOptMonoTime = value;
670                         return (0);
671                 } else {
672                         errno = EINVAL;
673                         return (-1);
674                 }
675         } 
676 #endif
677         errno = ENOENT;
678         return (-1);
679 }
680
681 int
682 evGetOption(evContext *opaqueCtx, const char *option, int *value) {
683         /* evContext_p *ctx = opaqueCtx->opaque; */
684
685         UNUSED(opaqueCtx);
686 #ifndef CLOCK_MONOTONIC
687         UNUSED(value);
688         UNUSED(option);
689 #endif 
690
691 #ifdef CLOCK_MONOTONIC
692         if (strcmp(option, "monotime") == 0) {
693                 if (opaqueCtx  != NULL)
694                         errno = EINVAL;
695                 *value = __evOptMonoTime;
696                 return (0);
697         }
698 #endif
699         errno = ENOENT;
700         return (-1);
701 }
702
703 #ifdef NEED_PSELECT
704 /* XXX needs to move to the porting library. */
705 static int
706 pselect(int nfds, void *rfds, void *wfds, void *efds,
707         struct timespec *tsp,
708         const sigset_t *sigmask)
709 {
710         struct timeval tv, *tvp;
711         sigset_t sigs;
712         int n;
713
714         if (tsp) {
715                 tvp = &tv;
716                 tv = evTimeVal(*tsp);
717         } else
718                 tvp = NULL;
719         if (sigmask)
720                 sigprocmask(SIG_SETMASK, sigmask, &sigs);
721         n = select(nfds, rfds, wfds, efds, tvp);
722         if (sigmask)
723                 sigprocmask(SIG_SETMASK, &sigs, NULL);
724         if (tsp)
725                 *tsp = evTimeSpec(tv);
726         return (n);
727 }
728 #endif