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