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