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