]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/select.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / scheduler / select.c
index c72adbdc98fb35f91651f268bf663e6ce0868dde..06079c37a0860db5e4a24c2c2b59001cec62a3ac 100644 (file)
@@ -1,37 +1,10 @@
 /*
- * "$Id: select.c 6166 2006-12-29 20:35:18Z mike $"
+ * Select abstraction functions for the CUPS scheduler.
  *
- *   Select abstraction functions for the Common UNIX Printing System (CUPS).
+ * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2006-2007 by Easy Software Products.
  *
- *   Copyright 2006 by Easy Software Products.
- *
- *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
- *
- * Contents:
- *
- *   cupsdAddSelect()    - Add a file descriptor to the list.
- *   cupsdDoSelect()     - Do a select-like operation.
- *   cupsdIsSelecting()  - Determine whether we are monitoring a file
- *                         descriptor.
- *   cupsdRemoveSelect() - Remove a file descriptor from the list.
- *   cupsdStartSelect()  - Initialize the file polling engine.
- *   cupsdStopSelect()   - Shutdown the file polling engine.
- *   compare_fds()       - Compare file descriptors.
- *   find_fd()           - Find an existing file descriptor record.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
  */
 
 /*
 
 #ifdef HAVE_EPOLL
 #  include <sys/epoll.h>
+#  include <poll.h>
 #elif defined(HAVE_KQUEUE)
 #  include <sys/event.h>
 #  include <sys/time.h>
 #elif defined(HAVE_POLL)
-#  include <sys/poll.h>
-#elif defined(__hpux)
-#  include <sys/time.h>
+#  include <poll.h>
 #else
 #  include <sys/select.h>
 #endif /* HAVE_EPOLL */
 /*
  * Design Notes for Poll/Select API in CUPSD
  * -----------------------------------------
- * 
+ *
  * SUPPORTED APIS
- * 
+ *
  *     OS              select  poll    epoll   kqueue  /dev/poll
  *     --------------  ------  ------  ------  ------  ---------
  *     AIX             YES     YES     NO      NO      NO
  *     FreeBSD         YES     YES     NO      YES     NO
  *     HP-UX           YES     YES     NO      NO      NO
- *     IRIX            YES     YES     NO      NO      NO
  *     Linux           YES     YES     YES     NO      NO
- *     MacOS X         YES     YES     NO      YES     NO
+ *     macOS           YES     YES     NO      YES     NO
  *     NetBSD          YES     YES     NO      YES     NO
  *     OpenBSD         YES     YES     NO      YES     NO
  *     Solaris         YES     YES     NO      NO      YES
  *     Tru64           YES     YES     NO      NO      NO
  *     Windows         YES     NO      NO      NO      NO
- * 
- * 
+ *
+ *
  * HIGH-LEVEL API
- * 
+ *
  *     typedef void (*cupsd_selfunc_t)(void *data);
- * 
+ *
  *     void cupsdStartSelect(void);
  *     void cupsdStopSelect(void);
  *     void cupsdAddSelect(int fd, cupsd_selfunc_t read_cb,
  *                         cupsd_selfunc_t write_cb, void *data);
  *     void cupsdRemoveSelect(int fd);
  *     int cupsdDoSelect(int timeout);
- * 
- * 
+ *
+ *
  * IMPLEMENTATION STRATEGY
- * 
+ *
  *     0. Common Stuff
  *         a. CUPS array of file descriptor to callback functions
- *            and data.
- *         b. cupsdStartSelect() creates the array
- *         c. cupsdStopSelect() destroys the array and all elements.
+ *            and data + temporary array of removed fd's.
+ *         b. cupsdStartSelect() creates the arrays
+ *         c. cupsdStopSelect() destroys the arrays and all elements.
  *         d. cupsdAddSelect() adds to the array and allocates a
  *            new callback element.
- *         e. cupsdRemoveSelect() removes from the array and frees
- *            the callback element.
+ *         e. cupsdRemoveSelect() removes from the active array and
+ *            adds to the inactive array.
  *         f. _cupsd_fd_t provides a reference-counted structure for
  *            tracking file descriptors that are monitored.
- * 
+ *         g. cupsdDoSelect() frees all inactive FDs.
+ *
  *     1. select() O(n)
  *         a. Input/Output fd_set variables, copied to working
  *            copies and then used with select().
  *            working sets.
  *         d. cupsdStopSelect() frees all of the memory used by the
  *            CUPS array and fd_set's.
- * 
+ *
  *     2. poll() - O(n log n)
  *         a. Regular array of pollfd, sorted the same as the CUPS
  *            array.
  *         e. cupsdRemoveSelect() flags the pollfd array as invalid.
  *         f. cupsdStopSelect() frees all of the memory used by the
  *            CUPS array and pollfd array.
- * 
+ *
  *     3. epoll() - O(n)
  *         a. cupsdStartSelect() creates epoll file descriptor using
  *            epoll_create() with the maximum fd count, and
  *            the callback record.
  *         d. cupsdStopSelect() closes the epoll file descriptor and
  *            frees all of the memory used by the event buffer.
- * 
+ *
  *     4. kqueue() - O(n)
  *         b. cupsdStartSelect() creates kqueue file descriptor
- *            using kqyeue() function and allocates a global event
+ *            using kqueue() function and allocates a global event
  *            buffer.
  *         c. cupsdAdd/RemoveSelect() uses EV_SET and kevent() to
  *            register the changes. The event user data field is a
  *         d. cupsdDoSelect() uses kevent() to poll for events and
  *            loops through the events, using the user data field to
  *            find the callback record.
- *         e. cupsdStopSelect() closes the kqyeye() file descriptor
+ *         e. cupsdStopSelect() closes the kqueue() file descriptor
  *            and frees all of the memory used by the event buffer.
- * 
+ *
  *     5. /dev/poll - O(n log n) - NOT YET IMPLEMENTED
  *         a. cupsdStartSelect() opens /dev/poll and allocates an
  *            array of pollfd structs; on failure to open /dev/poll,
  *
  *   In tests using the "make test" target with option 0 (keep cupsd
  *   running) and the "testspeed" program with "-c 50 -r 1000", epoll()
- *   performed 5.5% slower select(), followed by kqueue() at 16% slower
- *   than select() and poll() at 18% slower than select().  Similar
+ *   performed 5.5% slower than select(), followed by kqueue() at 16%
+ *   slower than select() and poll() at 18% slower than select().  Similar
  *   results were seen with twice the number of client connections.
  *
  *   The epoll() and kqueue() performance is likely limited by the
@@ -217,11 +189,12 @@ typedef struct _cupsd_fd_s
  */
 
 static cups_array_t    *cupsd_fds = NULL;
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+static cups_array_t    *cupsd_inactive_fds = NULL;
+static int             cupsd_in_select = 0;
+#endif /* HAVE_EPOLL || HAVE_KQUEUE */
 
-#ifdef HAVE_EPOLL
-static int             cupsd_epoll_fd = -1;
-static struct epoll_event *cupsd_epoll_events = NULL;
-#elif defined(HAVE_KQUEUE)
+#ifdef HAVE_KQUEUE
 static int             cupsd_kqueue_fd = -1,
                        cupsd_kqueue_changes = 0;
 static struct kevent   *cupsd_kqueue_events = NULL;
@@ -229,12 +202,16 @@ static struct kevent      *cupsd_kqueue_events = NULL;
 static int             cupsd_alloc_pollfds = 0,
                        cupsd_update_pollfds = 0;
 static struct pollfd   *cupsd_pollfds = NULL;
+#  ifdef HAVE_EPOLL
+static int             cupsd_epoll_fd = -1;
+static struct epoll_event *cupsd_epoll_events = NULL;
+#  endif /* HAVE_EPOLL */
 #else /* select() */
 static fd_set          cupsd_global_input,
                        cupsd_global_output,
                        cupsd_current_input,
                        cupsd_current_output;
-#endif /* HAVE_EPOLL */
+#endif /* HAVE_KQUEUE */
 
 
 /*
@@ -261,7 +238,9 @@ cupsdAddSelect(int             fd,  /* I - File descriptor */
               void            *data)   /* I - Data to pass to callback */
 {
   _cupsd_fd_t  *fdptr;                 /* File descriptor record */
+#ifdef HAVE_EPOLL
   int          added;                  /* 1 if added, 0 if modified */
+#endif /* HAVE_EPOLL */
 
 
  /*
@@ -269,7 +248,7 @@ cupsdAddSelect(int             fd,  /* I - File descriptor */
   */
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdAddSelect: fd=%d, read_cb=%p, write_cb=%p, data=%p",
+                  "cupsdAddSelect(fd=%d, read_cb=%p, write_cb=%p, data=%p)",
                  fd, read_cb, write_cb, data);
 
   if (fd < 0)
@@ -298,31 +277,16 @@ cupsdAddSelect(int             fd,        /* I - File descriptor */
       return (0);
     }
 
+#ifdef HAVE_EPOLL
     added = 1;
   }
   else
     added = 0;
-
-#ifdef HAVE_EPOLL
-  {
-    struct epoll_event event;          /* Event data */
-
-
-    event.events = 0;
-
-    if (read_cb)
-      event.events |= EPOLLIN;
-
-    if (write_cb)
-      event.events |= EPOLLOUT;
-
-    event.data.ptr = fdptr;
-
-    epoll_ctl(cupsd_epoll_fd, added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd,
-              &event);
+#else
   }
+#endif /* HAVE_EPOLL */
 
-#elif defined(HAVE_KQUEUE)
+#ifdef HAVE_KQUEUE
   {
     struct kevent      event;          /* Event data */
     struct timespec    timeout;        /* Timeout value */
@@ -340,8 +304,7 @@ cupsdAddSelect(int             fd,  /* I - File descriptor */
 
       if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
       {
-       cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                       "cupsdAddSelect: kevent() returned %s",
+       cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
                        strerror(errno));
        return (0);
       }
@@ -356,8 +319,7 @@ cupsdAddSelect(int             fd,  /* I - File descriptor */
 
       if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
       {
-       cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                       "cupsdAddSelect: kevent() returned %s",
+       cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
                        strerror(errno));
        return (0);
       }
@@ -365,6 +327,33 @@ cupsdAddSelect(int             fd, /* I - File descriptor */
   }
 
 #elif defined(HAVE_POLL)
+#  ifdef HAVE_EPOLL
+  if (cupsd_epoll_fd >= 0)
+  {
+    struct epoll_event event;          /* Event data */
+
+
+    event.events = 0;
+
+    if (read_cb)
+      event.events |= EPOLLIN;
+
+    if (write_cb)
+      event.events |= EPOLLOUT;
+
+    event.data.ptr = fdptr;
+
+    if (epoll_ctl(cupsd_epoll_fd, added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd,
+                  &event))
+    {
+      close(cupsd_epoll_fd);
+      cupsd_epoll_fd       = -1;
+      cupsd_update_pollfds = 1;
+    }
+  }
+  else
+#  endif /* HAVE_EPOLL */
+
   cupsd_update_pollfds = 1;
 
 #else /* select() */
@@ -374,33 +363,21 @@ cupsdAddSelect(int             fd,        /* I - File descriptor */
   */
 
   if (read_cb)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdAddSelect: Adding fd %d to input set...", fd);
     FD_SET(fd, &cupsd_global_input);
-  }
   else
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdAddSelect: Removing fd %d from input set...", fd);
     FD_CLR(fd, &cupsd_global_input);
     FD_CLR(fd, &cupsd_current_input);
   }
 
   if (write_cb)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdAddSelect: Adding fd %d to output set...", fd);
     FD_SET(fd, &cupsd_global_output);
-  }
   else
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdAddSelect: Removing fd %d from output set...", fd);
     FD_CLR(fd, &cupsd_global_output);
     FD_CLR(fd, &cupsd_current_output);
   }
-#endif /* HAVE_EPOLL */
+#endif /* HAVE_KQUEUE */
 
  /*
   * Save the (new) read and write callbacks...
@@ -423,56 +400,13 @@ cupsdDoSelect(long timeout)               /* I - Timeout in seconds */
 {
   int                  nfds;           /* Number of file descriptors */
   _cupsd_fd_t          *fdptr;         /* Current file descriptor */
-#ifdef HAVE_EPOLL
-  int                  i;              /* Looping var */
-  struct epoll_event   *event;         /* Current event */
-
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdDoSelect: polling %d fds for %ld seconds...",
-                 cupsArrayCount(cupsd_fds), timeout);
-
-  if (timeout >= 0 && timeout < 86400)
-    nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs,
-                      timeout * 1000);
-  else
-    nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs, -1);
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: epoll() returned %d...",
-                  nfds);
-
-  for (i = nfds, event = cupsd_epoll_events; i > 0; i --, event ++)
-  {
-    fdptr = (_cupsd_fd_t *)event->data.ptr;
-
-    retain_fd(fdptr);
-
-    if (fdptr->read_cb && (event->events & (EPOLLIN | EPOLLERR | EPOLLHUP)))
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
-                     fdptr->fd);
-      (*(fdptr->read_cb))(fdptr->data);
-    }
-
-    if (fdptr->write_cb && (event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)))
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
-                     fdptr->fd);
-      (*(fdptr->write_cb))(fdptr->data);
-    }
-
-    release_fd(fdptr);
-  }
-
-#elif defined(HAVE_KQUEUE)
+#ifdef HAVE_KQUEUE
   int                  i;              /* Looping var */
   struct kevent                *event;         /* Current event */
   struct timespec      ktimeout;       /* kevent() timeout */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdDoSelect: polling %d fds for %ld seconds...",
-                 cupsArrayCount(cupsd_fds), timeout);
+  cupsd_in_select = 1;
 
   if (timeout >= 0 && timeout < 86400)
   {
@@ -485,34 +419,23 @@ cupsdDoSelect(long timeout)               /* I - Timeout in seconds */
   else
     nfds = kevent(cupsd_kqueue_fd, NULL, 0, cupsd_kqueue_events, MaxFDs, NULL);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdDoSelect: kevent(%d, ..., %d, ...) returned %d...",
-                  cupsd_kqueue_fd, MaxFDs, nfds);
-
   cupsd_kqueue_changes = 0;
 
   for (i = nfds, event = cupsd_kqueue_events; i > 0; i --, event ++)
   {
     fdptr = (_cupsd_fd_t *)event->udata;
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "event->filter=%d, event->ident=%d",
-                    event->filter, (int)event->ident);
+    if (cupsArrayFind(cupsd_inactive_fds, fdptr))
+      continue;
 
     retain_fd(fdptr);
 
     if (fdptr->read_cb && event->filter == EVFILT_READ)
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
-                     fdptr->fd);
       (*(fdptr->read_cb))(fdptr->data);
-    }
 
-    if (fdptr->write_cb && event->filter == EVFILT_WRITE)
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
-                     fdptr->fd);
+    if (fdptr->use > 1 && fdptr->write_cb && event->filter == EVFILT_WRITE &&
+        !cupsArrayFind(cupsd_inactive_fds, fdptr))
       (*(fdptr->write_cb))(fdptr->data);
-    }
 
     release_fd(fdptr);
   }
@@ -522,6 +445,53 @@ cupsdDoSelect(long timeout)                /* I - Timeout in seconds */
   int                  count;          /* Number of file descriptors */
 
 
+#  ifdef HAVE_EPOLL
+  cupsd_in_select = 1;
+
+  if (cupsd_epoll_fd >= 0)
+  {
+    int                        i;              /* Looping var */
+    struct epoll_event *event;         /* Current event */
+
+
+    if (timeout >= 0 && timeout < 86400)
+      nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs,
+                       timeout * 1000);
+    else
+      nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs, -1);
+
+    if (nfds < 0 && errno != EINTR)
+    {
+      close(cupsd_epoll_fd);
+      cupsd_epoll_fd = -1;
+    }
+    else
+    {
+      for (i = nfds, event = cupsd_epoll_events; i > 0; i --, event ++)
+      {
+       fdptr = (_cupsd_fd_t *)event->data.ptr;
+
+       if (cupsArrayFind(cupsd_inactive_fds, fdptr))
+         continue;
+
+       retain_fd(fdptr);
+
+       if (fdptr->read_cb && (event->events & (EPOLLIN | EPOLLERR | EPOLLHUP)))
+         (*(fdptr->read_cb))(fdptr->data);
+
+       if (fdptr->use > 1 && fdptr->write_cb &&
+            (event->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) &&
+            !cupsArrayFind(cupsd_inactive_fds, fdptr))
+         (*(fdptr->write_cb))(fdptr->data);
+
+       release_fd(fdptr);
+      }
+
+      goto release_inactive;
+    }
+  }
+#  endif /* HAVE_EPOLL */
+
   count = cupsArrayCount(cupsd_fds);
 
   if (cupsd_update_pollfds)
@@ -532,8 +502,6 @@ cupsdDoSelect(long timeout)         /* I - Timeout in seconds */
 
     cupsd_update_pollfds = 0;
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Updating pollfd array...");
-
    /*
     * (Re)allocate memory as needed...
     */
@@ -544,15 +512,13 @@ cupsdDoSelect(long timeout)               /* I - Timeout in seconds */
 
 
       if (cupsd_pollfds)
-       pfd = realloc(cupsd_pollfds, allocfds * sizeof(struct pollfd));
+       pfd = realloc(cupsd_pollfds, (size_t)allocfds * sizeof(struct pollfd));
       else
-       pfd = malloc(allocfds * sizeof(struct pollfd));
+       pfd = malloc((size_t)allocfds * sizeof(struct pollfd));
 
       if (!pfd)
       {
-       cupsdLogMessage(CUPSD_LOG_EMERG,
-                       "Unable to allocate %d bytes for polling!",
-                       (int)(allocfds * sizeof(struct pollfd)));
+       cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to allocate %d bytes for polling.", (int)((size_t)allocfds * sizeof(struct pollfd)));
 
        return (-1);
       }
@@ -580,17 +546,10 @@ cupsdDoSelect(long timeout)               /* I - Timeout in seconds */
     }
   }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdDoSelect: polling %d fds for %ld seconds...",
-                 count, timeout);
-
   if (timeout >= 0 && timeout < 86400)
-    nfds = poll(cupsd_pollfds, count, timeout * 1000);
+    nfds = poll(cupsd_pollfds, (nfds_t)count, timeout * 1000);
   else
-    nfds = poll(cupsd_pollfds, count, -1);
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: poll() returned %d...",
-                  nfds);
+    nfds = poll(cupsd_pollfds, (nfds_t)count, -1);
 
   if (nfds > 0)
   {
@@ -600,10 +559,6 @@ cupsdDoSelect(long timeout)                /* I - Timeout in seconds */
 
     for (pfd = cupsd_pollfds; count > 0; pfd ++, count --)
     {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdDoSelect: pollfds[%d]={fd=%d, revents=%x}",
-                     pfd - cupsd_pollfds, pfd->fd, pfd->revents);
-
       if (!pfd->revents)
         continue;
 
@@ -613,18 +568,11 @@ cupsdDoSelect(long timeout)               /* I - Timeout in seconds */
       retain_fd(fdptr);
 
       if (fdptr->read_cb && (pfd->revents & (POLLIN | POLLERR | POLLHUP)))
-      {
-        cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
-                       fdptr->fd);
         (*(fdptr->read_cb))(fdptr->data);
-      }
 
-      if (fdptr->write_cb && (pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
-      {
-        cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
-                       fdptr->fd);
+      if (fdptr->use > 1 && fdptr->write_cb &&
+          (pfd->revents & (POLLOUT | POLLERR | POLLHUP)))
         (*(fdptr->write_cb))(fdptr->data);
-      }
 
       release_fd(fdptr);
     }
@@ -651,10 +599,6 @@ cupsdDoSelect(long timeout)                /* I - Timeout in seconds */
   cupsd_current_input  = cupsd_global_input;
   cupsd_current_output = cupsd_global_output;
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdDoSelect: selecting %d fds for %ld seconds...",
-                 maxfd, timeout);
-
   if (timeout >= 0 && timeout < 86400)
   {
     stimeout.tv_sec  = timeout;
@@ -667,9 +611,6 @@ cupsdDoSelect(long timeout)         /* I - Timeout in seconds */
     nfds = select(maxfd, &cupsd_current_input, &cupsd_current_output, NULL,
                   NULL);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: select() returned %d...",
-                  nfds);
-
   if (nfds > 0)
   {
    /*
@@ -683,24 +624,37 @@ cupsdDoSelect(long timeout)               /* I - Timeout in seconds */
       retain_fd(fdptr);
 
       if (fdptr->read_cb && FD_ISSET(fdptr->fd, &cupsd_current_input))
-      {
-        cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Read on fd %d...",
-                       fdptr->fd);
         (*(fdptr->read_cb))(fdptr->data);
-      }
 
-      if (fdptr->write_cb && FD_ISSET(fdptr->fd, &cupsd_current_output))
-      {
-        cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDoSelect: Write on fd %d...",
-                       fdptr->fd);
+      if (fdptr->use > 1 && fdptr->write_cb &&
+          FD_ISSET(fdptr->fd, &cupsd_current_output))
         (*(fdptr->write_cb))(fdptr->data);
-      }
 
       release_fd(fdptr);
     }
   }
 
-#endif /* HAVE_EPOLL */
+#endif /* HAVE_KQUEUE */
+
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+ /*
+  * Release all inactive file descriptors...
+  */
+
+#  ifndef HAVE_KQUEUE
+  release_inactive:
+#  endif /* !HAVE_KQUEUE */
+
+  cupsd_in_select = 0;
+
+  for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_inactive_fds);
+       fdptr;
+       fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_inactive_fds))
+  {
+    cupsArrayRemove(cupsd_inactive_fds, fdptr);
+    release_fd(fdptr);
+  }
+#endif /* HAVE_EPOLL || HAVE_KQUEUE */
 
  /*
   * Return the number of file descriptors handled...
@@ -710,6 +664,7 @@ cupsdDoSelect(long timeout)         /* I - Timeout in seconds */
 }
 
 
+#ifdef CUPSD_IS_SELECTING
 /*
  * 'cupsdIsSelecting()' - Determine whether we are monitoring a file
  *                        descriptor.
@@ -720,6 +675,7 @@ cupsdIsSelecting(int fd)            /* I - File descriptor */
 {
   return (find_fd(fd) != NULL);
 }
+#endif /* CUPSD_IS_SELECTING */
 
 
 /*
@@ -744,7 +700,7 @@ cupsdRemoveSelect(int fd)           /* I - File descriptor */
   * Range check input...
   */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRemoveSelect: fd=%d", fd);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRemoveSelect(fd=%d)", fd);
 
   if (fd < 0)
     return;
@@ -757,7 +713,12 @@ cupsdRemoveSelect(int fd)          /* I - File descriptor */
     return;
 
 #ifdef HAVE_EPOLL
-  epoll_ctl(cupsd_epoll_fd, EPOLL_CTL_DEL, fd, &event);
+  if (epoll_ctl(cupsd_epoll_fd, EPOLL_CTL_DEL, fd, &event))
+  {
+    close(cupsd_epoll_fd);
+    cupsd_epoll_fd       = -1;
+    cupsd_update_pollfds = 1;
+  }
 
 #elif defined(HAVE_KQUEUE)
   timeout.tv_sec  = 0;
@@ -769,10 +730,9 @@ cupsdRemoveSelect(int fd)          /* I - File descriptor */
 
     if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
     {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                     "cupsdRemoveSelect: kevent() returned %s",
+      cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
                      strerror(errno));
-      return;
+      goto cleanup;
     }
   }
 
@@ -782,14 +742,12 @@ cupsdRemoveSelect(int fd)         /* I - File descriptor */
 
     if (kevent(cupsd_kqueue_fd, &event, 1, NULL, 0, &timeout))
     {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                     "cupsdRemoveSelect: kevent() returned %s",
+      cupsdLogMessage(CUPSD_LOG_EMERG, "kevent() returned %s",
                      strerror(errno));
-      return;
+      goto cleanup;
     }
   }
 
-
 #elif defined(HAVE_POLL)
  /*
   * Update the pollfds array...
@@ -798,20 +756,29 @@ cupsdRemoveSelect(int fd)         /* I - File descriptor */
   cupsd_update_pollfds = 1;
 
 #else /* select() */
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdRemoveSelect: Removing fd %d from input and output "
-                 "sets...", fd);
   FD_CLR(fd, &cupsd_global_input);
   FD_CLR(fd, &cupsd_global_output);
   FD_CLR(fd, &cupsd_current_input);
   FD_CLR(fd, &cupsd_current_output);
 #endif /* HAVE_EPOLL */
 
+#ifdef HAVE_KQUEUE
+  cleanup:
+#endif /* HAVE_KQUEUE */
+
  /*
-  * Remove the file descriptor for from the FD array...
+  * Remove the file descriptor from the active array and add to the
+  * inactive array (or release, if we don't need the inactive array...)
   */
 
   cupsArrayRemove(cupsd_fds, fdptr);
+
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+  if (cupsd_in_select)
+    cupsArrayAdd(cupsd_inactive_fds, fdptr);
+  else
+#endif /* HAVE_EPOLL || HAVE_KQUEUE */
+
   release_fd(fdptr);
 }
 
@@ -823,16 +790,23 @@ cupsdRemoveSelect(int fd)         /* I - File descriptor */
 void
 cupsdStartSelect(void)
 {
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartSelect()");
+
   cupsd_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
 
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+  cupsd_inactive_fds = cupsArrayNew((cups_array_func_t)compare_fds, NULL);
+#endif /* HAVE_EPOLL || HAVE_KQUEUE */
+
 #ifdef HAVE_EPOLL
-  cupsd_epoll_fd     = epoll_create(MaxFDs);
-  cupsd_epoll_events = calloc(MaxFDs, sizeof(struct epoll_event));
+  cupsd_epoll_fd       = epoll_create(MaxFDs);
+  cupsd_epoll_events   = calloc((size_t)MaxFDs, sizeof(struct epoll_event));
+  cupsd_update_pollfds = 0;
 
 #elif defined(HAVE_KQUEUE)
   cupsd_kqueue_fd      = kqueue();
   cupsd_kqueue_changes = 0;
-  cupsd_kqueue_events  = calloc(MaxFDs, sizeof(struct kevent));
+  cupsd_kqueue_events  = calloc((size_t)MaxFDs, sizeof(struct kevent));
 
 #elif defined(HAVE_POLL)
   cupsd_update_pollfds = 0;
@@ -854,6 +828,8 @@ cupsdStopSelect(void)
   _cupsd_fd_t  *fdptr;                 /* Current file descriptor */
 
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStopSelect()");
+
   for (fdptr = (_cupsd_fd_t *)cupsArrayFirst(cupsd_fds);
        fdptr;
        fdptr = (_cupsd_fd_t *)cupsArrayNext(cupsd_fds))
@@ -862,20 +838,12 @@ cupsdStopSelect(void)
   cupsArrayDelete(cupsd_fds);
   cupsd_fds = NULL;
 
-#ifdef HAVE_EPOLL
-  if (cupsd_epoll_events)
-  {
-    free(cupsd_epoll_events);
-    cupsd_epoll_events = NULL;
-  }
-
-  if (cupsd_epoll_fd >= 0)
-  {
-    close(cupsd_epoll_fd);
-    cupsd_epoll_fd = -1;
-  }
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+  cupsArrayDelete(cupsd_inactive_fds);
+  cupsd_inactive_fds = NULL;
+#endif /* HAVE_EPOLL || HAVE_KQUEUE */
 
-#elif defined(HAVE_KQUEUE)
+#ifdef HAVE_KQUEUE
   if (cupsd_kqueue_events)
   {
     free(cupsd_kqueue_events);
@@ -891,6 +859,20 @@ cupsdStopSelect(void)
   cupsd_kqueue_changes = 0;
 
 #elif defined(HAVE_POLL)
+#  ifdef HAVE_EPOLL
+  if (cupsd_epoll_events)
+  {
+    free(cupsd_epoll_events);
+    cupsd_epoll_events = NULL;
+  }
+
+  if (cupsd_epoll_fd >= 0)
+  {
+    close(cupsd_epoll_fd);
+    cupsd_epoll_fd = -1;
+  }
+#  endif /* HAVE_EPOLL */
+
   if (cupsd_pollfds)
   {
     free(cupsd_pollfds);
@@ -939,8 +921,3 @@ find_fd(int fd)                             /* I - File descriptor */
 
   return (fdptr);
 }
-
-
-/*
- * End of "$Id: select.c 6166 2006-12-29 20:35:18Z mike $".
- */