]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/select.c
Add option to specify a single operation to test with.
[thirdparty/cups.git] / scheduler / select.c
CommitLineData
f7deaa1a 1/*
b19ccc9e 2 * "$Id: select.c 7720 2008-07-11 22:46:21Z mike $"
f7deaa1a 3 *
10d09e33 4 * Select abstraction functions for the CUPS scheduler.
f7deaa1a 5 *
3dd9c340 6 * Copyright 2007-2012 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>
dcb445bc 36# include <poll.h>
f7deaa1a 37#elif defined(HAVE_KQUEUE)
38# include <sys/event.h>
39# include <sys/time.h>
40#elif defined(HAVE_POLL)
dcb445bc 41# include <poll.h>
f7deaa1a 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 * -----------------------------------------
3dd9c340 52 *
f7deaa1a 53 * SUPPORTED APIS
3dd9c340 54 *
f7deaa1a 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
f7deaa1a 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
3dd9c340
MS
67 *
68 *
f7deaa1a 69 * HIGH-LEVEL API
3dd9c340 70 *
f7deaa1a 71 * typedef void (*cupsd_selfunc_t)(void *data);
3dd9c340 72 *
f7deaa1a 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);
3dd9c340
MS
79 *
80 *
f7deaa1a 81 * IMPLEMENTATION STRATEGY
3dd9c340 82 *
f7deaa1a 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.
3dd9c340 105 *
f7deaa1a 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.
3dd9c340 119 *
f7deaa1a 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.
3dd9c340 135 *
f7deaa1a 136 * 4. kqueue() - O(n)
137 * b. cupsdStartSelect() creates kqueue file descriptor
41681883 138 * using kqueue() function and allocates a global event
f7deaa1a 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.
41681883 146 * e. cupsdStopSelect() closes the kqueue() file descriptor
f7deaa1a 147 * and frees all of the memory used by the event buffer.
3dd9c340 148 *
f7deaa1a 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()
0a682745
MS
170 * performed 5.5% slower than select(), followed by kqueue() at 16%
171 * slower than select() and poll() at 18% slower than select(). Similar
f7deaa1a 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
0a682745 217#ifdef HAVE_KQUEUE
f7deaa1a 218static int cupsd_kqueue_fd = -1,
219 cupsd_kqueue_changes = 0;
220static struct kevent *cupsd_kqueue_events = NULL;
221#elif defined(HAVE_POLL)
222static int cupsd_alloc_pollfds = 0,
223 cupsd_update_pollfds = 0;
224static struct pollfd *cupsd_pollfds = NULL;
0a682745
MS
225# ifdef HAVE_EPOLL
226static int cupsd_epoll_fd = -1;
227static struct epoll_event *cupsd_epoll_events = NULL;
228# endif /* HAVE_EPOLL */
f7deaa1a 229#else /* select() */
230static fd_set cupsd_global_input,
231 cupsd_global_output,
232 cupsd_current_input,
233 cupsd_current_output;
0a682745 234#endif /* HAVE_KQUEUE */
f7deaa1a 235
236
237/*
238 * Local functions...
239 */
240
241static int compare_fds(_cupsd_fd_t *a, _cupsd_fd_t *b);
242static _cupsd_fd_t *find_fd(int fd);
c7017ecc 243#define release_fd(f) { \
f7deaa1a 244 (f)->use --; \
245 if (!(f)->use) free((f));\
246 }
c7017ecc 247#define retain_fd(f) (f)->use++
f7deaa1a 248
249
250/*
251 * 'cupsdAddSelect()' - Add a file descriptor to the list.
252 */
253
254int /* O - 1 on success, 0 on error */
255cupsdAddSelect(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 */
1f0275e3 261#ifdef HAVE_EPOLL
f7deaa1a 262 int added; /* 1 if added, 0 if modified */
1f0275e3 263#endif /* HAVE_EPOLL */
f7deaa1a 264
265
266 /*
267 * Range check input...
268 */
269
270 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1 271 "cupsdAddSelect(fd=%d, read_cb=%p, write_cb=%p, data=%p)",
f7deaa1a 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
1f0275e3 300#ifdef HAVE_EPOLL
f7deaa1a 301 added = 1;
302 }
303 else
304 added = 0;
1f0275e3
MS
305#else
306 }
307#endif /* HAVE_EPOLL */
f7deaa1a 308
0a682745 309#ifdef HAVE_KQUEUE
f7deaa1a 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 {
b9faaae1 327 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
f7deaa1a 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 {
b9faaae1 342 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
f7deaa1a 343 strerror(errno));
344 return (0);
345 }
346 }
347 }
348
349#elif defined(HAVE_POLL)
0a682745
MS
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
f7deaa1a 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)
f7deaa1a 386 FD_SET(fd, &cupsd_global_input);
f7deaa1a 387 else
388 {
f7deaa1a 389 FD_CLR(fd, &cupsd_global_input);
390 FD_CLR(fd, &cupsd_current_input);
391 }
392
393 if (write_cb)
f7deaa1a 394 FD_SET(fd, &cupsd_global_output);
f7deaa1a 395 else
396 {
f7deaa1a 397 FD_CLR(fd, &cupsd_global_output);
398 FD_CLR(fd, &cupsd_current_output);
399 }
0a682745 400#endif /* HAVE_KQUEUE */
f7deaa1a 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
418int /* O - Number of files or -1 on error */
419cupsdDoSelect(long timeout) /* I - Timeout in seconds */
420{
421 int nfds; /* Number of file descriptors */
422 _cupsd_fd_t *fdptr; /* Current file descriptor */
0a682745 423#ifdef HAVE_KQUEUE
f7deaa1a 424 int i; /* Looping var */
425 struct kevent *event; /* Current event */
426 struct timespec ktimeout; /* kevent() timeout */
427
428
c0e1af83 429 cupsd_in_select = 1;
430
f7deaa1a 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
f7deaa1a 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
c0e1af83 448 if (cupsArrayFind(cupsd_inactive_fds, fdptr))
449 continue;
450
f7deaa1a 451 retain_fd(fdptr);
452
453 if (fdptr->read_cb && event->filter == EVFILT_READ)
f7deaa1a 454 (*(fdptr->read_cb))(fdptr->data);
f7deaa1a 455
c7017ecc
MS
456 if (fdptr->use > 1 && fdptr->write_cb && event->filter == EVFILT_WRITE &&
457 !cupsArrayFind(cupsd_inactive_fds, fdptr))
f7deaa1a 458 (*(fdptr->write_cb))(fdptr->data);
f7deaa1a 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
0a682745
MS
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
0a682745
MS
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)))
0a682745 500 (*(fdptr->read_cb))(fdptr->data);
0a682745 501
ef55b745 502 if (fdptr->use > 1 && fdptr->write_cb &&
c7017ecc
MS
503 (event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) &&
504 !cupsArrayFind(cupsd_inactive_fds, fdptr))
0a682745 505 (*(fdptr->write_cb))(fdptr->data);
0a682745
MS
506
507 release_fd(fdptr);
508 }
509
510 goto release_inactive;
511 }
512 }
513# endif /* HAVE_EPOLL */
514
f7deaa1a 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
f7deaa1a 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
f7deaa1a 571 if (timeout >= 0 && timeout < 86400)
572 nfds = poll(cupsd_pollfds, count, timeout * 1000);
573 else
574 nfds = poll(cupsd_pollfds, count, -1);
575
f7deaa1a 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 {
f7deaa1a 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)))
f7deaa1a 593 (*(fdptr->read_cb))(fdptr->data);
f7deaa1a 594
ef55b745
MS
595 if (fdptr->use > 1 && fdptr->write_cb &&
596 (pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
f7deaa1a 597 (*(fdptr->write_cb))(fdptr->data);
f7deaa1a 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
f7deaa1a 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
f7deaa1a 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))
f7deaa1a 649 (*(fdptr->read_cb))(fdptr->data);
f7deaa1a 650
ef55b745
MS
651 if (fdptr->use > 1 && fdptr->write_cb &&
652 FD_ISSET(fdptr->fd, &cupsd_current_output))
f7deaa1a 653 (*(fdptr->write_cb))(fdptr->data);
f7deaa1a 654
655 release_fd(fdptr);
656 }
657 }
658
0a682745 659#endif /* HAVE_KQUEUE */
f7deaa1a 660
c0e1af83 661#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
662 /*
663 * Release all inactive file descriptors...
664 */
665
0a682745
MS
666# ifndef HAVE_KQUEUE
667 release_inactive:
668# endif /* !HAVE_KQUEUE */
669
c0e1af83 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
f7deaa1a 681 /*
682 * Return the number of file descriptors handled...
683 */
684
685 return (nfds);
686}
687
688
f899b121 689#ifdef CUPSD_IS_SELECTING
f7deaa1a 690/*
691 * 'cupsdIsSelecting()' - Determine whether we are monitoring a file
692 * descriptor.
693 */
694
695int /* O - 1 if selecting, 0 otherwise */
696cupsdIsSelecting(int fd) /* I - File descriptor */
697{
698 return (find_fd(fd) != NULL);
699}
f899b121 700#endif /* CUPSD_IS_SELECTING */
f7deaa1a 701
702
703/*
704 * 'cupsdRemoveSelect()' - Remove a file descriptor from the list.
705 */
706
707void
708cupsdRemoveSelect(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
b9faaae1 725 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRemoveSelect(fd=%d)", fd);
f7deaa1a 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
0a682745
MS
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 }
f7deaa1a 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 {
b9faaae1 755 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
f7deaa1a 756 strerror(errno));
4a4b4f99 757 goto cleanup;
f7deaa1a 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 {
b9faaae1 767 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
f7deaa1a 768 strerror(errno));
4a4b4f99 769 goto cleanup;
f7deaa1a 770 }
771 }
772
f7deaa1a 773#elif defined(HAVE_POLL)
774 /*
775 * Update the pollfds array...
776 */
777
778 cupsd_update_pollfds = 1;
779
780#else /* select() */
f7deaa1a 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
4a4b4f99
MS
787#ifdef HAVE_KQUEUE
788 cleanup:
789#endif /* HAVE_KQUEUE */
790
f7deaa1a 791 /*
c0e1af83 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...)
f7deaa1a 794 */
795
796 cupsArrayRemove(cupsd_fds, fdptr);
c0e1af83 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
f7deaa1a 804 release_fd(fdptr);
805}
806
807
808/*
809 * 'cupsdStartSelect()' - Initialize the file polling engine.
810 */
811
812void
813cupsdStartSelect(void)
814{
b9faaae1
MS
815 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSelect()");
816
f7deaa1a 817 cupsd_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
818
c0e1af83 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
f7deaa1a 823#ifdef HAVE_EPOLL
0a682745
MS
824 cupsd_epoll_fd = epoll_create(MaxFDs);
825 cupsd_epoll_events = calloc(MaxFDs, sizeof(struct epoll_event));
826 cupsd_update_pollfds = 0;
f7deaa1a 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
847void
848cupsdStopSelect(void)
849{
850 _cupsd_fd_t *fdptr; /* Current file descriptor */
851
852
b9faaae1
MS
853 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopSelect()");
854
f7deaa1a 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
c0e1af83 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
0a682745 868#ifdef HAVE_KQUEUE
f7deaa1a 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)
0a682745
MS
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
f7deaa1a 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
918static int /* O - Result of comparison */
919compare_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
930static _cupsd_fd_t * /* O - FD record pointer or NULL */
931find_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/*
b19ccc9e 949 * End of "$Id: select.c 7720 2008-07-11 22:46:21Z mike $".
f7deaa1a 950 */