1 /*****************************************************************************
2  *  Written by Chris Dunlap <cdunlap@llnl.gov>.
3  *  Copyright (C) 2007-2018 Lawrence Livermore National Security, LLC.
4  *  Copyright (C) 2001-2007 The Regents of the University of California.
5  *  UCRL-CODE-2002-009.
6  *
7  *  This file is part of ConMan: The Console Manager.
8  *  For details, see <https://dun.github.io/conman/>.
9  *
10  *  ConMan is free software: you can redistribute it and/or modify it under
11  *  the terms of the GNU General Public License as published by the Free
12  *  Software Foundation, either version 3 of the License, or (at your option)
13  *  any later version.
14  *
15  *  ConMan is distributed in the hope that it will be useful, but WITHOUT
16  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18  *  for more details.
19  *
20  *  You should have received a copy of the GNU General Public License along
21  *  with ConMan.  If not, see <http://www.gnu.org/licenses/>.
22  *****************************************************************************/
23 
24 
25 #if HAVE_CONFIG_H
26 #  include <config.h>
27 #endif /* HAVE_CONFIG_H */
28 
29 #include <assert.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <poll.h>
33 #include <pthread.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/resource.h>
37 #include <sys/time.h>
38 #include <unistd.h>
39 #include "bool.h"
40 #include "log.h"
41 #include "tpoll.h"
42 
43 
44 /*****************************************************************************
45  *  Notes
46  *****************************************************************************
47  *  Based on ideas from:
48  *  - David R. Butenhof's "Programming with POSIX Threads" (Section 3.3.4)
49  *  - Jon C. Snader's "Effective TCP/IP Programming" (Tip #20)
50  *
51  *  This implementation is thread-safe.
52  *
53  *  This implementation assumes the set of file descriptors being polled is
54  *  densely populated up through the maximum file descriptor of interest; as
55  *  such, the fd_array[] is indexed by the file descriptor.  If this assumption
56  *  is not the case, performance can be increased by adding new file
57  *  descriptors to the first empty slot in fd_array[], and maintaining a hash
58  *  to map file descriptors onto the corresponding fd_array[] index.
59  *
60  *  This implementation assumes the number of concurrent active timers is
61  *  moderate; as such, active timers are stored in a linked-list in order of
62  *  increasing timevals (ie, the head of the list (timers_active) is the next
63  *  timer to expire).  It does not scale well to a large number of timers
64  *  because insertion and deletion are O(n), although dispatching is O(1).
65  *  Other possible implementations are heaps [Sedgewick 1998] which are
66  *  O(log n) for insertion, deletion, and dispatch; or hashed timing wheels
67  *  [Varghese and Lauck 1996] which can be as efficient as O(1) for insertion,
68  *  deletion, and dispatch.
69  */
70 
71 
72 /*****************************************************************************
73  *  Constants
74  *****************************************************************************/
75 
76 #define TPOLL_ALLOC     256
77 
78 
79 /*****************************************************************************
80  *  Internal Data Types
81  *****************************************************************************/
82 
83 typedef struct tpoll_timer * _tpoll_timer_t;
84 
85 struct tpoll {
86     struct pollfd   *fd_array;          /* poll fd array                     */
87     int              fd_pipe[ 2 ];      /* signal pipe for unblocking poll() */
88     int              num_fds_alloc;     /* num pollfd structs allocated      */
89     int              num_fds_used;      /* num pollfd structs in use         */
90     int              max_fd;            /* max fd in array in use            */
91     _tpoll_timer_t   timers_active;     /* sorted list of active timers      */
92     int              timers_next_id;    /* next id to be assigned to a timer */
93     pthread_mutex_t  mutex;             /* locking primitive                 */
94     bool             is_blocked;        /* flag set when blocking on poll()  */
95     bool             is_realloced;      /* flag set after fd_array[] realloc */
96     bool             is_signaled;       /* flag set when fd_pipe is signaled */
97     bool             is_mutex_inited;   /* flag set when mutex initialized   */
98 };
99 
100 struct tpoll_timer {
101     int              id;                /* timer ID                          */
102     callback_f       fnc;               /* callback function                 */
103     void            *arg;               /* callback function arg             */
104     struct timeval   tv;                /* expiration time                   */
105     _tpoll_timer_t   next;              /* next timer in list                */
106 };
107 
108 
109 /*****************************************************************************
110  *  Internal Prototypes
111  *****************************************************************************/
112 
113 static void _tpoll_init (tpoll_t tp, tpoll_zero_t how);
114 
115 static void _tpoll_signal_send (tpoll_t tp);
116 
117 static void _tpoll_signal_recv (tpoll_t tp);
118 
119 static int _tpoll_grow (tpoll_t tp, int num_fds_req);
120 
121 static void _tpoll_get_timeval (struct timeval *tvp, int ms);
122 
123 static int _tpoll_diff_timeval (struct timeval *tvp1, struct timeval *tvp0);
124 
125 
126 /*****************************************************************************
127  *  Functions
128  *****************************************************************************/
129 
130 tpoll_t
tpoll_create(int n)131 tpoll_create (int n)
132 {
133 /*  Creates a new tpoll object for multiplexing timers as well as I/O over at
134  *    least [n] file descriptors.  If [n] is 0, the default size will be used.
135  *  Returns an opaque pointer to this new object, or NULL on error.
136  */
137     tpoll_t tp = NULL;
138     int     i;
139     int     fval;
140     int     e;
141 
142     assert (TPOLL_ALLOC > 0);
143 
144     if (n <= 0) {
145         n = TPOLL_ALLOC;
146     }
147     if (!(tp = malloc (sizeof (struct tpoll)))) {
148         goto err;
149     }
150     tp->fd_pipe[ 0 ] = tp->fd_pipe[ 1 ] = -1;
151     tp->timers_active = NULL;
152     tp->is_blocked = false;
153     tp->is_realloced = false;
154     tp->is_signaled = false;
155     tp->is_mutex_inited = false;
156 
157     if (!(tp->fd_array = malloc (n * sizeof (struct pollfd)))) {
158         goto err;
159     }
160     tp->num_fds_alloc = n;
161 
162     if (pipe (tp->fd_pipe) < 0) {
163         goto err;
164     }
165     for (i = 0; i < 2; i++) {
166         if ((fval = fcntl (tp->fd_pipe[ i ], F_GETFL, 0)) < 0) {
167             goto err;
168         }
169         if (fcntl (tp->fd_pipe[ i ], F_SETFL, fval | O_NONBLOCK) < 0) {
170             goto err;
171         }
172         if (fcntl (tp->fd_pipe[ i ], F_SETFD, FD_CLOEXEC) < 0) {
173             goto err;
174         }
175     }
176     if ((e = pthread_mutex_init (&tp->mutex, NULL)) != 0) {
177         errno = e;
178         goto err;
179     }
180     tp->is_mutex_inited = true;
181 
182     /*  The mutex is not locked here before calling _tpoll_init() because the
183      *    object handle (tp) has not yet been returned.
184      */
185     _tpoll_init (tp, TPOLL_ZERO_ALL);
186     return (tp);
187 
188 err:
189     tpoll_destroy (tp);
190     return (NULL);
191 }
192 
193 
194 void
tpoll_destroy(tpoll_t tp)195 tpoll_destroy (tpoll_t tp)
196 {
197 /*  Destroys the tpoll object [tp] and cancels all of its associated timers.
198  */
199     int            i;
200     _tpoll_timer_t t;
201     int            e;
202 
203     if (!tp) {
204         return;
205     }
206     if (tp->is_mutex_inited) {
207         if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
208             log_err (errno = e, "Unable to lock tpoll mutex");
209         }
210     }
211     if (tp->fd_array) {
212         free (tp->fd_array);
213         tp->fd_array = NULL;
214     }
215     for (i = 0; i < 2; i++) {
216         if (tp->fd_pipe[ i ] > -1) {
217             (void) close (tp->fd_pipe[ i ]);
218             tp->fd_pipe[ i ] = -1;
219         }
220     }
221     while (tp->timers_active) {
222         t = tp->timers_active;
223         tp->timers_active = t->next;
224         free (t);
225     }
226     if (tp->is_mutex_inited) {
227         if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
228             log_err (errno = e, "Unable to unlock tpoll mutex");
229         }
230         if ((e = pthread_mutex_destroy (&tp->mutex)) != 0) {
231             log_err (errno = e, "Unable to destroy tpoll mutex");
232         }
233         tp->is_mutex_inited = false;
234     }
235     free (tp);
236     return;
237 }
238 
239 
240 int
tpoll_zero(tpoll_t tp,tpoll_zero_t how)241 tpoll_zero (tpoll_t tp, tpoll_zero_t how)
242 {
243 /*  Re-initializes the tpoll object [tp].
244  *    If [how] is TPOLL_ZERO_ALL, everything is reset.
245  *    If [how] is TPOLL_ZERO_FDS, only the file descriptor events are reset.
246  *    If [how] is TPOLL_ZERO_TIMERS, only the timers are canceled.
247  *    If [how] is anything else, no action is taken.
248  *  Returns 0 on success, or -1 on error.
249  */
250     int e;
251 
252     if (!tp) {
253         errno = EINVAL;
254         return (-1);
255     }
256     if (how & ~TPOLL_ZERO_ALL) {
257         errno = EINVAL;
258         return (-1);
259     }
260     if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
261         log_err (errno = e, "Unable to lock tpoll mutex");
262     }
263     _tpoll_init (tp, how);
264     _tpoll_signal_send (tp);
265 
266     DPRINTF((21, "tpoll_zero how=%d.\n", how));
267     if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
268         log_err (errno = e, "Unable to unlock tpoll mutex");
269     }
270     return (0);
271 }
272 
273 
274 int
tpoll_clear(tpoll_t tp,int fd,short int events)275 tpoll_clear (tpoll_t tp, int fd, short int events)
276 {
277 /*  Removes the bitwise-OR'd [events] from any existing events for file
278  *    descriptor [fd] within the tpoll object [tp].
279  *  Returns 0 on success, or -1 on error.
280  */
281     short int events_new = 0;
282     int       i;
283     int       e;
284 
285     if (!tp) {
286         errno = EINVAL;
287         return (-1);
288     }
289     if (fd < 0) {
290         errno = EINVAL;
291         return (-1);
292     }
293     if (events == 0) {
294         return (0);
295     }
296     if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
297         log_err (errno = e, "Unable to lock tpoll mutex");
298     }
299     if ((fd <= tp->max_fd) && (tp->fd_array[ fd ].fd > -1)) {
300 
301         assert (tp->fd_array[ fd ].fd == fd);
302         events_new = tp->fd_array[ fd ].events & ~events;
303         if (tp->fd_array[ fd ].events != events_new) {
304 
305             tp->fd_array[ fd ].events = events_new;
306 
307             if (events_new == 0) {
308                 tp->fd_array[ fd ].revents = 0;
309                 tp->fd_array[ fd ].fd = -1;
310                 tp->num_fds_used--;
311 
312                 if (tp->max_fd == fd) {
313                     for (i = fd - 1; i >= 0; i--) {
314                         if (tp->fd_array[ i ].fd > -1) {
315                             break;
316                         }
317                     }
318                     tp->max_fd = i;
319                 }
320             }
321             _tpoll_signal_send (tp);
322         }
323     }
324     DPRINTF((21, "tpoll_clear fd=%d e=0x%02x r=0x%02x.\n",
325         fd, events, events_new));
326     if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
327         log_err (errno = e, "Unable to unlock tpoll mutex");
328     }
329     return (0);
330 }
331 
332 
333 int
tpoll_is_set(tpoll_t tp,int fd,short int events)334 tpoll_is_set (tpoll_t tp, int fd, short int events)
335 {
336 /*  Tests whether any of the bitwise-OR'd [events] have occurred for file
337  *    descriptor [fd] within the tpoll object [tp].
338  *  Returns >0  if any of the specified [events] have occurred,
339  *    0 if none of the specified [events] have occurred, or -1 on error.
340  */
341     int rc;
342     int e;
343 
344     if (!tp) {
345         errno = EINVAL;
346         return (-1);
347     }
348     if (fd < 0) {
349         errno = EINVAL;
350         return (-1);
351     }
352     if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
353         log_err (errno = e, "Unable to lock tpoll mutex");
354     }
355     if (fd > tp->max_fd) {
356         rc = 0;
357     }
358     else if (tp->fd_array[ fd ].fd < 0) {
359         rc = 0;
360     }
361     else {
362         assert (tp->fd_array[ fd ].fd == fd);
363         rc = tp->fd_array[ fd ].revents & events;
364     }
365     DPRINTF((21, "tpoll_is_set fd=%d e=0x%02x r=0x%02x rc=%d.\n",
366         fd, events, tp->fd_array[ fd ].revents, rc));
367     if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
368         log_err (errno = e, "Unable to unlock tpoll mutex");
369     }
370     return (rc);
371 }
372 
373 
374 int
tpoll_set(tpoll_t tp,int fd,short int events)375 tpoll_set (tpoll_t tp, int fd, short int events)
376 {
377 /*  Adds the bitwise-OR'd [events] to any existing events for file descriptor
378  *    [fd] within the tpoll object [tp].
379  *  The internal fd table will grow as needed.
380  *  Returns 0 on success, or -1 on error.
381  */
382     int       rc;
383     short int events_new = 0;
384     int       e;
385 
386     if (!tp) {
387         errno = EINVAL;
388         return (-1);
389     }
390     if (fd < 0) {
391         errno = EINVAL;
392         return (-1);
393     }
394     if (events == 0) {
395         return (0);
396     }
397     if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
398         log_err (errno = e, "Unable to lock tpoll mutex");
399     }
400     if ((fd >= tp->num_fds_alloc) && (_tpoll_grow (tp, fd + 1) < 0)) {
401         rc = -1;
402     }
403     else {
404         if (tp->fd_array[ fd ].fd < 0) {
405             assert (tp->fd_array[ fd ].events == 0);
406             assert (tp->fd_array[ fd ].revents == 0);
407             tp->fd_array[ fd ].fd = fd;
408             tp->num_fds_used++;
409             if (fd > tp->max_fd) {
410                 tp->max_fd = fd;
411             }
412             events_new = events;
413         }
414         else {
415             events_new = tp->fd_array[ fd ].events | events;
416         }
417         if (tp->fd_array[ fd ].events != events_new) {
418             tp->fd_array[ fd ].events = events_new;
419             _tpoll_signal_send (tp);
420         }
421         rc = 0;
422     }
423     DPRINTF((21, "tpoll_set fd=%d e=0x%02x r=0x%02x.\n",
424         fd, events, events_new));
425     if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
426         log_err (errno = e, "Unable to unlock tpoll mutex");
427     }
428     return (rc);
429 }
430 
431 
432 int
tpoll_timeout_absolute(tpoll_t tp,callback_f cb,void * arg,const struct timeval * tvp)433 tpoll_timeout_absolute (tpoll_t tp, callback_f cb, void *arg,
434     const struct timeval *tvp)
435 {
436 /*  Sets an "absolute" timer event for the tpoll object [tp] specifying when
437  *    the timer should expire.  At expiration time [tvp], the callback
438  *    function [cb] will be invoked with the argument [arg].
439  *  Returns a timer ID > 0 for use with tpoll_timeout_cancel(), or -1 on error.
440  */
441     _tpoll_timer_t  t;
442     _tpoll_timer_t *t_ptr;
443     int             rc;
444     int             e;
445 
446     if (!tp) {
447         errno = EINVAL;
448         return (-1);
449     }
450     if (!cb) {
451         errno = EINVAL;
452         return (-1);
453     }
454     if (!tvp) {
455         errno = EINVAL;
456         return (-1);
457     }
458     if (!(t = malloc (sizeof (struct tpoll_timer)))) {
459         return (-1);
460     }
461     t->fnc = cb;
462     t->arg = arg;
463     t->tv = *tvp;
464 
465     if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
466         log_err (errno = e, "Unable to lock tpoll mutex");
467     }
468     rc = t->id = tp->timers_next_id++;
469     if (tp->timers_next_id <= 0) {
470         tp->timers_next_id = 1;
471     }
472     t_ptr = &tp->timers_active;
473     while (*t_ptr && !timercmp (tvp, &(*t_ptr)->tv, <)) {
474         t_ptr = &((*t_ptr)->next);
475     }
476     if (*t_ptr == tp->timers_active) {
477         _tpoll_signal_send (tp);
478     }
479     t->next = *t_ptr;
480     *t_ptr = t;
481 
482     DPRINTF((22, "tpoll timer set id=%d.\n", t->id));
483     if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
484         log_err (errno = e, "Unable to unlock tpoll mutex");
485     }
486     return (rc);
487 }
488 
489 
490 int
tpoll_timeout_relative(tpoll_t tp,callback_f cb,void * arg,int ms)491 tpoll_timeout_relative (tpoll_t tp, callback_f cb, void *arg, int ms)
492 {
493 /*  Sets a "relative" timer event for the tpoll object [tp] specifying the
494  *    duration (in milliseconds [ms]) before it expires.  At expiration, the
495  *    callback function [cb] will be invoked with the argument [arg].
496  *  Returns a timer ID > 0 for use with tpoll_timeout_cancel(), or -1 on error.
497  */
498     struct timeval tv;
499 
500     _tpoll_get_timeval (&tv, ms);
501     return (tpoll_timeout_absolute (tp, cb, arg, &tv));
502 }
503 
504 
505 int
tpoll_timeout_cancel(tpoll_t tp,int id)506 tpoll_timeout_cancel (tpoll_t tp, int id)
507 {
508 /*  Cancels the timer event [id] from the tpoll object [tp].
509  *  Returns 1 if the timer was canceled, 0 if the timer was not found,
510  *    or -1 on error.
511  */
512     _tpoll_timer_t  t;
513     _tpoll_timer_t *t_ptr;
514     int             rc;
515     int             e;
516 
517     if (!tp) {
518         errno = EINVAL;
519         return (-1);
520     }
521     if (id <= 0) {
522         errno = EINVAL;
523         return (-1);
524     }
525     if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
526         log_err (errno = e, "Unable to lock tpoll mutex");
527     }
528     t_ptr = &tp->timers_active;
529     while (*t_ptr && (id != (*t_ptr)->id)) {
530         t_ptr = &((*t_ptr)->next);
531     }
532     if (!*t_ptr) {
533         rc = 0;
534     }
535     else {
536         DPRINTF((22, "tpoll timer cancel id=%d.\n", (*t_ptr)->id));
537         if (*t_ptr == tp->timers_active) {
538             _tpoll_signal_send (tp);
539         }
540         t = *t_ptr;
541         *t_ptr = t->next;
542         free (t);
543         rc = 1;
544     }
545     if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
546         log_err (errno = e, "Unable to unlock tpoll mutex");
547     }
548     return (rc);
549 }
550 
551 
552 int
tpoll(tpoll_t tp,int ms)553 tpoll (tpoll_t tp, int ms)
554 {
555 /*  Similar to poll(), but file descriptors and timers are specified by
556  *    auxiliary functions.
557  *  Examines the tpoll object [tp] to see if any file descriptors are ready
558  *    for I/O, and dispatches any timer events that have expired.
559  *  Blocks until I/O is ready on one or more file descriptors, or [ms]
560  *    milliseconds have passed.  Blocks indefinitely if I/O events are
561  *    specified and [ms] is -1.  While blocked, timers are still dispatched
562  *    once they expire.
563  *  Returns immediately if the [ms] timeout is 0, or if no I/O events are
564  *    specified and no timers remain and [ms] is -1.
565  *  Returns the number of file descriptors with I/O ready, 0 on timeout,
566  *    or -1 or error.
567  */
568     struct timeval  tv_timeout;
569     struct timeval  tv_now;
570     _tpoll_timer_t  t;
571     int             timeout;
572     int             ms_diff;
573     int             n;
574     int             e;
575 
576     if (!tp) {
577         errno = EINVAL;
578         return (-1);
579     }
580     if (ms > 0) {
581         _tpoll_get_timeval (&tv_timeout, ms);
582     }
583     if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
584         log_err (errno = e, "Unable to lock tpoll mutex");
585     }
586     DPRINTF((23, "tpoll enter ms=%d nfd=%d mfd=%d.\n",
587         ms, tp->num_fds_used, tp->max_fd));
588     _tpoll_get_timeval (&tv_now, 0);
589 
590     for (;;) {
591         /*
592          *  Dispatch timer events that have expired.
593          */
594         while (tp->timers_active
595                 && !timercmp (&tp->timers_active->tv, &tv_now, >)) {
596 
597             t = tp->timers_active;
598             tp->timers_active = t->next;
599             DPRINTF((22, "tpoll timer dispatch id=%d.\n", t->id));
600             /*
601              *  Release the mutex while performing the callback function
602              *    in case the callback wants to set/cancel another timer.
603              */
604             if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
605                 log_err (errno = e, "Unable to unlock tpoll mutex");
606             }
607             t->fnc (t->arg);
608             free (t);
609 
610             if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
611                 log_err (errno = e, "Unable to lock tpoll mutex");
612             }
613         }
614         /*  Compute timeout for poll().
615          */
616         if (ms == 0) {
617             timeout = 0;
618         }
619         else if ((ms < 0) && !tp->timers_active) {
620             if (tp->num_fds_used > 0) {
621                 timeout = -1;           /* fd events but no more timers */
622             }
623             else {
624                 timeout = 0;            /* no fd events and no more timers */
625             }
626         }
627         else {
628             _tpoll_get_timeval (&tv_now, 0);
629 
630             if (ms < 0) {
631                 assert (tp->timers_active != NULL);
632                 ms_diff =
633                     _tpoll_diff_timeval (&tp->timers_active->tv, &tv_now);
634             }
635             else if (!tp->timers_active) {
636                 assert (ms > 0);
637                 ms_diff =
638                     _tpoll_diff_timeval (&tv_timeout, &tv_now);
639             }
640             else if (!timercmp (&tp->timers_active->tv, &tv_timeout, >)) {
641                 assert (ms > 0);
642                 ms_diff =
643                     _tpoll_diff_timeval (&tp->timers_active->tv, &tv_now);
644             }
645             else {
646                 assert (ms > 0);
647                 ms_diff =
648                     _tpoll_diff_timeval (&tv_timeout, &tv_now);
649             }
650             timeout = (ms_diff > 0) ? ms_diff : 0;
651         }
652         /*  Poll for events, discarding any on the "signaling pipe".
653          */
654         tp->is_blocked = true;
655 
656         if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
657             log_err (errno = e, "Unable to unlock tpoll mutex");
658         }
659         DPRINTF((25, "tpoll poll enter ms=%d mfd=%d.\n", timeout, tp->max_fd));
660         n = poll (tp->fd_array, tp->max_fd + 1, timeout);
661         DPRINTF((25, "tpoll poll return n=%d.\n", n));
662 
663         if ((e = pthread_mutex_lock (&tp->mutex)) != 0) {
664             log_err (errno = e, "Unable to lock tpoll mutex");
665         }
666         tp->is_blocked = false;
667 
668         if (n < 0) {
669             break;
670         }
671         if (tp->is_realloced) {
672             DPRINTF((25, "tpoll is_realloced.\n"));
673             tp->is_realloced = false;
674             _tpoll_signal_recv (tp);
675             continue;
676         }
677         if (tp->fd_array[ tp->fd_pipe[ 0 ] ].revents & POLLIN) {
678             _tpoll_signal_recv (tp);
679             n--;
680         }
681         if (n > 0) {
682             assert (tp->num_fds_used > 0);
683             break;
684         }
685         if ((ms == 0)
686                 || ((ms < 0) && !tp->num_fds_used && !tp->timers_active)) {
687             break;
688         }
689         _tpoll_get_timeval (&tv_now, 0);
690         if ((ms > 0) && !timercmp (&tv_timeout, &tv_now, >)) {
691             break;
692         }
693     }
694     DPRINTF((23, "tpoll return n=%d.\n", n));
695     if ((e = pthread_mutex_unlock (&tp->mutex)) != 0) {
696         log_err (errno = e, "Unable to unlock tpoll mutex");
697     }
698     return (n);
699 }
700 
701 
702 /*****************************************************************************
703  *  Internal Functions
704  *****************************************************************************/
705 
706 static void
_tpoll_init(tpoll_t tp,tpoll_zero_t how)707 _tpoll_init (tpoll_t tp, tpoll_zero_t how)
708 {
709 /*  Initializes the tpoll object [tp] to the empty set.
710  *  This routine assumes the [tp] mutex is already locked.
711  */
712     int            i;
713     _tpoll_timer_t t;
714 
715     assert (tp != NULL);
716     assert (tp->fd_pipe[ 0 ] > -1);
717     assert (tp->num_fds_alloc > 0);
718     assert ((how & ~TPOLL_ZERO_ALL) == 0);
719 
720     if (how & TPOLL_ZERO_FDS) {
721         memset (tp->fd_array, 0, tp->num_fds_alloc * sizeof (struct pollfd));
722         for (i = 0; i < tp->num_fds_alloc; i++) {
723             tp->fd_array[ i ].fd = -1;
724         }
725         tp->fd_array[ tp->fd_pipe[ 0 ] ].fd = tp->fd_pipe[ 0 ];
726         tp->fd_array[ tp->fd_pipe[ 0 ] ].events = POLLIN;
727         tp->max_fd = tp->fd_pipe[ 0 ];
728         tp->num_fds_used = 0;
729     }
730     if (how & TPOLL_ZERO_TIMERS) {
731         while (tp->timers_active) {
732             t = tp->timers_active;
733             tp->timers_active = t->next;
734             free (t);
735         }
736         tp->timers_next_id = 1;
737     }
738     return;
739 }
740 
741 
742 static void
_tpoll_signal_send(tpoll_t tp)743 _tpoll_signal_send (tpoll_t tp)
744 {
745 /*  Signals the tpoll object [tp] that an fd or timer or somesuch has changed
746  *    and poll() needs to unblock and re-examine its state.
747  *  This routine assumes the [tp] mutex is already locked.
748  */
749     int           n;
750     unsigned char c = 0;
751 
752     assert (tp != NULL);
753     assert (tp->fd_pipe[ 1 ] > -1);
754 
755     if (tp->is_signaled || !tp->is_blocked) {
756         return;
757     }
758     for (;;) {
759         n = write (tp->fd_pipe[ 1 ], &c, 1);
760         if (n < 0) {
761             if (errno == EINTR) {
762                 continue;
763             }
764             if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
765                 break;
766             }
767             log_err (errno, "Unable to write signal to tpoll");
768         }
769         else if (n == 0) {
770             log_err (0, "Got an unexpected 0 writing to tpoll's pipe");
771         }
772         break;
773     }
774     tp->is_signaled = true;
775     DPRINTF((24, "tpoll signal sent.\n"));
776     return;
777 }
778 
779 
780 static void
_tpoll_signal_recv(tpoll_t tp)781 _tpoll_signal_recv (tpoll_t tp)
782 {
783 /*  Drains all signals sent to the tpoll object [tp].
784  *  This routine assumes the [tp] mutex is already locked.
785  */
786     int           n;
787     unsigned char c[ 2 ];
788 
789     assert (tp != NULL);
790     assert (tp->fd_pipe[ 0 ] > -1);
791     assert (tp->fd_array[ tp->fd_pipe[ 0 ] ].fd == tp->fd_pipe[ 0 ]);
792 
793     if (!tp->is_signaled) {
794         return;
795     }
796     for (;;) {
797         n = read (tp->fd_pipe[ 0 ], &c, sizeof (c));
798         if (n < 0) {
799             if (errno == EINTR) {
800                 continue;
801             }
802             if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
803                 break;
804             }
805             log_err (errno, "Unable to read signal from tpoll");
806         }
807         else if (n == 0) {
808             log_err (0, "Got an unexpected EOF reading from tpoll's pipe");
809         }
810         else if (n == sizeof (c)) {
811             assert (0);                 /* is_signaled should prevent this */
812             continue;
813         }
814         break;
815     }
816     tp->is_signaled = false;
817     DPRINTF((24, "tpoll signal received.\n"));
818     return;
819 }
820 
821 
822 static int
_tpoll_grow(tpoll_t tp,int num_fds_req)823 _tpoll_grow (tpoll_t tp, int num_fds_req)
824 {
825 /*  Attempts to grow [tp]'s pollfd array to at least [num_fds_req] structs.
826  *  Returns 0 if the request is successful, -1 if not.
827  *  This routine assumes the [tp] mutex is already locked.
828  */
829     struct pollfd *fd_array_tmp;
830     struct pollfd *fd_array_new;
831     int            num_fds_tmp;
832     int            num_fds_new;
833     int            i;
834 
835     assert (tp != NULL);
836     assert (num_fds_req > 0);
837 
838     if (num_fds_req <= tp->num_fds_alloc) {
839         return (0);
840     }
841     num_fds_tmp = tp->num_fds_alloc;
842     while ((num_fds_tmp < num_fds_req) && (num_fds_tmp > 0)) {
843         num_fds_tmp *= 2;
844     }
845     if (num_fds_tmp < num_fds_req) {
846         num_fds_tmp = num_fds_req;
847     }
848     /*  Force tpoll()'s poll() to unblock before we realloc the fd_array.
849      *  Then tpoll() will have to re-acquire the mutex before continuing.
850      *  Since we currently have the mutex, we can now safely realloc fd_array.
851      */
852     _tpoll_signal_send (tp);
853     if (!(fd_array_tmp =
854             realloc (tp->fd_array, num_fds_tmp * sizeof (struct pollfd)))) {
855         return (-1);
856     }
857     fd_array_new = fd_array_tmp + tp->num_fds_alloc;
858     num_fds_new = num_fds_tmp - tp->num_fds_alloc;
859     memset (fd_array_new, 0, num_fds_new * sizeof (struct pollfd));
860     for (i = tp->num_fds_alloc; i < num_fds_tmp; i++) {
861         fd_array_tmp[ i ].fd = -1;
862     }
863     tp->is_realloced = true;
864     tp->fd_array = fd_array_tmp;
865     tp->num_fds_alloc = num_fds_tmp;
866     return (0);
867 }
868 
869 
870 static void
_tpoll_get_timeval(struct timeval * tvp,int ms)871 _tpoll_get_timeval (struct timeval *tvp, int ms)
872 {
873 /*  Sets [tvp] to the current time.
874  *  If [ms] > 0, adds the number of milliseconds [ms] to [tvp].
875  */
876     assert (tvp != NULL);
877 
878     if (gettimeofday (tvp, NULL) < 0) {
879         log_err (0, "Unable to get time of day");
880     }
881     if (ms > 0) {
882         tvp->tv_sec += ms / 1000;
883         tvp->tv_usec += (ms % 1000) * 1000;
884         if (tvp->tv_usec >= 1000000) {
885             tvp->tv_sec += tvp->tv_usec / 1000000;
886             tvp->tv_usec %= 1000000;
887         }
888     }
889     return;
890 }
891 
892 
893 static int
_tpoll_diff_timeval(struct timeval * tvp1,struct timeval * tvp0)894 _tpoll_diff_timeval (struct timeval *tvp1, struct timeval *tvp0)
895 {
896 /*  Returns the millisecond difference between [tvp1] and [tvp0].
897  *  If either is NULL, the current time will be used in its place.
898  */
899     struct timeval tv;
900     int            ms;
901 
902     if (!tvp0 || !tvp1) {
903         if (gettimeofday (&tv, NULL) < 0) {
904             log_err (0, "Unable to get time of day");
905         }
906         if (!tvp0) {
907             tvp0 = &tv;
908         }
909         if (!tvp1) {
910             tvp1 = &tv;
911         }
912     }
913     ms = ( (tvp1->tv_sec  - tvp0->tv_sec)  * 1000 ) +
914          ( (tvp1->tv_usec - tvp0->tv_usec) / 1000 ) ;
915     /*
916      *  Round to the next millisecond.
917      */
918     if ((tvp1->tv_sec >= tvp0->tv_sec)
919             && (tvp1->tv_usec > tvp0->tv_usec)) {
920         ms++;
921     }
922     else if ((tvp1->tv_sec <= tvp0->tv_sec)
923             && (tvp1->tv_usec < tvp0->tv_usec)) {
924         ms--;
925     }
926     return (ms);
927 }
928