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