]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/select.c
Merge changes from CUPS 1.4svn-r7696.
[thirdparty/cups.git] / scheduler / select.c
1 /*
2 * "$Id: select.c 7093 2007-11-30 19:09:36Z mike $"
3 *
4 * Select abstraction functions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007 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 int added; /* 1 if added, 0 if modified */
263
264
265 /*
266 * Range check input...
267 */
268
269 cupsdLogMessage(CUPSD_LOG_DEBUG2,
270 "cupsdAddSelect: fd=%d, read_cb=%p, write_cb=%p, data=%p",
271 fd, read_cb, write_cb, data);
272
273 if (fd < 0)
274 return (0);
275
276 /*
277 * See if this FD has already been added...
278 */
279
280 if ((fdptr = find_fd(fd)) == NULL)
281 {
282 /*
283 * No, add a new entry...
284 */
285
286 if ((fdptr = calloc(1, sizeof(_cupsd_fd_t))) == NULL)
287 return (0);
288
289 fdptr->fd = fd;
290 fdptr->use = 1;
291
292 if (!cupsArrayAdd(cupsd_fds, fdptr))
293 {
294 cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to add fd %d to array!", fd);
295 free(fdptr);
296 return (0);
297 }
298
299 added = 1;
300 }
301 else
302 added = 0;
303
304 #ifdef HAVE_KQUEUE
305 {
306 struct kevent event; /* Event data */
307 struct timespec timeout; /* Timeout value */
308
309
310 timeout.tv_sec = 0;
311 timeout.tv_nsec = 0;
312
313 if (fdptr->read_cb != read_cb)
314 {
315 if (read_cb)
316 EV_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, fdptr);
317 else
318 EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
319
320 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
321 {
322 cupsdLogMessage(CUPSD_LOG_DEBUG2,
323 "cupsdAddSelect: kevent() returned %s",
324 strerror(errno));
325 return (0);
326 }
327 }
328
329 if (fdptr->write_cb != write_cb)
330 {
331 if (write_cb)
332 EV_SET(&event, fd, EVFILT_WRITE, EV_ADD, 0, 0, fdptr);
333 else
334 EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
335
336 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
337 {
338 cupsdLogMessage(CUPSD_LOG_DEBUG2,
339 "cupsdAddSelect: kevent() returned %s",
340 strerror(errno));
341 return (0);
342 }
343 }
344 }
345
346 #elif defined(HAVE_POLL)
347 # ifdef HAVE_EPOLL
348 if (cupsd_epoll_fd >= 0)
349 {
350 struct epoll_event event; /* Event data */
351
352
353 event.events = 0;
354
355 if (read_cb)
356 event.events |= EPOLLIN;
357
358 if (write_cb)
359 event.events |= EPOLLOUT;
360
361 event.data.ptr = fdptr;
362
363 if (epoll_ctl(cupsd_epoll_fd, added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd,
364 &event))
365 {
366 close(cupsd_epoll_fd);
367 cupsd_epoll_fd = -1;
368 cupsd_update_pollfds = 1;
369 }
370 }
371 else
372 # endif /* HAVE_EPOLL */
373
374 cupsd_update_pollfds = 1;
375
376 #else /* select() */
377 /*
378 * Add or remove the file descriptor in the input and output sets
379 * for select()...
380 */
381
382 if (read_cb)
383 {
384 cupsdLogMessage(CUPSD_LOG_DEBUG2,
385 "cupsdAddSelect: Adding fd %d to input set...", fd);
386 FD_SET(fd, &cupsd_global_input);
387 }
388 else
389 {
390 cupsdLogMessage(CUPSD_LOG_DEBUG2,
391 "cupsdAddSelect: Removing fd %d from input set...", fd);
392 FD_CLR(fd, &cupsd_global_input);
393 FD_CLR(fd, &cupsd_current_input);
394 }
395
396 if (write_cb)
397 {
398 cupsdLogMessage(CUPSD_LOG_DEBUG2,
399 "cupsdAddSelect: Adding fd %d to output set...", fd);
400 FD_SET(fd, &cupsd_global_output);
401 }
402 else
403 {
404 cupsdLogMessage(CUPSD_LOG_DEBUG2,
405 "cupsdAddSelect: Removing fd %d from output set...", fd);
406 FD_CLR(fd, &cupsd_global_output);
407 FD_CLR(fd, &cupsd_current_output);
408 }
409 #endif /* HAVE_KQUEUE */
410
411 /*
412 * Save the (new) read and write callbacks...
413 */
414
415 fdptr->read_cb = read_cb;
416 fdptr->write_cb = write_cb;
417 fdptr->data = data;
418
419 return (1);
420 }
421
422
423 /*
424 * 'cupsdDoSelect()' - Do a select-like operation.
425 */
426
427 int /* O - Number of files or -1 on error */
428 cupsdDoSelect(long timeout) /* I - Timeout in seconds */
429 {
430 int nfds; /* Number of file descriptors */
431 _cupsd_fd_t *fdptr; /* Current file descriptor */
432 #ifdef HAVE_KQUEUE
433 int i; /* Looping var */
434 struct kevent *event; /* Current event */
435 struct timespec ktimeout; /* kevent() timeout */
436
437
438 cupsdLogMessage(CUPSD_LOG_DEBUG2,
439 "cupsdDoSelect: polling %d fds for %ld seconds...",
440 cupsArrayCount(cupsd_fds), timeout);
441
442 cupsd_in_select = 1;
443
444 if (timeout >= 0 && timeout < 86400)
445 {
446 ktimeout.tv_sec = timeout;
447 ktimeout.tv_nsec = 0;
448
449 nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs,
450 &ktimeout);
451 }
452 else
453 nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs, NULL);
454
455 cupsdLogMessage(CUPSD_LOG_DEBUG2,
456 "cupsdDoSelect: kevent(%d, ..., %d, ...) returned %d...",
457 cupsd_kqueue_fd, MaxFDs, nfds);
458
459 cupsd_kqueue_changes = 0;
460
461 for (i = nfds, event = cupsd_kqueue_events; i > 0; i --, event ++)
462 {
463 fdptr = (_cupsd_fd_t *)event->udata;
464
465 if (cupsArrayFind(cupsd_inactive_fds, fdptr))
466 continue;
467
468 cupsdLogMessage(CUPSD_LOG_DEBUG2, "event->filter=%d, event->ident=%d",
469 event->filter, (int)event->ident);
470
471 retain_fd(fdptr);
472
473 if (fdptr->read_cb && event->filter == EVFILT_READ)
474 {
475 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
476 fdptr->fd);
477 (*(fdptr->read_cb))(fdptr->data);
478 }
479
480 if (fdptr->write_cb && event->filter == EVFILT_WRITE)
481 {
482 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
483 fdptr->fd);
484 (*(fdptr->write_cb))(fdptr->data);
485 }
486
487 release_fd(fdptr);
488 }
489
490 #elif defined(HAVE_POLL)
491 struct pollfd *pfd; /* Current pollfd structure */
492 int count; /* Number of file descriptors */
493
494
495 cupsdLogMessage(CUPSD_LOG_DEBUG2,
496 "cupsdDoSelect: polling %d fds for %ld seconds...",
497 cupsArrayCount(cupsd_fds), timeout);
498
499 # ifdef HAVE_EPOLL
500 cupsd_in_select = 1;
501
502 if (cupsd_epoll_fd >= 0)
503 {
504 int i; /* Looping var */
505 struct epoll_event *event; /* Current event */
506
507
508 if (timeout >= 0 && timeout < 86400)
509 nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs,
510 timeout * 1000);
511 else
512 nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs, -1);
513
514 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: epoll() returned %d...",
515 nfds);
516
517 if (nfds < 0 && errno != EINTR)
518 {
519 close(cupsd_epoll_fd);
520 cupsd_epoll_fd = -1;
521 }
522 else
523 {
524 for (i = nfds, event = cupsd_epoll_events; i > 0; i --, event ++)
525 {
526 fdptr = (_cupsd_fd_t *)event->data.ptr;
527
528 if (cupsArrayFind(cupsd_inactive_fds, fdptr))
529 continue;
530
531 retain_fd(fdptr);
532
533 if (fdptr->read_cb && (event->events & (EPOLLIN | EPOLLERR | EPOLLHUP)))
534 {
535 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
536 fdptr->fd);
537 (*(fdptr->read_cb))(fdptr->data);
538 }
539
540 if (fdptr->write_cb && (event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)))
541 {
542 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
543 fdptr->fd);
544 (*(fdptr->write_cb))(fdptr->data);
545 }
546
547 release_fd(fdptr);
548 }
549
550 goto release_inactive;
551 }
552 }
553 # endif /* HAVE_EPOLL */
554
555 count = cupsArrayCount(cupsd_fds);
556
557 if (cupsd_update_pollfds)
558 {
559 /*
560 * Update the cupsd_pollfds array to match the current FD array...
561 */
562
563 cupsd_update_pollfds = 0;
564
565 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Updating pollfd array...");
566
567 /*
568 * (Re)allocate memory as needed...
569 */
570
571 if (count > cupsd_alloc_pollfds)
572 {
573 int allocfds = count + 16;
574
575
576 if (cupsd_pollfds)
577 pfd = realloc(cupsd_pollfds, allocfds * sizeof(struct pollfd));
578 else
579 pfd = malloc(allocfds * sizeof(struct pollfd));
580
581 if (!pfd)
582 {
583 cupsdLogMessage(CUPSD_LOG_EMERG,
584 "Unable to allocate %d bytes for polling!",
585 (int)(allocfds * sizeof(struct pollfd)));
586
587 return (-1);
588 }
589
590 cupsd_pollfds = pfd;
591 cupsd_alloc_pollfds = allocfds;
592 }
593
594 /*
595 * Rebuild the array...
596 */
597
598 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds), pfd = cupsd_pollfds;
599 fdptr;
600 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds), pfd ++)
601 {
602 pfd->fd = fdptr->fd;
603 pfd->events = 0;
604
605 if (fdptr->read_cb)
606 pfd->events |= POLLIN;
607
608 if (fdptr->write_cb)
609 pfd->events |= POLLOUT;
610 }
611 }
612
613 cupsdLogMessage(CUPSD_LOG_DEBUG2,
614 "cupsdDoSelect: polling %d fds for %ld seconds...",
615 count, timeout);
616
617 if (timeout >= 0 && timeout < 86400)
618 nfds = poll(cupsd_pollfds, count, timeout * 1000);
619 else
620 nfds = poll(cupsd_pollfds, count, -1);
621
622 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: poll() returned %d...",
623 nfds);
624
625 if (nfds > 0)
626 {
627 /*
628 * Do callbacks for each file descriptor...
629 */
630
631 for (pfd = cupsd_pollfds; count > 0; pfd ++, count --)
632 {
633 cupsdLogMessage(CUPSD_LOG_DEBUG2,
634 "cupsdDoSelect: pollfds[%d]={fd=%d, revents=%x}",
635 pfd - cupsd_pollfds, pfd->fd, pfd->revents);
636
637 if (!pfd->revents)
638 continue;
639
640 if ((fdptr = find_fd(pfd->fd)) == NULL)
641 continue;
642
643 retain_fd(fdptr);
644
645 if (fdptr->read_cb && (pfd->revents & (POLLIN | POLLERR | POLLHUP)))
646 {
647 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
648 fdptr->fd);
649 (*(fdptr->read_cb))(fdptr->data);
650 }
651
652 if (fdptr->write_cb && (pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
653 {
654 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
655 fdptr->fd);
656 (*(fdptr->write_cb))(fdptr->data);
657 }
658
659 release_fd(fdptr);
660 }
661 }
662
663 #else /* select() */
664 struct timeval stimeout; /* Timeout for select() */
665 int maxfd; /* Maximum file descriptor */
666
667
668 /*
669 * Figure out the highest file descriptor number...
670 */
671
672 if ((fdptr = (_cupsd_fd_t *)cupsArrayLast(cupsd_fds)) == NULL)
673 maxfd = 1;
674 else
675 maxfd = fdptr->fd + 1;
676
677 /*
678 * Do the select()...
679 */
680
681 cupsd_current_input = cupsd_global_input;
682 cupsd_current_output = cupsd_global_output;
683
684 cupsdLogMessage(CUPSD_LOG_DEBUG2,
685 "cupsdDoSelect: selecting %d fds for %ld seconds...",
686 maxfd, timeout);
687
688 if (timeout >= 0 && timeout < 86400)
689 {
690 stimeout.tv_sec = timeout;
691 stimeout.tv_usec = 0;
692
693 nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
694 &stimeout);
695 }
696 else
697 nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
698 NULL);
699
700 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: select() returned %d...",
701 nfds);
702
703 if (nfds > 0)
704 {
705 /*
706 * Do callbacks for each file descriptor...
707 */
708
709 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
710 fdptr;
711 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
712 {
713 retain_fd(fdptr);
714
715 if (fdptr->read_cb && FD_ISSET(fdptr->fd, &cupsd_current_input))
716 {
717 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
718 fdptr->fd);
719 (*(fdptr->read_cb))(fdptr->data);
720 }
721
722 if (fdptr->write_cb && FD_ISSET(fdptr->fd, &cupsd_current_output))
723 {
724 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
725 fdptr->fd);
726 (*(fdptr->write_cb))(fdptr->data);
727 }
728
729 release_fd(fdptr);
730 }
731 }
732
733 #endif /* HAVE_KQUEUE */
734
735 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
736 /*
737 * Release all inactive file descriptors...
738 */
739
740 # ifndef HAVE_KQUEUE
741 release_inactive:
742 # endif /* !HAVE_KQUEUE */
743
744 cupsd_in_select = 0;
745
746 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_inactive_fds);
747 fdptr;
748 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_inactive_fds))
749 {
750 cupsArrayRemove(cupsd_inactive_fds, fdptr);
751 release_fd(fdptr);
752 }
753 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
754
755 /*
756 * Return the number of file descriptors handled...
757 */
758
759 return (nfds);
760 }
761
762
763 #ifdef CUPSD_IS_SELECTING
764 /*
765 * 'cupsdIsSelecting()' - Determine whether we are monitoring a file
766 * descriptor.
767 */
768
769 int /* O - 1 if selecting, 0 otherwise */
770 cupsdIsSelecting(int fd) /* I - File descriptor */
771 {
772 return (find_fd(fd) != NULL);
773 }
774 #endif /* CUPSD_IS_SELECTING */
775
776
777 /*
778 * 'cupsdRemoveSelect()' - Remove a file descriptor from the list.
779 */
780
781 void
782 cupsdRemoveSelect(int fd) /* I - File descriptor */
783 {
784 _cupsd_fd_t *fdptr; /* File descriptor record */
785 #ifdef HAVE_EPOLL
786 struct epoll_event event; /* Event data */
787 #elif defined(HAVE_KQUEUE)
788 struct kevent event; /* Event data */
789 struct timespec timeout; /* Timeout value */
790 #elif defined(HAVE_POLL)
791 /* No variables for poll() */
792 #endif /* HAVE_EPOLL */
793
794
795 /*
796 * Range check input...
797 */
798
799 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRemoveSelect: fd=%d", fd);
800
801 if (fd < 0)
802 return;
803
804 /*
805 * Find the file descriptor...
806 */
807
808 if ((fdptr = find_fd(fd)) == NULL)
809 return;
810
811 #ifdef HAVE_EPOLL
812 if (epoll_ctl(cupsd_epoll_fd, EPOLL_CTL_DEL, fd, &event))
813 {
814 close(cupsd_epoll_fd);
815 cupsd_epoll_fd = -1;
816 cupsd_update_pollfds = 1;
817 }
818
819 #elif defined(HAVE_KQUEUE)
820 timeout.tv_sec = 0;
821 timeout.tv_nsec = 0;
822
823 if (fdptr->read_cb)
824 {
825 EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
826
827 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
828 {
829 cupsdLogMessage(CUPSD_LOG_DEBUG2,
830 "cupsdRemoveSelect: kevent() returned %s",
831 strerror(errno));
832 return;
833 }
834 }
835
836 if (fdptr->write_cb)
837 {
838 EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
839
840 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
841 {
842 cupsdLogMessage(CUPSD_LOG_DEBUG2,
843 "cupsdRemoveSelect: kevent() returned %s",
844 strerror(errno));
845 return;
846 }
847 }
848
849
850 #elif defined(HAVE_POLL)
851 /*
852 * Update the pollfds array...
853 */
854
855 cupsd_update_pollfds = 1;
856
857 #else /* select() */
858 cupsdLogMessage(CUPSD_LOG_DEBUG2,
859 "cupsdRemoveSelect: Removing fd %d from input and output "
860 "sets...", fd);
861 FD_CLR(fd, &cupsd_global_input);
862 FD_CLR(fd, &cupsd_global_output);
863 FD_CLR(fd, &cupsd_current_input);
864 FD_CLR(fd, &cupsd_current_output);
865 #endif /* HAVE_EPOLL */
866
867 /*
868 * Remove the file descriptor from the active array and add to the
869 * inactive array (or release, if we don't need the inactive array...)
870 */
871
872 cupsArrayRemove(cupsd_fds, fdptr);
873
874 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
875 if (cupsd_in_select)
876 cupsArrayAdd(cupsd_inactive_fds, fdptr);
877 else
878 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
879
880 release_fd(fdptr);
881 }
882
883
884 /*
885 * 'cupsdStartSelect()' - Initialize the file polling engine.
886 */
887
888 void
889 cupsdStartSelect(void)
890 {
891 cupsd_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
892
893 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
894 cupsd_inactive_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
895 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
896
897 #ifdef HAVE_EPOLL
898 cupsd_epoll_fd = epoll_create(MaxFDs);
899 cupsd_epoll_events = calloc(MaxFDs, sizeof(struct epoll_event));
900 cupsd_update_pollfds = 0;
901
902 #elif defined(HAVE_KQUEUE)
903 cupsd_kqueue_fd = kqueue();
904 cupsd_kqueue_changes = 0;
905 cupsd_kqueue_events = calloc(MaxFDs, sizeof(struct kevent));
906
907 #elif defined(HAVE_POLL)
908 cupsd_update_pollfds = 0;
909
910 #else /* select() */
911 FD_ZERO(&cupsd_global_input);
912 FD_ZERO(&cupsd_global_output);
913 #endif /* HAVE_EPOLL */
914 }
915
916
917 /*
918 * 'cupsdStopSelect()' - Shutdown the file polling engine.
919 */
920
921 void
922 cupsdStopSelect(void)
923 {
924 _cupsd_fd_t *fdptr; /* Current file descriptor */
925
926
927 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
928 fdptr;
929 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
930 free(fdptr);
931
932 cupsArrayDelete(cupsd_fds);
933 cupsd_fds = NULL;
934
935 #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
936 cupsArrayDelete(cupsd_inactive_fds);
937 cupsd_inactive_fds = NULL;
938 #endif /* HAVE_EPOLL || HAVE_KQUEUE */
939
940 #ifdef HAVE_KQUEUE
941 if (cupsd_kqueue_events)
942 {
943 free(cupsd_kqueue_events);
944 cupsd_kqueue_events = NULL;
945 }
946
947 if (cupsd_kqueue_fd >= 0)
948 {
949 close(cupsd_kqueue_fd);
950 cupsd_kqueue_fd = -1;
951 }
952
953 cupsd_kqueue_changes = 0;
954
955 #elif defined(HAVE_POLL)
956 # ifdef HAVE_EPOLL
957 if (cupsd_epoll_events)
958 {
959 free(cupsd_epoll_events);
960 cupsd_epoll_events = NULL;
961 }
962
963 if (cupsd_epoll_fd >= 0)
964 {
965 close(cupsd_epoll_fd);
966 cupsd_epoll_fd = -1;
967 }
968 # endif /* HAVE_EPOLL */
969
970 if (cupsd_pollfds)
971 {
972 free(cupsd_pollfds);
973 cupsd_pollfds = NULL;
974 cupsd_alloc_pollfds = 0;
975 }
976
977 cupsd_update_pollfds = 0;
978
979 #else /* select() */
980 FD_ZERO(&cupsd_global_input);
981 FD_ZERO(&cupsd_global_output);
982 #endif /* HAVE_EPOLL */
983 }
984
985
986 /*
987 * 'compare_fds()' - Compare file descriptors.
988 */
989
990 static int /* O - Result of comparison */
991 compare_fds(_cupsd_fd_t *a, /* I - First file descriptor */
992 _cupsd_fd_t *b) /* I - Second file descriptor */
993 {
994 return (a->fd - b->fd);
995 }
996
997
998 /*
999 * 'find_fd()' - Find an existing file descriptor record.
1000 */
1001
1002 static _cupsd_fd_t * /* O - FD record pointer or NULL */
1003 find_fd(int fd) /* I - File descriptor */
1004 {
1005 _cupsd_fd_t *fdptr, /* Matching record (if any) */
1006 key; /* Search key */
1007
1008
1009 cupsArraySave(cupsd_fds);
1010
1011 key.fd = fd;
1012 fdptr = (_cupsd_fd_t *)cupsArrayFind(cupsd_fds, &key);
1013
1014 cupsArrayRestore(cupsd_fds);
1015
1016 return (fdptr);
1017 }
1018
1019
1020 /*
1021 * End of "$Id: select.c 7093 2007-11-30 19:09:36Z mike $".
1022 */