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