]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/select.c
Merge changes from CUPS 1.4svn-r7961.
[thirdparty/cups.git] / scheduler / select.c
1 /*
2 * "$Id: select.c 7720 2008-07-11 22:46:21Z mike $"
3 *
4 * Select abstraction functions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 2006-2007 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * Contents:
16 *
17 * cupsdAddSelect() - Add a file descriptor to the list.
18 * cupsdDoSelect() - Do a select-like operation.
19 * cupsdIsSelecting() - Determine whether we are monitoring a file
20 * descriptor.
21 * cupsdRemoveSelect() - Remove a file descriptor from the list.
22 * cupsdStartSelect() - Initialize the file polling engine.
23 * cupsdStopSelect() - Shutdown the file polling engine.
24 * compare_fds() - Compare file descriptors.
25 * find_fd() - Find an existing file descriptor record.
26 */
27
28 /*
29 * Include necessary headers...
30 */
31
32 #include "cupsd.h"
33
34 #ifdef HAVE_EPOLL
35 # include <sys/epoll.h>
36 # include <sys/poll.h>
37 #elif defined(HAVE_KQUEUE)
38 # include <sys/event.h>
39 # include <sys/time.h>
40 #elif defined(HAVE_POLL)
41 # include <sys/poll.h>
42 #elif defined(__hpux)
43 # include <sys/time.h>
44 #else
45 # include <sys/select.h>
46 #endif /* HAVE_EPOLL */
47
48
49 /*
50 * Design Notes for Poll/Select API in CUPSD
51 * -----------------------------------------
52 *
53 * SUPPORTED APIS
54 *
55 * OS select poll epoll kqueue /dev/poll
56 * -------------- ------ ------ ------ ------ ---------
57 * AIX YES YES NO NO NO
58 * FreeBSD YES YES NO YES NO
59 * HP-UX YES YES NO NO NO
60 * IRIX YES YES NO NO NO
61 * Linux YES YES YES NO NO
62 * MacOS X YES YES NO YES NO
63 * NetBSD YES YES NO YES NO
64 * OpenBSD YES YES NO YES NO
65 * Solaris YES YES NO NO YES
66 * Tru64 YES YES NO NO NO
67 * Windows YES NO NO NO NO
68 *
69 *
70 * HIGH-LEVEL API
71 *
72 * typedef void (*cupsd_selfunc_t)(void *data);
73 *
74 * void cupsdStartSelect(void);
75 * void cupsdStopSelect(void);
76 * void cupsdAddSelect(int fd, cupsd_selfunc_t read_cb,
77 * cupsd_selfunc_t write_cb, void *data);
78 * void cupsdRemoveSelect(int fd);
79 * int cupsdDoSelect(int timeout);
80 *
81 *
82 * IMPLEMENTATION STRATEGY
83 *
84 * 0. Common Stuff
85 * a. CUPS array of file descriptor to callback functions
86 * and data + temporary array of removed fd's.
87 * b. cupsdStartSelect() creates the arrays
88 * c. cupsdStopSelect() destroys the arrays and all elements.
89 * d. cupsdAddSelect() adds to the array and allocates a
90 * new callback element.
91 * e. cupsdRemoveSelect() removes from the active array and
92 * adds to the inactive array.
93 * f. _cupsd_fd_t provides a reference-counted structure for
94 * tracking file descriptors that are monitored.
95 * g. cupsdDoSelect() frees all inactive FDs.
96 *
97 * 1. select() O(n)
98 * a. Input/Output fd_set variables, copied to working
99 * copies and then used with select().
100 * b. Loop through CUPS array, using FD_ISSET and calling
101 * the read/write callbacks as needed.
102 * c. cupsdRemoveSelect() clears fd_set bit from main and
103 * working sets.
104 * d. cupsdStopSelect() frees all of the memory used by the
105 * CUPS array and fd_set's.
106 *
107 * 2. poll() - O(n log n)
108 * a. Regular array of pollfd, sorted the same as the CUPS
109 * array.
110 * b. Loop through pollfd array, call the corresponding
111 * read/write callbacks as needed.
112 * c. cupsdAddSelect() adds first to CUPS array and flags the
113 * pollfd array as invalid.
114 * d. cupsdDoSelect() rebuilds pollfd array as needed, calls
115 * poll(), then loops through the pollfd array looking up
116 * as needed.
117 * e. cupsdRemoveSelect() flags the pollfd array as invalid.
118 * f. cupsdStopSelect() frees all of the memory used by the
119 * CUPS array and pollfd array.
120 *
121 * 3. epoll() - O(n)
122 * a. cupsdStartSelect() creates epoll file descriptor using
123 * epoll_create() with the maximum fd count, and
124 * allocates an events buffer for the maximum fd count.
125 * b. cupsdAdd/RemoveSelect() uses epoll_ctl() to add
126 * (EPOLL_CTL_ADD) or remove (EPOLL_CTL_DEL) a single
127 * event using the level-triggered semantics. The event
128 * user data field is a pointer to the new callback array
129 * element.
130 * c. cupsdDoSelect() uses epoll_wait() with the global event
131 * buffer allocated in cupsdStartSelect() and then loops
132 * through the events, using the user data field to find
133 * the callback record.
134 * d. cupsdStopSelect() closes the epoll file descriptor and
135 * frees all of the memory used by the event buffer.
136 *
137 * 4. kqueue() - O(n)
138 * b. cupsdStartSelect() creates kqueue file descriptor
139 * using kqyeue() function and allocates a global event
140 * buffer.
141 * c. cupsdAdd/RemoveSelect() uses EV_SET and kevent() to
142 * register the changes. The event user data field is a
143 * pointer to the new callback array element.
144 * d. cupsdDoSelect() uses kevent() to poll for events and
145 * loops through the events, using the user data field to
146 * find the callback record.
147 * e. cupsdStopSelect() closes the kqyeye() file descriptor
148 * and frees all of the memory used by the event buffer.
149 *
150 * 5. /dev/poll - O(n log n) - NOT YET IMPLEMENTED
151 * a. cupsdStartSelect() opens /dev/poll and allocates an
152 * array of pollfd structs; on failure to open /dev/poll,
153 * revert to poll() system call.
154 * b. cupsdAddSelect() writes a single pollfd struct to
155 * /dev/poll with the new file descriptor and the
156 * POLLIN/POLLOUT flags.
157 * c. cupsdRemoveSelect() writes a single pollfd struct to
158 * /dev/poll with the file descriptor and the POLLREMOVE
159 * flag.
160 * d. cupsdDoSelect() uses the DP_POLL ioctl to retrieve
161 * events from /dev/poll and then loops through the
162 * returned pollfd array, looking up the file descriptors
163 * as needed.
164 * e. cupsdStopSelect() closes /dev/poll and frees the
165 * pollfd array.
166 *
167 * PERFORMANCE
168 *
169 * In tests using the "make test" target with option 0 (keep cupsd
170 * running) and the "testspeed" program with "-c 50 -r 1000", epoll()
171 * performed 5.5% slower than select(), followed by kqueue() at 16%
172 * slower than select() and poll() at 18% slower than select(). Similar
173 * results were seen with twice the number of client connections.
174 *
175 * The epoll() and kqueue() performance is likely limited by the
176 * number of system calls used to add/modify/remove file
177 * descriptors dynamically. Further optimizations may be possible
178 * in the area of limiting use of cupsdAddSelect() and
179 * cupsdRemoveSelect(), however extreme care will be needed to avoid
180 * excess CPU usage and deadlock conditions.
181 *
182 * We may be able to improve the poll() implementation simply by
183 * keeping the pollfd array sync'd with the _cupsd_fd_t array, as that
184 * will eliminate the rebuilding of the array whenever there is a
185 * change and eliminate the fd array lookups in the inner loop of
186 * cupsdDoSelect().
187 *
188 * Since /dev/poll will never be able to use a shadow array, it may
189 * not make sense to implement support for it. ioctl() overhead will
190 * impact performance as well, so my guess would be that, for CUPS,
191 * /dev/poll will yield a net performance loss.
192 */
193
194 /*
195 * Local structures...
196 */
197
198 typedef struct _cupsd_fd_s
199 {
200 int fd, /* File descriptor */
201 use; /* Use count */
202 cupsd_selfunc_t read_cb, /* Read callback */
203 write_cb; /* Write callback */
204 void *data; /* Data pointer for callbacks */
205 } _cupsd_fd_t;
206
207
208 /*
209 * Local globals...
210 */
211
212 static cups_array_t *cupsd_fds = NULL;
213 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
214 static cups_array_t *cupsd_inactive_fds = NULL;
215 static int cupsd_in_select = 0;
216 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
217
218 #ifdef HAVE_KQUEUE
219 static int cupsd_kqueue_fd = -1,
220 cupsd_kqueue_changes = 0;
221 static struct kevent *cupsd_kqueue_events = NULL;
222 #elif defined(HAVE_POLL)
223 static int cupsd_alloc_pollfds = 0,
224 cupsd_update_pollfds = 0;
225 static struct pollfd *cupsd_pollfds = NULL;
226 # ifdef HAVE_EPOLL
227 static int cupsd_epoll_fd = -1;
228 static struct epoll_event *cupsd_epoll_events = NULL;
229 # endif /* HAVE_EPOLL */
230 #else /* select() */
231 static fd_set cupsd_global_input,
232 cupsd_global_output,
233 cupsd_current_input,
234 cupsd_current_output;
235 #endif /* HAVE_KQUEUE */
236
237
238 /*
239 * Local functions...
240 */
241
242 static int compare_fds(_cupsd_fd_t *a, _cupsd_fd_t *b);
243 static _cupsd_fd_t *find_fd(int fd);
244 #define release_fd(f) { \
245 (f)->use --; \
246 if (!(f)->use) free((f));\
247 }
248 #define retain_fd(f) (f)->use++
249
250
251 /*
252 * 'cupsdAddSelect()' - Add a file descriptor to the list.
253 */
254
255 int /* O - 1 on success, 0 on error */
256 cupsdAddSelect(int fd, /* I - File descriptor */
257 cupsd_selfunc_t read_cb, /* I - Read callback */
258 cupsd_selfunc_t write_cb,/* I - Write callback */
259 void *data) /* I - Data to pass to callback */
260 {
261 _cupsd_fd_t *fdptr; /* File descriptor record */
262 #ifdef HAVE_EPOLL
263 int added; /* 1 if added, 0 if modified */
264 #endif /* HAVE_EPOLL */
265
266
267 /*
268 * Range check input...
269 */
270
271 cupsdLogMessage(CUPSD_LOG_DEBUG2,
272 "cupsdAddSelect: fd=%d, read_cb=%p, write_cb=%p, data=%p",
273 fd, read_cb, write_cb, data);
274
275 if (fd < 0)
276 return (0);
277
278 /*
279 * See if this FD has already been added...
280 */
281
282 if ((fdptr = find_fd(fd)) == NULL)
283 {
284 /*
285 * No, add a new entry...
286 */
287
288 if ((fdptr = calloc(1, sizeof(_cupsd_fd_t))) == NULL)
289 return (0);
290
291 fdptr->fd = fd;
292 fdptr->use = 1;
293
294 if (!cupsArrayAdd(cupsd_fds, fdptr))
295 {
296 cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to add fd %d to array!", fd);
297 free(fdptr);
298 return (0);
299 }
300
301 #ifdef HAVE_EPOLL
302 added = 1;
303 }
304 else
305 added = 0;
306 #else
307 }
308 #endif /* HAVE_EPOLL */
309
310 #ifdef HAVE_KQUEUE
311 {
312 struct kevent event; /* Event data */
313 struct timespec timeout; /* Timeout value */
314
315
316 timeout.tv_sec = 0;
317 timeout.tv_nsec = 0;
318
319 if (fdptr->read_cb != read_cb)
320 {
321 if (read_cb)
322 EV_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, fdptr);
323 else
324 EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
325
326 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
327 {
328 cupsdLogMessage(CUPSD_LOG_DEBUG2,
329 "cupsdAddSelect: kevent() returned %s",
330 strerror(errno));
331 return (0);
332 }
333 }
334
335 if (fdptr->write_cb != write_cb)
336 {
337 if (write_cb)
338 EV_SET(&event, fd, EVFILT_WRITE, EV_ADD, 0, 0, fdptr);
339 else
340 EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
341
342 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
343 {
344 cupsdLogMessage(CUPSD_LOG_DEBUG2,
345 "cupsdAddSelect: kevent() returned %s",
346 strerror(errno));
347 return (0);
348 }
349 }
350 }
351
352 #elif defined(HAVE_POLL)
353 # ifdef HAVE_EPOLL
354 if (cupsd_epoll_fd >= 0)
355 {
356 struct epoll_event event; /* Event data */
357
358
359 event.events = 0;
360
361 if (read_cb)
362 event.events |= EPOLLIN;
363
364 if (write_cb)
365 event.events |= EPOLLOUT;
366
367 event.data.ptr = fdptr;
368
369 if (epoll_ctl(cupsd_epoll_fd, added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd,
370 &event))
371 {
372 close(cupsd_epoll_fd);
373 cupsd_epoll_fd = -1;
374 cupsd_update_pollfds = 1;
375 }
376 }
377 else
378 # endif /* HAVE_EPOLL */
379
380 cupsd_update_pollfds = 1;
381
382 #else /* select() */
383 /*
384 * Add or remove the file descriptor in the input and output sets
385 * for select()...
386 */
387
388 if (read_cb)
389 {
390 cupsdLogMessage(CUPSD_LOG_DEBUG2,
391 "cupsdAddSelect: Adding fd %d to input set...", fd);
392 FD_SET(fd, &cupsd_global_input);
393 }
394 else
395 {
396 cupsdLogMessage(CUPSD_LOG_DEBUG2,
397 "cupsdAddSelect: Removing fd %d from input set...", fd);
398 FD_CLR(fd, &cupsd_global_input);
399 FD_CLR(fd, &cupsd_current_input);
400 }
401
402 if (write_cb)
403 {
404 cupsdLogMessage(CUPSD_LOG_DEBUG2,
405 "cupsdAddSelect: Adding fd %d to output set...", fd);
406 FD_SET(fd, &cupsd_global_output);
407 }
408 else
409 {
410 cupsdLogMessage(CUPSD_LOG_DEBUG2,
411 "cupsdAddSelect: Removing fd %d from output set...", fd);
412 FD_CLR(fd, &cupsd_global_output);
413 FD_CLR(fd, &cupsd_current_output);
414 }
415 #endif /* HAVE_KQUEUE */
416
417 /*
418 * Save the (new) read and write callbacks...
419 */
420
421 fdptr->read_cb = read_cb;
422 fdptr->write_cb = write_cb;
423 fdptr->data = data;
424
425 return (1);
426 }
427
428
429 /*
430 * 'cupsdDoSelect()' - Do a select-like operation.
431 */
432
433 int /* O - Number of files or -1 on error */
434 cupsdDoSelect(long timeout) /* I - Timeout in seconds */
435 {
436 int nfds; /* Number of file descriptors */
437 _cupsd_fd_t *fdptr; /* Current file descriptor */
438 #ifdef HAVE_KQUEUE
439 int i; /* Looping var */
440 struct kevent *event; /* Current event */
441 struct timespec ktimeout; /* kevent() timeout */
442
443
444 cupsdLogMessage(CUPSD_LOG_DEBUG2,
445 "cupsdDoSelect: polling %d fds for %ld seconds...",
446 cupsArrayCount(cupsd_fds), timeout);
447
448 cupsd_in_select = 1;
449
450 if (timeout >= 0 && timeout < 86400)
451 {
452 ktimeout.tv_sec = timeout;
453 ktimeout.tv_nsec = 0;
454
455 nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs,
456 &ktimeout);
457 }
458 else
459 nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs, NULL);
460
461 cupsdLogMessage(CUPSD_LOG_DEBUG2,
462 "cupsdDoSelect: kevent(%d, ..., %d, ...) returned %d...",
463 cupsd_kqueue_fd, MaxFDs, nfds);
464
465 cupsd_kqueue_changes = 0;
466
467 for (i = nfds, event = cupsd_kqueue_events; i > 0; i --, event ++)
468 {
469 fdptr = (_cupsd_fd_t *)event->udata;
470
471 if (cupsArrayFind(cupsd_inactive_fds, fdptr))
472 continue;
473
474 cupsdLogMessage(CUPSD_LOG_DEBUG2, "event->filter=%d, event->ident=%d",
475 event->filter, (int)event->ident);
476
477 retain_fd(fdptr);
478
479 if (fdptr->read_cb && event->filter == EVFILT_READ)
480 {
481 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
482 fdptr->fd);
483 (*(fdptr->read_cb))(fdptr->data);
484 }
485
486 if (fdptr->write_cb && event->filter == EVFILT_WRITE)
487 {
488 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
489 fdptr->fd);
490 (*(fdptr->write_cb))(fdptr->data);
491 }
492
493 release_fd(fdptr);
494 }
495
496 #elif defined(HAVE_POLL)
497 struct pollfd *pfd; /* Current pollfd structure */
498 int count; /* Number of file descriptors */
499
500
501 cupsdLogMessage(CUPSD_LOG_DEBUG2,
502 "cupsdDoSelect: polling %d fds for %ld seconds...",
503 cupsArrayCount(cupsd_fds), timeout);
504
505 # ifdef HAVE_EPOLL
506 cupsd_in_select = 1;
507
508 if (cupsd_epoll_fd >= 0)
509 {
510 int i; /* Looping var */
511 struct epoll_event *event; /* Current event */
512
513
514 if (timeout >= 0 && timeout < 86400)
515 nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs,
516 timeout * 1000);
517 else
518 nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs, -1);
519
520 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: epoll() returned %d...",
521 nfds);
522
523 if (nfds < 0 && errno != EINTR)
524 {
525 close(cupsd_epoll_fd);
526 cupsd_epoll_fd = -1;
527 }
528 else
529 {
530 for (i = nfds, event = cupsd_epoll_events; i > 0; i --, event ++)
531 {
532 fdptr = (_cupsd_fd_t *)event->data.ptr;
533
534 if (cupsArrayFind(cupsd_inactive_fds, fdptr))
535 continue;
536
537 retain_fd(fdptr);
538
539 if (fdptr->read_cb && (event->events & (EPOLLIN | EPOLLERR | EPOLLHUP)))
540 {
541 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
542 fdptr->fd);
543 (*(fdptr->read_cb))(fdptr->data);
544 }
545
546 if (fdptr->write_cb && (event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)))
547 {
548 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
549 fdptr->fd);
550 (*(fdptr->write_cb))(fdptr->data);
551 }
552
553 release_fd(fdptr);
554 }
555
556 goto release_inactive;
557 }
558 }
559 # endif /* HAVE_EPOLL */
560
561 count = cupsArrayCount(cupsd_fds);
562
563 if (cupsd_update_pollfds)
564 {
565 /*
566 * Update the cupsd_pollfds array to match the current FD array...
567 */
568
569 cupsd_update_pollfds = 0;
570
571 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Updating pollfd array...");
572
573 /*
574 * (Re)allocate memory as needed...
575 */
576
577 if (count > cupsd_alloc_pollfds)
578 {
579 int allocfds = count + 16;
580
581
582 if (cupsd_pollfds)
583 pfd = realloc(cupsd_pollfds, allocfds * sizeof(struct pollfd));
584 else
585 pfd = malloc(allocfds * sizeof(struct pollfd));
586
587 if (!pfd)
588 {
589 cupsdLogMessage(CUPSD_LOG_EMERG,
590 "Unable to allocate %d bytes for polling!",
591 (int)(allocfds * sizeof(struct pollfd)));
592
593 return (-1);
594 }
595
596 cupsd_pollfds = pfd;
597 cupsd_alloc_pollfds = allocfds;
598 }
599
600 /*
601 * Rebuild the array...
602 */
603
604 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds), pfd = cupsd_pollfds;
605 fdptr;
606 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds), pfd ++)
607 {
608 pfd->fd = fdptr->fd;
609 pfd->events = 0;
610
611 if (fdptr->read_cb)
612 pfd->events |= POLLIN;
613
614 if (fdptr->write_cb)
615 pfd->events |= POLLOUT;
616 }
617 }
618
619 cupsdLogMessage(CUPSD_LOG_DEBUG2,
620 "cupsdDoSelect: polling %d fds for %ld seconds...",
621 count, timeout);
622
623 if (timeout >= 0 && timeout < 86400)
624 nfds = poll(cupsd_pollfds, count, timeout * 1000);
625 else
626 nfds = poll(cupsd_pollfds, count, -1);
627
628 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: poll() returned %d...",
629 nfds);
630
631 if (nfds > 0)
632 {
633 /*
634 * Do callbacks for each file descriptor...
635 */
636
637 for (pfd = cupsd_pollfds; count > 0; pfd ++, count --)
638 {
639 cupsdLogMessage(CUPSD_LOG_DEBUG2,
640 "cupsdDoSelect: pollfds[%d]={fd=%d, revents=%x}",
641 pfd - cupsd_pollfds, pfd->fd, pfd->revents);
642
643 if (!pfd->revents)
644 continue;
645
646 if ((fdptr = find_fd(pfd->fd)) == NULL)
647 continue;
648
649 retain_fd(fdptr);
650
651 if (fdptr->read_cb && (pfd->revents & (POLLIN | POLLERR | POLLHUP)))
652 {
653 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
654 fdptr->fd);
655 (*(fdptr->read_cb))(fdptr->data);
656 }
657
658 if (fdptr->write_cb && (pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
659 {
660 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
661 fdptr->fd);
662 (*(fdptr->write_cb))(fdptr->data);
663 }
664
665 release_fd(fdptr);
666 }
667 }
668
669 #else /* select() */
670 struct timeval stimeout; /* Timeout for select() */
671 int maxfd; /* Maximum file descriptor */
672
673
674 /*
675 * Figure out the highest file descriptor number...
676 */
677
678 if ((fdptr = (_cupsd_fd_t *)cupsArrayLast(cupsd_fds)) == NULL)
679 maxfd = 1;
680 else
681 maxfd = fdptr->fd + 1;
682
683 /*
684 * Do the select()...
685 */
686
687 cupsd_current_input = cupsd_global_input;
688 cupsd_current_output = cupsd_global_output;
689
690 cupsdLogMessage(CUPSD_LOG_DEBUG2,
691 "cupsdDoSelect: selecting %d fds for %ld seconds...",
692 maxfd, timeout);
693
694 if (timeout >= 0 && timeout < 86400)
695 {
696 stimeout.tv_sec = timeout;
697 stimeout.tv_usec = 0;
698
699 nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
700 &stimeout);
701 }
702 else
703 nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
704 NULL);
705
706 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: select() returned %d...",
707 nfds);
708
709 if (nfds > 0)
710 {
711 /*
712 * Do callbacks for each file descriptor...
713 */
714
715 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
716 fdptr;
717 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
718 {
719 retain_fd(fdptr);
720
721 if (fdptr->read_cb && FD_ISSET(fdptr->fd, &cupsd_current_input))
722 {
723 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
724 fdptr->fd);
725 (*(fdptr->read_cb))(fdptr->data);
726 }
727
728 if (fdptr->write_cb && FD_ISSET(fdptr->fd, &cupsd_current_output))
729 {
730 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
731 fdptr->fd);
732 (*(fdptr->write_cb))(fdptr->data);
733 }
734
735 release_fd(fdptr);
736 }
737 }
738
739 #endif /* HAVE_KQUEUE */
740
741 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
742 /*
743 * Release all inactive file descriptors...
744 */
745
746 # ifndef HAVE_KQUEUE
747 release_inactive:
748 # endif /* !HAVE_KQUEUE */
749
750 cupsd_in_select = 0;
751
752 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_inactive_fds);
753 fdptr;
754 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_inactive_fds))
755 {
756 cupsArrayRemove(cupsd_inactive_fds, fdptr);
757 release_fd(fdptr);
758 }
759 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
760
761 /*
762 * Return the number of file descriptors handled...
763 */
764
765 return (nfds);
766 }
767
768
769 #ifdef CUPSD_IS_SELECTING
770 /*
771 * 'cupsdIsSelecting()' - Determine whether we are monitoring a file
772 * descriptor.
773 */
774
775 int /* O - 1 if selecting, 0 otherwise */
776 cupsdIsSelecting(int fd) /* I - File descriptor */
777 {
778 return (find_fd(fd) != NULL);
779 }
780 #endif /* CUPSD_IS_SELECTING */
781
782
783 /*
784 * 'cupsdRemoveSelect()' - Remove a file descriptor from the list.
785 */
786
787 void
788 cupsdRemoveSelect(int fd) /* I - File descriptor */
789 {
790 _cupsd_fd_t *fdptr; /* File descriptor record */
791 #ifdef HAVE_EPOLL
792 struct epoll_event event; /* Event data */
793 #elif defined(HAVE_KQUEUE)
794 struct kevent event; /* Event data */
795 struct timespec timeout; /* Timeout value */
796 #elif defined(HAVE_POLL)
797 /* No variables for poll() */
798 #endif /* HAVE_EPOLL */
799
800
801 /*
802 * Range check input...
803 */
804
805 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRemoveSelect: fd=%d", fd);
806
807 if (fd < 0)
808 return;
809
810 /*
811 * Find the file descriptor...
812 */
813
814 if ((fdptr = find_fd(fd)) == NULL)
815 return;
816
817 #ifdef HAVE_EPOLL
818 if (epoll_ctl(cupsd_epoll_fd, EPOLL_CTL_DEL, fd, &event))
819 {
820 close(cupsd_epoll_fd);
821 cupsd_epoll_fd = -1;
822 cupsd_update_pollfds = 1;
823 }
824
825 #elif defined(HAVE_KQUEUE)
826 timeout.tv_sec = 0;
827 timeout.tv_nsec = 0;
828
829 if (fdptr->read_cb)
830 {
831 EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
832
833 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
834 {
835 cupsdLogMessage(CUPSD_LOG_DEBUG2,
836 "cupsdRemoveSelect: kevent() returned %s",
837 strerror(errno));
838 return;
839 }
840 }
841
842 if (fdptr->write_cb)
843 {
844 EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
845
846 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
847 {
848 cupsdLogMessage(CUPSD_LOG_DEBUG2,
849 "cupsdRemoveSelect: kevent() returned %s",
850 strerror(errno));
851 return;
852 }
853 }
854
855
856 #elif defined(HAVE_POLL)
857 /*
858 * Update the pollfds array...
859 */
860
861 cupsd_update_pollfds = 1;
862
863 #else /* select() */
864 cupsdLogMessage(CUPSD_LOG_DEBUG2,
865 "cupsdRemoveSelect: Removing fd %d from input and output "
866 "sets...", fd);
867 FD_CLR(fd, &cupsd_global_input);
868 FD_CLR(fd, &cupsd_global_output);
869 FD_CLR(fd, &cupsd_current_input);
870 FD_CLR(fd, &cupsd_current_output);
871 #endif /* HAVE_EPOLL */
872
873 /*
874 * Remove the file descriptor from the active array and add to the
875 * inactive array (or release, if we don't need the inactive array...)
876 */
877
878 cupsArrayRemove(cupsd_fds, fdptr);
879
880 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
881 if (cupsd_in_select)
882 cupsArrayAdd(cupsd_inactive_fds, fdptr);
883 else
884 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
885
886 release_fd(fdptr);
887 }
888
889
890 /*
891 * 'cupsdStartSelect()' - Initialize the file polling engine.
892 */
893
894 void
895 cupsdStartSelect(void)
896 {
897 cupsd_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
898
899 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
900 cupsd_inactive_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
901 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
902
903 #ifdef HAVE_EPOLL
904 cupsd_epoll_fd = epoll_create(MaxFDs);
905 cupsd_epoll_events = calloc(MaxFDs, sizeof(struct epoll_event));
906 cupsd_update_pollfds = 0;
907
908 #elif defined(HAVE_KQUEUE)
909 cupsd_kqueue_fd = kqueue();
910 cupsd_kqueue_changes = 0;
911 cupsd_kqueue_events = calloc(MaxFDs, sizeof(struct kevent));
912
913 #elif defined(HAVE_POLL)
914 cupsd_update_pollfds = 0;
915
916 #else /* select() */
917 FD_ZERO(&cupsd_global_input);
918 FD_ZERO(&cupsd_global_output);
919 #endif /* HAVE_EPOLL */
920 }
921
922
923 /*
924 * 'cupsdStopSelect()' - Shutdown the file polling engine.
925 */
926
927 void
928 cupsdStopSelect(void)
929 {
930 _cupsd_fd_t *fdptr; /* Current file descriptor */
931
932
933 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
934 fdptr;
935 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
936 free(fdptr);
937
938 cupsArrayDelete(cupsd_fds);
939 cupsd_fds = NULL;
940
941 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
942 cupsArrayDelete(cupsd_inactive_fds);
943 cupsd_inactive_fds = NULL;
944 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
945
946 #ifdef HAVE_KQUEUE
947 if (cupsd_kqueue_events)
948 {
949 free(cupsd_kqueue_events);
950 cupsd_kqueue_events = NULL;
951 }
952
953 if (cupsd_kqueue_fd >= 0)
954 {
955 close(cupsd_kqueue_fd);
956 cupsd_kqueue_fd = -1;
957 }
958
959 cupsd_kqueue_changes = 0;
960
961 #elif defined(HAVE_POLL)
962 # ifdef HAVE_EPOLL
963 if (cupsd_epoll_events)
964 {
965 free(cupsd_epoll_events);
966 cupsd_epoll_events = NULL;
967 }
968
969 if (cupsd_epoll_fd >= 0)
970 {
971 close(cupsd_epoll_fd);
972 cupsd_epoll_fd = -1;
973 }
974 # endif /* HAVE_EPOLL */
975
976 if (cupsd_pollfds)
977 {
978 free(cupsd_pollfds);
979 cupsd_pollfds = NULL;
980 cupsd_alloc_pollfds = 0;
981 }
982
983 cupsd_update_pollfds = 0;
984
985 #else /* select() */
986 FD_ZERO(&cupsd_global_input);
987 FD_ZERO(&cupsd_global_output);
988 #endif /* HAVE_EPOLL */
989 }
990
991
992 /*
993 * 'compare_fds()' - Compare file descriptors.
994 */
995
996 static int /* O - Result of comparison */
997 compare_fds(_cupsd_fd_t *a, /* I - First file descriptor */
998 _cupsd_fd_t *b) /* I - Second file descriptor */
999 {
1000 return (a->fd - b->fd);
1001 }
1002
1003
1004 /*
1005 * 'find_fd()' - Find an existing file descriptor record.
1006 */
1007
1008 static _cupsd_fd_t * /* O - FD record pointer or NULL */
1009 find_fd(int fd) /* I - File descriptor */
1010 {
1011 _cupsd_fd_t *fdptr, /* Matching record (if any) */
1012 key; /* Search key */
1013
1014
1015 cupsArraySave(cupsd_fds);
1016
1017 key.fd = fd;
1018 fdptr = (_cupsd_fd_t *)cupsArrayFind(cupsd_fds, &key);
1019
1020 cupsArrayRestore(cupsd_fds);
1021
1022 return (fdptr);
1023 }
1024
1025
1026 /*
1027 * End of "$Id: select.c 7720 2008-07-11 22:46:21Z mike $".
1028 */