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