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