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