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