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