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