]>
Commit | Line | Data |
---|---|---|
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 | ||
177 | typedef 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 | ||
191 | static cups_array_t *cupsd_fds = NULL; | |
c0e1af83 | 192 | #if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE) |
193 | static cups_array_t *cupsd_inactive_fds = NULL; | |
194 | static int cupsd_in_select = 0; | |
195 | #endif /* HAVE_EPOLL || HAVE_KQUEUE */ | |
f7deaa1a | 196 | |
0a682745 | 197 | #ifdef HAVE_KQUEUE |
f7deaa1a | 198 | static int cupsd_kqueue_fd = -1, |
199 | cupsd_kqueue_changes = 0; | |
200 | static struct kevent *cupsd_kqueue_events = NULL; | |
201 | #elif defined(HAVE_POLL) | |
202 | static int cupsd_alloc_pollfds = 0, | |
203 | cupsd_update_pollfds = 0; | |
204 | static struct pollfd *cupsd_pollfds = NULL; | |
0a682745 MS |
205 | # ifdef HAVE_EPOLL |
206 | static int cupsd_epoll_fd = -1; | |
207 | static struct epoll_event *cupsd_epoll_events = NULL; | |
208 | # endif /* HAVE_EPOLL */ | |
f7deaa1a | 209 | #else /* select() */ |
210 | static 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 | ||
221 | static int compare_fds(_cupsd_fd_t *a, _cupsd_fd_t *b); | |
222 | static _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 | ||
234 | int /* O - 1 on success, 0 on error */ | |
235 | cupsdAddSelect(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 | ||
398 | int /* O - Number of files or -1 on error */ | |
399 | cupsdDoSelect(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 | ||
673 | int /* O - 1 if selecting, 0 otherwise */ | |
674 | cupsdIsSelecting(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 | ||
685 | void | |
686 | cupsdRemoveSelect(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 | ||
790 | void | |
791 | cupsdStartSelect(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 | ||
825 | void | |
826 | cupsdStopSelect(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 | ||
896 | static int /* O - Result of comparison */ | |
897 | compare_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 | ||
908 | static _cupsd_fd_t * /* O - FD record pointer or NULL */ | |
909 | find_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 | } |