05ddb47fb4031aff06ffb1c83278583e9ecff588
[tinc] / event.c
1 /*
2     event.c -- I/O, timeout, and event handling
3     Copyright (C) 2012-2022 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #include "event.h"
23
24 struct timeval now;
25
26 static int io_compare(const io_t *a, const io_t *b) {
27 #ifndef HAVE_WINDOWS
28         return a->fd - b->fd;
29 #else
30
31         if(a->event < b->event) {
32                 return -1;
33         }
34
35         if(a->event > b->event) {
36                 return 1;
37         }
38
39         return 0;
40 #endif
41 }
42
43 static int timeout_compare(const timeout_t *a, const timeout_t *b) {
44         struct timeval diff;
45         timersub(&a->tv, &b->tv, &diff);
46
47         if(diff.tv_sec < 0) {
48                 return -1;
49         }
50
51         if(diff.tv_sec > 0) {
52                 return 1;
53         }
54
55         if(diff.tv_usec < 0) {
56                 return -1;
57         }
58
59         if(diff.tv_usec > 0) {
60                 return 1;
61         }
62
63         uintptr_t ap = (uintptr_t)a;
64         uintptr_t bp = (uintptr_t)b;
65
66         if(ap < bp) {
67                 return -1;
68         }
69
70         if(ap > bp) {
71                 return 1;
72         }
73
74         return 0;
75 }
76
77 splay_tree_t io_tree = {.compare = (splay_compare_t)io_compare};
78 splay_tree_t timeout_tree = {.compare = (splay_compare_t)timeout_compare};
79
80 void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, const struct timeval *tv) {
81         timeout->cb = cb;
82         timeout->data = data;
83         timeout->node.data = timeout;
84
85         timeout_set(timeout, tv);
86 }
87
88 void timeout_set(timeout_t *timeout, const struct timeval *tv) {
89         if(timerisset(&timeout->tv)) {
90                 splay_unlink_node(&timeout_tree, &timeout->node);
91         }
92
93         if(!now.tv_sec) {
94                 gettimeofday(&now, NULL);
95         }
96
97         timeradd(&now, tv, &timeout->tv);
98
99         if(!splay_insert_node(&timeout_tree, &timeout->node)) {
100                 abort();
101         }
102 }
103
104 void timeout_del(timeout_t *timeout) {
105         if(!timeout->cb) {
106                 return;
107         }
108
109         splay_unlink_node(&timeout_tree, &timeout->node);
110         timeout->cb = 0;
111         timeout->tv = (struct timeval) {
112                 0, 0
113         };
114 }
115
116 struct timeval *timeout_execute(struct timeval *diff) {
117         gettimeofday(&now, NULL);
118         struct timeval *tv = NULL;
119
120         while(timeout_tree.head) {
121                 timeout_t *timeout = timeout_tree.head->data;
122                 timersub(&timeout->tv, &now, diff);
123
124                 if(diff->tv_sec < 0) {
125                         timeout->cb(timeout->data);
126
127                         if(timercmp(&timeout->tv, &now, <)) {
128                                 timeout_del(timeout);
129                         }
130                 } else {
131                         tv = diff;
132                         break;
133                 }
134         }
135
136         return tv;
137 }