1: /* fdwatch.c - fd watcher routines, either select() or poll()
2: **
3: ** Copyright © 1999,2000 by Jef Poskanzer <jef@mail.acme.com>.
4: ** All rights reserved.
5: **
6: ** Redistribution and use in source and binary forms, with or without
7: ** modification, are permitted provided that the following conditions
8: ** are met:
9: ** 1. Redistributions of source code must retain the above copyright
10: ** notice, this list of conditions and the following disclaimer.
11: ** 2. Redistributions in binary form must reproduce the above copyright
12: ** notice, this list of conditions and the following disclaimer in the
13: ** documentation and/or other materials provided with the distribution.
14: **
15: ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16: ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17: ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18: ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19: ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20: ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21: ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22: ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23: ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24: ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25: ** SUCH DAMAGE.
26: */
27:
28: #include <sys/types.h>
29: #include <stdlib.h>
30: #include <unistd.h>
31: #include <string.h>
32: #include <sys/time.h>
33: #include <sys/resource.h>
34: #include <syslog.h>
35: #include <fcntl.h>
36:
37: #ifndef MIN
38: #define MIN(a,b) ((a) < (b) ? (a) : (b))
39: #endif
40:
41: #ifdef HAVE_POLL_H
42: #include <poll.h>
43: #else /* HAVE_POLL_H */
44: #ifdef HAVE_SYS_POLL_H
45: #include <sys/poll.h>
46: #endif /* HAVE_SYS_POLL_H */
47: #endif /* HAVE_POLL_H */
48:
49: #ifdef HAVE_SYS_DEVPOLL_H
50: #include <sys/devpoll.h>
51: #ifndef HAVE_DEVPOLL
52: #define HAVE_DEVPOLL
53: #endif /* !HAVE_DEVPOLL */
54: #endif /* HAVE_SYS_DEVPOLL_H */
55:
56: #ifdef HAVE_SYS_EVENT_H
57: #include <sys/event.h>
58: #endif /* HAVE_SYS_EVENT_H */
59:
60: #include "fdwatch.h"
61:
62: #ifdef HAVE_SELECT
63: #ifndef FD_SET
64: #define NFDBITS 32
65: #define FD_SETSIZE 32
66: #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
67: #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
68: #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
69: #define FD_ZERO(p) bzero((char*)(p), sizeof(*(p)))
70: #endif /* !FD_SET */
71: #endif /* HAVE_SELECT */
72:
73: static int nfiles;
74: static long nwatches;
75: static int* fd_rw;
76: static void** fd_data;
77: static int nreturned, next_ridx;
78:
79: #ifdef HAVE_KQUEUE
80:
81: #define WHICH "kevent"
82: #define INIT( nfiles ) kqueue_init( nfiles )
83: #define ADD_FD( fd, rw ) kqueue_add_fd( fd, rw )
84: #define DEL_FD( fd ) kqueue_del_fd( fd )
85: #define WATCH( timeout_msecs ) kqueue_watch( timeout_msecs )
86: #define CHECK_FD( fd ) kqueue_check_fd( fd )
87: #define GET_FD( ridx ) kqueue_get_fd( ridx )
88:
89: static int kqueue_init( int nfiles );
90: static void kqueue_add_fd( int fd, int rw );
91: static void kqueue_del_fd( int fd );
92: static int kqueue_watch( long timeout_msecs );
93: static int kqueue_check_fd( int fd );
94: static int kqueue_get_fd( int ridx );
95:
96: #else /* HAVE_KQUEUE */
97: # ifdef HAVE_DEVPOLL
98:
99: #define WHICH "devpoll"
100: #define INIT( nfiles ) devpoll_init( nfiles )
101: #define ADD_FD( fd, rw ) devpoll_add_fd( fd, rw )
102: #define DEL_FD( fd ) devpoll_del_fd( fd )
103: #define WATCH( timeout_msecs ) devpoll_watch( timeout_msecs )
104: #define CHECK_FD( fd ) devpoll_check_fd( fd )
105: #define GET_FD( ridx ) devpoll_get_fd( ridx )
106:
107: static int devpoll_init( int nfiles );
108: static void devpoll_add_fd( int fd, int rw );
109: static void devpoll_del_fd( int fd );
110: static int devpoll_watch( long timeout_msecs );
111: static int devpoll_check_fd( int fd );
112: static int devpoll_get_fd( int ridx );
113:
114: # else /* HAVE_DEVPOLL */
115: # ifdef HAVE_POLL
116:
117: #define WHICH "poll"
118: #define INIT( nfiles ) poll_init( nfiles )
119: #define ADD_FD( fd, rw ) poll_add_fd( fd, rw )
120: #define DEL_FD( fd ) poll_del_fd( fd )
121: #define WATCH( timeout_msecs ) poll_watch( timeout_msecs )
122: #define CHECK_FD( fd ) poll_check_fd( fd )
123: #define GET_FD( ridx ) poll_get_fd( ridx )
124:
125: static int poll_init( int nfiles );
126: static void poll_add_fd( int fd, int rw );
127: static void poll_del_fd( int fd );
128: static int poll_watch( long timeout_msecs );
129: static int poll_check_fd( int fd );
130: static int poll_get_fd( int ridx );
131:
132: # else /* HAVE_POLL */
133: # ifdef HAVE_SELECT
134:
135: #define WHICH "select"
136: #define INIT( nfiles ) select_init( nfiles )
137: #define ADD_FD( fd, rw ) select_add_fd( fd, rw )
138: #define DEL_FD( fd ) select_del_fd( fd )
139: #define WATCH( timeout_msecs ) select_watch( timeout_msecs )
140: #define CHECK_FD( fd ) select_check_fd( fd )
141: #define GET_FD( ridx ) select_get_fd( ridx )
142:
143: static int select_init( int nfiles );
144: static void select_add_fd( int fd, int rw );
145: static void select_del_fd( int fd );
146: static int select_watch( long timeout_msecs );
147: static int select_check_fd( int fd );
148: static int select_get_fd( int ridx );
149:
150: # endif /* HAVE_SELECT */
151: # endif /* HAVE_POLL */
152: # endif /* HAVE_DEVPOLL */
153: #endif /* HAVE_KQUEUE */
154:
155:
156: /* Routines. */
157:
158: /* Figure out how many file descriptors the system allows, and
159: ** initialize the fdwatch data structures. Returns -1 on failure.
160: */
161: int
162: fdwatch_get_nfiles( void )
163: {
164: int i;
165: #ifdef RLIMIT_NOFILE
166: struct rlimit rl;
167: #endif /* RLIMIT_NOFILE */
168:
169: /* Figure out how many fd's we can have. */
170: nfiles = getdtablesize();
171: #ifdef RLIMIT_NOFILE
172: /* If we have getrlimit(), use that, and attempt to raise the limit. */
173: if ( getrlimit( RLIMIT_NOFILE, &rl ) == 0 )
174: {
175: nfiles = rl.rlim_cur;
176: if ( rl.rlim_max == RLIM_INFINITY )
177: rl.rlim_cur = 8192; /* arbitrary */
178: else if ( rl.rlim_max > rl.rlim_cur )
179: rl.rlim_cur = rl.rlim_max;
180: if ( setrlimit( RLIMIT_NOFILE, &rl ) == 0 )
181: nfiles = rl.rlim_cur;
182: }
183: #endif /* RLIMIT_NOFILE */
184:
185: #if defined(HAVE_SELECT) && ! ( defined(HAVE_POLL) || defined(HAVE_DEVPOLL) || defined(HAVE_KQUEUE) )
186: /* If we use select(), then we must limit ourselves to FD_SETSIZE. */
187: nfiles = MIN( nfiles, FD_SETSIZE );
188: #endif /* HAVE_SELECT && ! ( HAVE_POLL || HAVE_DEVPOLL || HAVE_KQUEUE ) */
189:
190: /* Initialize the fdwatch data structures. */
191: nwatches = 0;
192: fd_rw = (int*) malloc( sizeof(int) * nfiles );
193: fd_data = (void**) malloc( sizeof(void*) * nfiles );
194: if ( fd_rw == (int*) 0 || fd_data == (void**) 0 )
195: return -1;
196: for ( i = 0; i < nfiles; ++i )
197: fd_rw[i] = -1;
198: if ( INIT( nfiles ) == -1 )
199: return -1;
200:
201: return nfiles;
202: }
203:
204:
205: /* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */
206: void
207: fdwatch_add_fd( int fd, void* client_data, int rw )
208: {
209: if ( fd < 0 || fd >= nfiles || fd_rw[fd] != -1 )
210: {
211: syslog( LOG_ERR, "bad fd (%d) passed to fdwatch_add_fd!", fd );
212: return;
213: }
214: ADD_FD( fd, rw );
215: fd_rw[fd] = rw;
216: fd_data[fd] = client_data;
217: }
218:
219:
220: /* Remove a descriptor from the watch list. */
221: void
222: fdwatch_del_fd( int fd )
223: {
224: if ( fd < 0 || fd >= nfiles || fd_rw[fd] == -1 )
225: {
226: syslog( LOG_ERR, "bad fd (%d) passed to fdwatch_del_fd!", fd );
227: return;
228: }
229: DEL_FD( fd );
230: fd_rw[fd] = -1;
231: fd_data[fd] = (void*) 0;
232: }
233:
234: /* Do the watch. Return value is the number of descriptors that are ready,
235: ** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means
236: ** wait indefinitely.
237: */
238: int
239: fdwatch( long timeout_msecs )
240: {
241: ++nwatches;
242: nreturned = WATCH( timeout_msecs );
243: next_ridx = 0;
244: return nreturned;
245: }
246:
247:
248: /* Check if a descriptor was ready. */
249: int
250: fdwatch_check_fd( int fd )
251: {
252: if ( fd < 0 || fd >= nfiles || fd_rw[fd] == -1 )
253: {
254: syslog( LOG_ERR, "bad fd (%d) passed to fdwatch_check_fd!", fd );
255: return 0;
256: }
257: return CHECK_FD( fd );
258: }
259:
260:
261: void*
262: fdwatch_get_next_client_data( void )
263: {
264: int fd;
265:
266: if ( next_ridx >= nreturned )
267: return (void*) -1;
268: fd = GET_FD( next_ridx++ );
269: if ( fd < 0 || fd >= nfiles )
270: return (void*) 0;
271: return fd_data[fd];
272: }
273:
274:
275: /* Generate debugging statistics syslog message. */
276: void
277: fdwatch_logstats( long secs )
278: {
279: if ( secs > 0 )
280: syslog(
281: LOG_INFO, " fdwatch - %ld %ss (%g/sec)",
282: nwatches, WHICH, (float) nwatches / secs );
283: nwatches = 0;
284: }
285:
286:
287: #ifdef HAVE_KQUEUE
288:
289: static int maxkqevents;
290: static struct kevent* kqevents;
291: static int nkqevents;
292: static struct kevent* kqrevents;
293: static int* kqrfdidx;
294: static int kq;
295:
296:
297: static int
298: kqueue_init( int nfiles )
299: {
300: kq = kqueue();
301: if ( kq == -1 )
302: return -1;
303: maxkqevents = nfiles * 2;
304: kqevents = (struct kevent*) malloc( sizeof(struct kevent) * maxkqevents );
305: kqrevents = (struct kevent*) malloc( sizeof(struct kevent) * nfiles );
306: kqrfdidx = (int*) malloc( sizeof(int) * nfiles );
307: if ( kqevents == (struct kevent*) 0 || kqrevents == (struct kevent*) 0 ||
308: kqrfdidx == (int*) 0 )
309: return -1;
310: (void) memset( kqevents, 0, sizeof(struct kevent) * maxkqevents );
311: (void) memset( kqrfdidx, 0, sizeof(int) * nfiles );
312: return 0;
313: }
314:
315:
316: static void
317: kqueue_add_fd( int fd, int rw )
318: {
319: if ( nkqevents >= maxkqevents )
320: {
321: syslog( LOG_ERR, "too many kqevents in kqueue_add_fd!" );
322: return;
323: }
324: kqevents[nkqevents].ident = fd;
325: kqevents[nkqevents].flags = EV_ADD;
326: switch ( rw )
327: {
328: case FDW_READ: kqevents[nkqevents].filter = EVFILT_READ; break;
329: case FDW_WRITE: kqevents[nkqevents].filter = EVFILT_WRITE; break;
330: default: break;
331: }
332: ++nkqevents;
333: }
334:
335:
336: static void
337: kqueue_del_fd( int fd )
338: {
339: if ( nkqevents >= maxkqevents )
340: {
341: syslog( LOG_ERR, "too many kqevents in kqueue_del_fd!" );
342: return;
343: }
344: kqevents[nkqevents].ident = fd;
345: kqevents[nkqevents].flags = EV_DELETE;
346: switch ( fd_rw[fd] )
347: {
348: case FDW_READ: kqevents[nkqevents].filter = EVFILT_READ; break;
349: case FDW_WRITE: kqevents[nkqevents].filter = EVFILT_WRITE; break;
350: }
351: ++nkqevents;
352: }
353:
354:
355: static int
356: kqueue_watch( long timeout_msecs )
357: {
358: int i, r;
359:
360: if ( timeout_msecs == INFTIM )
361: r = kevent(
362: kq, kqevents, nkqevents, kqrevents, nfiles, (struct timespec*) 0 );
363: else
364: {
365: struct timespec ts;
366: ts.tv_sec = timeout_msecs / 1000L;
367: ts.tv_nsec = ( timeout_msecs % 1000L ) * 1000000L;
368: r = kevent( kq, kqevents, nkqevents, kqrevents, nfiles, &ts );
369: }
370: nkqevents = 0;
371: if ( r == -1 )
372: return -1;
373:
374: for ( i = 0; i < r; ++i )
375: kqrfdidx[kqrevents[i].ident] = i;
376:
377: return r;
378: }
379:
380:
381: static int
382: kqueue_check_fd( int fd )
383: {
384: int ridx = kqrfdidx[fd];
385:
386: if ( ridx < 0 || ridx >= nfiles )
387: {
388: syslog( LOG_ERR, "bad ridx (%d) in kqueue_check_fd!", ridx );
389: return 0;
390: }
391: if ( ridx >= nreturned )
392: return 0;
393: if ( kqrevents[ridx].ident != fd )
394: return 0;
395: if ( kqrevents[ridx].flags & EV_ERROR )
396: return 0;
397: switch ( fd_rw[fd] )
398: {
399: case FDW_READ: return kqrevents[ridx].filter == EVFILT_READ;
400: case FDW_WRITE: return kqrevents[ridx].filter == EVFILT_WRITE;
401: default: return 0;
402: }
403: }
404:
405:
406: static int
407: kqueue_get_fd( int ridx )
408: {
409: if ( ridx < 0 || ridx >= nfiles )
410: {
411: syslog( LOG_ERR, "bad ridx (%d) in kqueue_get_fd!", ridx );
412: return -1;
413: }
414: return kqrevents[ridx].ident;
415: }
416:
417: #else /* HAVE_KQUEUE */
418:
419:
420: # ifdef HAVE_DEVPOLL
421:
422: static int maxdpevents;
423: static struct pollfd* dpevents;
424: static int ndpevents;
425: static struct pollfd* dprevents;
426: static int* dp_rfdidx;
427: static int dp;
428:
429:
430: static int
431: devpoll_init( int nfiles )
432: {
433: dp = open( "/dev/poll", O_RDWR );
434: if ( dp == -1 )
435: return -1;
436: (void) fcntl( dp, F_SETFD, 1 );
437: maxdpevents = nfiles * 2;
438: dpevents = (struct pollfd*) malloc( sizeof(struct pollfd) * maxdpevents );
439: dprevents = (struct pollfd*) malloc( sizeof(struct pollfd) * nfiles );
440: dp_rfdidx = (int*) malloc( sizeof(int) * nfiles );
441: if ( dpevents == (struct pollfd*) 0 || dprevents == (struct pollfd*) 0 ||
442: dp_rfdidx == (int*) 0 )
443: return -1;
444: (void) memset( dp_rfdidx, 0, sizeof(int) * nfiles );
445: return 0;
446: }
447:
448:
449: static void
450: devpoll_add_fd( int fd, int rw )
451: {
452: if ( ndpevents >= maxdpevents )
453: {
454: syslog( LOG_ERR, "too many fds in devpoll_add_fd!" );
455: return;
456: }
457: dpevents[ndpevents].fd = fd;
458: switch ( rw )
459: {
460: case FDW_READ: dpevents[ndpevents].events = POLLIN; break;
461: case FDW_WRITE: dpevents[ndpevents].events = POLLOUT; break;
462: default: break;
463: }
464: ++ndpevents;
465: }
466:
467:
468: static void
469: devpoll_del_fd( int fd )
470: {
471: if ( ndpevents >= maxdpevents )
472: {
473: syslog( LOG_ERR, "too many fds in devpoll_del_fd!" );
474: return;
475: }
476: dpevents[ndpevents].fd = fd;
477: dpevents[ndpevents].events = POLLREMOVE;
478: ++ndpevents;
479: }
480:
481:
482: static int
483: devpoll_watch( long timeout_msecs )
484: {
485: int i, r;
486: struct dvpoll dvp;
487:
488: r = sizeof(struct pollfd) * ndpevents;
489: if ( r > 0 && write( dp, dpevents, r ) != r )
490: return -1;
491:
492: ndpevents = 0;
493: dvp.dp_fds = dprevents;
494: dvp.dp_nfds = nfiles;
495: dvp.dp_timeout = (int) timeout_msecs;
496:
497: r = ioctl( dp, DP_POLL, &dvp );
498: if ( r == -1 )
499: return -1;
500:
501: for ( i = 0; i < r; ++i )
502: dp_rfdidx[dprevents[i].fd] = i;
503:
504: return r;
505: }
506:
507:
508: static int
509: devpoll_check_fd( int fd )
510: {
511: int ridx = dp_rfdidx[fd];
512:
513: if ( ridx < 0 || ridx >= nfiles )
514: {
515: syslog( LOG_ERR, "bad ridx (%d) in devpoll_check_fd!", ridx );
516: return 0;
517: }
518: if ( ridx >= nreturned )
519: return 0;
520: if ( dprevents[ridx].fd != fd )
521: return 0;
522: if ( dprevents[ridx].revents & POLLERR )
523: return 0;
524: switch ( fd_rw[fd] )
525: {
526: case FDW_READ: return dprevents[ridx].revents & ( POLLIN | POLLHUP | POLLNVAL );
527: case FDW_WRITE: return dprevents[ridx].revents & ( POLLOUT | POLLHUP | POLLNVAL );
528: default: return 0;
529: }
530: }
531:
532:
533: static int
534: devpoll_get_fd( int ridx )
535: {
536: if ( ridx < 0 || ridx >= nfiles )
537: {
538: syslog( LOG_ERR, "bad ridx (%d) in devpoll_get_fd!", ridx );
539: return -1;
540: }
541: return dprevents[ridx].fd;
542: }
543:
544:
545: # else /* HAVE_DEVPOLL */
546:
547:
548: # ifdef HAVE_POLL
549:
550: static struct pollfd* pollfds;
551: static int npoll_fds;
552: static int* poll_fdidx;
553: static int* poll_rfdidx;
554:
555:
556: static int
557: poll_init( int nfiles )
558: {
559: int i;
560:
561: pollfds = (struct pollfd*) malloc( sizeof(struct pollfd) * nfiles );
562: poll_fdidx = (int*) malloc( sizeof(int) * nfiles );
563: poll_rfdidx = (int*) malloc( sizeof(int) * nfiles );
564: if ( pollfds == (struct pollfd*) 0 || poll_fdidx == (int*) 0 ||
565: poll_rfdidx == (int*) 0 )
566: return -1;
567: for ( i = 0; i < nfiles; ++i )
568: pollfds[i].fd = poll_fdidx[i] = -1;
569: return 0;
570: }
571:
572:
573: static void
574: poll_add_fd( int fd, int rw )
575: {
576: if ( npoll_fds >= nfiles )
577: {
578: syslog( LOG_ERR, "too many fds in poll_add_fd!" );
579: return;
580: }
581: pollfds[npoll_fds].fd = fd;
582: switch ( rw )
583: {
584: case FDW_READ: pollfds[npoll_fds].events = POLLIN; break;
585: case FDW_WRITE: pollfds[npoll_fds].events = POLLOUT; break;
586: default: break;
587: }
588: poll_fdidx[fd] = npoll_fds;
589: ++npoll_fds;
590: }
591:
592:
593: static void
594: poll_del_fd( int fd )
595: {
596: int idx = poll_fdidx[fd];
597:
598: if ( idx < 0 || idx >= nfiles )
599: {
600: syslog( LOG_ERR, "bad idx (%d) in poll_del_fd!", idx );
601: return;
602: }
603: --npoll_fds;
604: pollfds[idx] = pollfds[npoll_fds];
605: poll_fdidx[pollfds[idx].fd] = idx;
606: pollfds[npoll_fds].fd = -1;
607: poll_fdidx[fd] = -1;
608: }
609:
610:
611: static int
612: poll_watch( long timeout_msecs )
613: {
614: int r, ridx, i;
615:
616: r = poll( pollfds, npoll_fds, (int) timeout_msecs );
617: if ( r <= 0 )
618: return r;
619:
620: ridx = 0;
621: for ( i = 0; i < npoll_fds; ++i )
622: if ( pollfds[i].revents &
623: ( POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL ) )
624: {
625: poll_rfdidx[ridx++] = pollfds[i].fd;
626: if ( ridx == r )
627: break;
628: }
629:
630: return ridx; /* should be equal to r */
631: }
632:
633:
634: static int
635: poll_check_fd( int fd )
636: {
637: int fdidx = poll_fdidx[fd];
638:
639: if ( fdidx < 0 || fdidx >= nfiles )
640: {
641: syslog( LOG_ERR, "bad fdidx (%d) in poll_check_fd!", fdidx );
642: return 0;
643: }
644: if ( pollfds[fdidx].revents & POLLERR )
645: return 0;
646: switch ( fd_rw[fd] )
647: {
648: case FDW_READ: return pollfds[fdidx].revents & ( POLLIN | POLLHUP | POLLNVAL );
649: case FDW_WRITE: return pollfds[fdidx].revents & ( POLLOUT | POLLHUP | POLLNVAL );
650: default: return 0;
651: }
652: }
653:
654:
655: static int
656: poll_get_fd( int ridx )
657: {
658: if ( ridx < 0 || ridx >= nfiles )
659: {
660: syslog( LOG_ERR, "bad ridx (%d) in poll_get_fd!", ridx );
661: return -1;
662: }
663: return poll_rfdidx[ridx];
664: }
665:
666: # else /* HAVE_POLL */
667:
668:
669: # ifdef HAVE_SELECT
670:
671: static fd_set master_rfdset;
672: static fd_set master_wfdset;
673: static fd_set working_rfdset;
674: static fd_set working_wfdset;
675: static int* select_fds;
676: static int* select_fdidx;
677: static int* select_rfdidx;
678: static int nselect_fds;
679: static int maxfd;
680: static int maxfd_changed;
681:
682:
683: static int
684: select_init( int nfiles )
685: {
686: int i;
687:
688: FD_ZERO( &master_rfdset );
689: FD_ZERO( &master_wfdset );
690: select_fds = (int*) malloc( sizeof(int) * nfiles );
691: select_fdidx = (int*) malloc( sizeof(int) * nfiles );
692: select_rfdidx = (int*) malloc( sizeof(int) * nfiles );
693: if ( select_fds == (int*) 0 || select_fdidx == (int*) 0 ||
694: select_rfdidx == (int*) 0 )
695: return -1;
696: nselect_fds = 0;
697: maxfd = -1;
698: maxfd_changed = 0;
699: for ( i = 0; i < nfiles; ++i )
700: select_fds[i] = select_fdidx[i] = -1;
701: return 0;
702: }
703:
704:
705: static void
706: select_add_fd( int fd, int rw )
707: {
708: if ( nselect_fds >= nfiles )
709: {
710: syslog( LOG_ERR, "too many fds in select_add_fd!" );
711: return;
712: }
713: select_fds[nselect_fds] = fd;
714: switch ( rw )
715: {
716: case FDW_READ: FD_SET( fd, &master_rfdset ); break;
717: case FDW_WRITE: FD_SET( fd, &master_wfdset ); break;
718: default: break;
719: }
720: if ( fd > maxfd )
721: maxfd = fd;
722: select_fdidx[fd] = nselect_fds;
723: ++nselect_fds;
724: }
725:
726:
727: static void
728: select_del_fd( int fd )
729: {
730: int idx = select_fdidx[fd];
731:
732: if ( idx < 0 || idx >= nfiles )
733: {
734: syslog( LOG_ERR, "bad idx (%d) in select_del_fd!", idx );
735: return;
736: }
737:
738: --nselect_fds;
739: select_fds[idx] = select_fds[nselect_fds];
740: select_fdidx[select_fds[idx]] = idx;
741: select_fds[nselect_fds] = -1;
742: select_fdidx[fd] = -1;
743:
744: FD_CLR( fd, &master_rfdset );
745: FD_CLR( fd, &master_wfdset );
746:
747: if ( fd >= maxfd )
748: maxfd_changed = 1;
749: }
750:
751:
752: static int
753: select_get_maxfd( void )
754: {
755: if ( maxfd_changed )
756: {
757: int i;
758: maxfd = -1;
759: for ( i = 0; i < nselect_fds; ++i )
760: if ( select_fds[i] > maxfd )
761: maxfd = select_fds[i];
762: maxfd_changed = 0;
763: }
764: return maxfd;
765: }
766:
767:
768: static int
769: select_watch( long timeout_msecs )
770: {
771: int mfd;
772: int r, idx, ridx;
773:
774: working_rfdset = master_rfdset;
775: working_wfdset = master_wfdset;
776: mfd = select_get_maxfd();
777: if ( timeout_msecs == INFTIM )
778: r = select(
779: mfd + 1, &working_rfdset, &working_wfdset, (fd_set*) 0,
780: (struct timeval*) 0 );
781: else
782: {
783: struct timeval timeout;
784: timeout.tv_sec = timeout_msecs / 1000L;
785: timeout.tv_usec = ( timeout_msecs % 1000L ) * 1000L;
786: r = select(
787: mfd + 1, &working_rfdset, &working_wfdset, (fd_set*) 0, &timeout );
788: }
789: if ( r <= 0 )
790: return r;
791:
792: ridx = 0;
793: for ( idx = 0; idx < nselect_fds; ++idx )
794: if ( select_check_fd( select_fds[idx] ) )
795: {
796: select_rfdidx[ridx++] = select_fds[idx];
797: if ( ridx == r )
798: break;
799: }
800:
801: return ridx; /* should be equal to r */
802: }
803:
804:
805: static int
806: select_check_fd( int fd )
807: {
808: switch ( fd_rw[fd] )
809: {
810: case FDW_READ: return FD_ISSET( fd, &working_rfdset );
811: case FDW_WRITE: return FD_ISSET( fd, &working_wfdset );
812: default: return 0;
813: }
814: }
815:
816:
817: static int
818: select_get_fd( int ridx )
819: {
820: if ( ridx < 0 || ridx >= nfiles )
821: {
822: syslog( LOG_ERR, "bad ridx (%d) in select_get_fd!", ridx );
823: return -1;
824: }
825: return select_rfdidx[ridx];
826: }
827:
828: # endif /* HAVE_SELECT */
829:
830: # endif /* HAVE_POLL */
831:
832: # endif /* HAVE_DEVPOLL */
833:
834: #endif /* HAVE_KQUEUE */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>