]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/select.c
Merge changes from CUPS 1.5svn-r8857.
[thirdparty/cups.git] / scheduler / select.c
CommitLineData
f7deaa1a 1/*
b19ccc9e 2 * "$Id: select.c 7720 2008-07-11 22:46:21Z mike $"
f7deaa1a 3 *
4 * Select abstraction functions for the Common UNIX Printing System (CUPS).
5 *
b9faaae1 6 * Copyright 2007-2009 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 */
1f0275e3 262#ifdef HAVE_EPOLL
f7deaa1a 263 int added; /* 1 if added, 0 if modified */
1f0275e3 264#endif /* HAVE_EPOLL */
f7deaa1a 265
266
267 /*
268 * Range check input...
269 */
270
271 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1 272 "cupsdAddSelect(fd=%d, read_cb=%p, write_cb=%p, data=%p)",
f7deaa1a 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
1f0275e3 301#ifdef HAVE_EPOLL
f7deaa1a 302 added = 1;
303 }
304 else
305 added = 0;
1f0275e3
MS
306#else
307 }
308#endif /* HAVE_EPOLL */
f7deaa1a 309
0a682745 310#ifdef HAVE_KQUEUE
f7deaa1a 311 {
312 struct kevent event; /* Event data */
313 struct timespec timeout; /* Timeout value */
314
315
316 timeout.tv_sec = 0;
317 timeout.tv_nsec = 0;
318
319 if (fdptr->read_cb != read_cb)
320 {
321 if (read_cb)
322 EV_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, fdptr);
323 else
324 EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
325
326 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
327 {
b9faaae1 328 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
f7deaa1a 329 strerror(errno));
330 return (0);
331 }
332 }
333
334 if (fdptr->write_cb != write_cb)
335 {
336 if (write_cb)
337 EV_SET(&event, fd, EVFILT_WRITE, EV_ADD, 0, 0, fdptr);
338 else
339 EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
340
341 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
342 {
b9faaae1 343 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
f7deaa1a 344 strerror(errno));
345 return (0);
346 }
347 }
348 }
349
350#elif defined(HAVE_POLL)
0a682745
MS
351# ifdef HAVE_EPOLL
352 if (cupsd_epoll_fd >= 0)
353 {
354 struct epoll_event event; /* Event data */
355
356
357 event.events = 0;
358
359 if (read_cb)
360 event.events |= EPOLLIN;
361
362 if (write_cb)
363 event.events |= EPOLLOUT;
364
365 event.data.ptr = fdptr;
366
367 if (epoll_ctl(cupsd_epoll_fd, added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd,
368 &event))
369 {
370 close(cupsd_epoll_fd);
371 cupsd_epoll_fd = -1;
372 cupsd_update_pollfds = 1;
373 }
374 }
375 else
376# endif /* HAVE_EPOLL */
377
f7deaa1a 378 cupsd_update_pollfds = 1;
379
380#else /* select() */
381 /*
382 * Add or remove the file descriptor in the input and output sets
383 * for select()...
384 */
385
386 if (read_cb)
f7deaa1a 387 FD_SET(fd, &cupsd_global_input);
f7deaa1a 388 else
389 {
f7deaa1a 390 FD_CLR(fd, &cupsd_global_input);
391 FD_CLR(fd, &cupsd_current_input);
392 }
393
394 if (write_cb)
f7deaa1a 395 FD_SET(fd, &cupsd_global_output);
f7deaa1a 396 else
397 {
f7deaa1a 398 FD_CLR(fd, &cupsd_global_output);
399 FD_CLR(fd, &cupsd_current_output);
400 }
0a682745 401#endif /* HAVE_KQUEUE */
f7deaa1a 402
403 /*
404 * Save the (new) read and write callbacks...
405 */
406
407 fdptr->read_cb = read_cb;
408 fdptr->write_cb = write_cb;
409 fdptr->data = data;
410
411 return (1);
412}
413
414
415/*
416 * 'cupsdDoSelect()' - Do a select-like operation.
417 */
418
419int /* O - Number of files or -1 on error */
420cupsdDoSelect(long timeout) /* I - Timeout in seconds */
421{
422 int nfds; /* Number of file descriptors */
423 _cupsd_fd_t *fdptr; /* Current file descriptor */
0a682745 424#ifdef HAVE_KQUEUE
f7deaa1a 425 int i; /* Looping var */
426 struct kevent *event; /* Current event */
427 struct timespec ktimeout; /* kevent() timeout */
428
429
c0e1af83 430 cupsd_in_select = 1;
431
f7deaa1a 432 if (timeout >= 0 && timeout < 86400)
433 {
434 ktimeout.tv_sec = timeout;
435 ktimeout.tv_nsec = 0;
436
437 nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs,
438 &ktimeout);
439 }
440 else
441 nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs, NULL);
442
f7deaa1a 443 cupsd_kqueue_changes = 0;
444
445 for (i = nfds, event = cupsd_kqueue_events; i > 0; i --, event ++)
446 {
447 fdptr = (_cupsd_fd_t *)event->udata;
448
c0e1af83 449 if (cupsArrayFind(cupsd_inactive_fds, fdptr))
450 continue;
451
f7deaa1a 452 retain_fd(fdptr);
453
454 if (fdptr->read_cb && event->filter == EVFILT_READ)
f7deaa1a 455 (*(fdptr->read_cb))(fdptr->data);
f7deaa1a 456
457 if (fdptr->write_cb && event->filter == EVFILT_WRITE)
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
MS
501
502 if (fdptr->write_cb && (event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)))
0a682745 503 (*(fdptr->write_cb))(fdptr->data);
0a682745
MS
504
505 release_fd(fdptr);
506 }
507
508 goto release_inactive;
509 }
510 }
511# endif /* HAVE_EPOLL */
512
f7deaa1a 513 count = cupsArrayCount(cupsd_fds);
514
515 if (cupsd_update_pollfds)
516 {
517 /*
518 * Update the cupsd_pollfds array to match the current FD array...
519 */
520
521 cupsd_update_pollfds = 0;
522
f7deaa1a 523 /*
524 * (Re)allocate memory as needed...
525 */
526
527 if (count > cupsd_alloc_pollfds)
528 {
529 int allocfds = count + 16;
530
531
532 if (cupsd_pollfds)
533 pfd = realloc(cupsd_pollfds, allocfds * sizeof(struct pollfd));
534 else
535 pfd = malloc(allocfds * sizeof(struct pollfd));
536
537 if (!pfd)
538 {
539 cupsdLogMessage(CUPSD_LOG_EMERG,
540 "Unable to allocate %d bytes for polling!",
541 (int)(allocfds * sizeof(struct pollfd)));
542
543 return (-1);
544 }
545
546 cupsd_pollfds = pfd;
547 cupsd_alloc_pollfds = allocfds;
548 }
549
550 /*
551 * Rebuild the array...
552 */
553
554 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds), pfd = cupsd_pollfds;
555 fdptr;
556 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds), pfd ++)
557 {
558 pfd->fd = fdptr->fd;
559 pfd->events = 0;
560
561 if (fdptr->read_cb)
562 pfd->events |= POLLIN;
563
564 if (fdptr->write_cb)
565 pfd->events |= POLLOUT;
566 }
567 }
568
f7deaa1a 569 if (timeout >= 0 && timeout < 86400)
570 nfds = poll(cupsd_pollfds, count, timeout * 1000);
571 else
572 nfds = poll(cupsd_pollfds, count, -1);
573
f7deaa1a 574 if (nfds > 0)
575 {
576 /*
577 * Do callbacks for each file descriptor...
578 */
579
580 for (pfd = cupsd_pollfds; count > 0; pfd ++, count --)
581 {
f7deaa1a 582 if (!pfd->revents)
583 continue;
584
585 if ((fdptr = find_fd(pfd->fd)) == NULL)
586 continue;
587
588 retain_fd(fdptr);
589
590 if (fdptr->read_cb && (pfd->revents & (POLLIN | POLLERR | POLLHUP)))
f7deaa1a 591 (*(fdptr->read_cb))(fdptr->data);
f7deaa1a 592
593 if (fdptr->write_cb && (pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
f7deaa1a 594 (*(fdptr->write_cb))(fdptr->data);
f7deaa1a 595
596 release_fd(fdptr);
597 }
598 }
599
600#else /* select() */
601 struct timeval stimeout; /* Timeout for select() */
602 int maxfd; /* Maximum file descriptor */
603
604
605 /*
606 * Figure out the highest file descriptor number...
607 */
608
609 if ((fdptr = (_cupsd_fd_t *)cupsArrayLast(cupsd_fds)) == NULL)
610 maxfd = 1;
611 else
612 maxfd = fdptr->fd + 1;
613
614 /*
615 * Do the select()...
616 */
617
618 cupsd_current_input = cupsd_global_input;
619 cupsd_current_output = cupsd_global_output;
620
f7deaa1a 621 if (timeout >= 0 && timeout < 86400)
622 {
623 stimeout.tv_sec = timeout;
624 stimeout.tv_usec = 0;
625
626 nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
627 &stimeout);
628 }
629 else
630 nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
631 NULL);
632
f7deaa1a 633 if (nfds > 0)
634 {
635 /*
636 * Do callbacks for each file descriptor...
637 */
638
639 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
640 fdptr;
641 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
642 {
643 retain_fd(fdptr);
644
645 if (fdptr->read_cb && FD_ISSET(fdptr->fd, &cupsd_current_input))
f7deaa1a 646 (*(fdptr->read_cb))(fdptr->data);
f7deaa1a 647
648 if (fdptr->write_cb && FD_ISSET(fdptr->fd, &cupsd_current_output))
f7deaa1a 649 (*(fdptr->write_cb))(fdptr->data);
f7deaa1a 650
651 release_fd(fdptr);
652 }
653 }
654
0a682745 655#endif /* HAVE_KQUEUE */
f7deaa1a 656
c0e1af83 657#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
658 /*
659 * Release all inactive file descriptors...
660 */
661
0a682745
MS
662# ifndef HAVE_KQUEUE
663 release_inactive:
664# endif /* !HAVE_KQUEUE */
665
c0e1af83 666 cupsd_in_select = 0;
667
668 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_inactive_fds);
669 fdptr;
670 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_inactive_fds))
671 {
672 cupsArrayRemove(cupsd_inactive_fds, fdptr);
673 release_fd(fdptr);
674 }
675#endif /* HAVE_EPOLL || HAVE_KQUEUE */
676
f7deaa1a 677 /*
678 * Return the number of file descriptors handled...
679 */
680
681 return (nfds);
682}
683
684
f899b121 685#ifdef CUPSD_IS_SELECTING
f7deaa1a 686/*
687 * 'cupsdIsSelecting()' - Determine whether we are monitoring a file
688 * descriptor.
689 */
690
691int /* O - 1 if selecting, 0 otherwise */
692cupsdIsSelecting(int fd) /* I - File descriptor */
693{
694 return (find_fd(fd) != NULL);
695}
f899b121 696#endif /* CUPSD_IS_SELECTING */
f7deaa1a 697
698
699/*
700 * 'cupsdRemoveSelect()' - Remove a file descriptor from the list.
701 */
702
703void
704cupsdRemoveSelect(int fd) /* I - File descriptor */
705{
706 _cupsd_fd_t *fdptr; /* File descriptor record */
707#ifdef HAVE_EPOLL
708 struct epoll_event event; /* Event data */
709#elif defined(HAVE_KQUEUE)
710 struct kevent event; /* Event data */
711 struct timespec timeout; /* Timeout value */
712#elif defined(HAVE_POLL)
713 /* No variables for poll() */
714#endif /* HAVE_EPOLL */
715
716
717 /*
718 * Range check input...
719 */
720
b9faaae1 721 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRemoveSelect(fd=%d)", fd);
f7deaa1a 722
723 if (fd < 0)
724 return;
725
726 /*
727 * Find the file descriptor...
728 */
729
730 if ((fdptr = find_fd(fd)) == NULL)
731 return;
732
733#ifdef HAVE_EPOLL
0a682745
MS
734 if (epoll_ctl(cupsd_epoll_fd, EPOLL_CTL_DEL, fd, &event))
735 {
736 close(cupsd_epoll_fd);
737 cupsd_epoll_fd = -1;
738 cupsd_update_pollfds = 1;
739 }
f7deaa1a 740
741#elif defined(HAVE_KQUEUE)
742 timeout.tv_sec = 0;
743 timeout.tv_nsec = 0;
744
745 if (fdptr->read_cb)
746 {
747 EV_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, fdptr);
748
749 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
750 {
b9faaae1 751 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
f7deaa1a 752 strerror(errno));
4a4b4f99 753 goto cleanup;
f7deaa1a 754 }
755 }
756
757 if (fdptr->write_cb)
758 {
759 EV_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdptr);
760
761 if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
762 {
b9faaae1 763 cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
f7deaa1a 764 strerror(errno));
4a4b4f99 765 goto cleanup;
f7deaa1a 766 }
767 }
768
f7deaa1a 769#elif defined(HAVE_POLL)
770 /*
771 * Update the pollfds array...
772 */
773
774 cupsd_update_pollfds = 1;
775
776#else /* select() */
f7deaa1a 777 FD_CLR(fd, &cupsd_global_input);
778 FD_CLR(fd, &cupsd_global_output);
779 FD_CLR(fd, &cupsd_current_input);
780 FD_CLR(fd, &cupsd_current_output);
781#endif /* HAVE_EPOLL */
782
4a4b4f99
MS
783#ifdef HAVE_KQUEUE
784 cleanup:
785#endif /* HAVE_KQUEUE */
786
f7deaa1a 787 /*
c0e1af83 788 * Remove the file descriptor from the active array and add to the
789 * inactive array (or release, if we don't need the inactive array...)
f7deaa1a 790 */
791
792 cupsArrayRemove(cupsd_fds, fdptr);
c0e1af83 793
794#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
795 if (cupsd_in_select)
796 cupsArrayAdd(cupsd_inactive_fds, fdptr);
797 else
798#endif /* HAVE_EPOLL || HAVE_KQUEUE */
799
f7deaa1a 800 release_fd(fdptr);
801}
802
803
804/*
805 * 'cupsdStartSelect()' - Initialize the file polling engine.
806 */
807
808void
809cupsdStartSelect(void)
810{
b9faaae1
MS
811 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSelect()");
812
f7deaa1a 813 cupsd_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
814
c0e1af83 815#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
816 cupsd_inactive_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
817#endif /* HAVE_EPOLL || HAVE_KQUEUE */
818
f7deaa1a 819#ifdef HAVE_EPOLL
0a682745
MS
820 cupsd_epoll_fd = epoll_create(MaxFDs);
821 cupsd_epoll_events = calloc(MaxFDs, sizeof(struct epoll_event));
822 cupsd_update_pollfds = 0;
f7deaa1a 823
824#elif defined(HAVE_KQUEUE)
825 cupsd_kqueue_fd = kqueue();
826 cupsd_kqueue_changes = 0;
827 cupsd_kqueue_events = calloc(MaxFDs, sizeof(struct kevent));
828
829#elif defined(HAVE_POLL)
830 cupsd_update_pollfds = 0;
831
832#else /* select() */
833 FD_ZERO(&cupsd_global_input);
834 FD_ZERO(&cupsd_global_output);
835#endif /* HAVE_EPOLL */
836}
837
838
839/*
840 * 'cupsdStopSelect()' - Shutdown the file polling engine.
841 */
842
843void
844cupsdStopSelect(void)
845{
846 _cupsd_fd_t *fdptr; /* Current file descriptor */
847
848
b9faaae1
MS
849 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopSelect()");
850
f7deaa1a 851 for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
852 fdptr;
853 fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
854 free(fdptr);
855
856 cupsArrayDelete(cupsd_fds);
857 cupsd_fds = NULL;
858
c0e1af83 859#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
860 cupsArrayDelete(cupsd_inactive_fds);
861 cupsd_inactive_fds = NULL;
862#endif /* HAVE_EPOLL || HAVE_KQUEUE */
863
0a682745 864#ifdef HAVE_KQUEUE
f7deaa1a 865 if (cupsd_kqueue_events)
866 {
867 free(cupsd_kqueue_events);
868 cupsd_kqueue_events = NULL;
869 }
870
871 if (cupsd_kqueue_fd >= 0)
872 {
873 close(cupsd_kqueue_fd);
874 cupsd_kqueue_fd = -1;
875 }
876
877 cupsd_kqueue_changes = 0;
878
879#elif defined(HAVE_POLL)
0a682745
MS
880# ifdef HAVE_EPOLL
881 if (cupsd_epoll_events)
882 {
883 free(cupsd_epoll_events);
884 cupsd_epoll_events = NULL;
885 }
886
887 if (cupsd_epoll_fd >= 0)
888 {
889 close(cupsd_epoll_fd);
890 cupsd_epoll_fd = -1;
891 }
892# endif /* HAVE_EPOLL */
893
f7deaa1a 894 if (cupsd_pollfds)
895 {
896 free(cupsd_pollfds);
897 cupsd_pollfds = NULL;
898 cupsd_alloc_pollfds = 0;
899 }
900
901 cupsd_update_pollfds = 0;
902
903#else /* select() */
904 FD_ZERO(&cupsd_global_input);
905 FD_ZERO(&cupsd_global_output);
906#endif /* HAVE_EPOLL */
907}
908
909
910/*
911 * 'compare_fds()' - Compare file descriptors.
912 */
913
914static int /* O - Result of comparison */
915compare_fds(_cupsd_fd_t *a, /* I - First file descriptor */
916 _cupsd_fd_t *b) /* I - Second file descriptor */
917{
918 return (a->fd - b->fd);
919}
920
921
922/*
923 * 'find_fd()' - Find an existing file descriptor record.
924 */
925
926static _cupsd_fd_t * /* O - FD record pointer or NULL */
927find_fd(int fd) /* I - File descriptor */
928{
929 _cupsd_fd_t *fdptr, /* Matching record (if any) */
930 key; /* Search key */
931
932
933 cupsArraySave(cupsd_fds);
934
935 key.fd = fd;
936 fdptr = (_cupsd_fd_t *)cupsArrayFind(cupsd_fds, &key);
937
938 cupsArrayRestore(cupsd_fds);
939
940 return (fdptr);
941}
942
943
944/*
b19ccc9e 945 * End of "$Id: select.c 7720 2008-07-11 22:46:21Z mike $".
f7deaa1a 946 */