[IOMultiplexerV2] added CIOTimer class to c++ interface && added some features to...
[NextIRCd.git] / src / IOHandler / IOTimer.c
1 /* IOTimer.c - IOMultiplexer v2
2  * Copyright (C) 2014  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #define _IOHandler_internals
18 #include "IOInternal.h"
19 #include "IOHandler.h"
20 #include "IOTimer.h"
21 #include "IOLog.h"
22
23 #include <sys/time.h>
24 #include <stdlib.h>
25
26 static void _rearrange_timer(struct _IOTimerDescriptor *timer);
27 static void _autoreload_timer(struct _IOTimerDescriptor *timer);
28
29 struct _IOTimerDescriptor *iotimer_sorted_descriptors;
30
31 /* public functions */
32
33 struct IOTimerDescriptor *iotimer_create(struct timeval *timeout) {
34         struct IOTimerDescriptor *descriptor = calloc(1, sizeof(*descriptor));
35         if(!descriptor) {
36                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
37                 return NULL;
38         }
39         struct _IOTimerDescriptor *timer = _create_timer(timeout);
40         if(!timer) {
41                 free(descriptor);
42                 return NULL;
43         }
44         timer->parent = descriptor;
45         timer->flags |= IOTIMERFLAG_PARENT_PUBLIC;
46         descriptor->iotimer = timer;
47         
48         return descriptor;
49 }
50
51 void iotimer_start(struct IOTimerDescriptor *descriptor) {
52         struct _IOTimerDescriptor *timer = descriptor->iotimer;
53         if(timer == NULL) {
54                 iolog_trigger(IOLOG_WARNING, "called iotimer_start for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
55                 return;
56         }
57         timer->flags |= IOTIMERFLAG_ACTIVE;
58         _rearrange_timer(timer);
59 }
60
61 void iotimer_stop(struct IOTimerDescriptor *descriptor) {
62         struct _IOTimerDescriptor *timer = descriptor->iotimer;
63         if(timer == NULL) {
64                 iolog_trigger(IOLOG_WARNING, "called iotimer_stop for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
65                 return;
66         }
67         timer->flags &= ~IOTIMERFLAG_ACTIVE;
68         if((timer->flags & IOTIMERFLAG_PERSISTENT)) {
69                 timer->flags &= ~IOTIMERFLAG_ACTIVE;
70                 _rearrange_timer(timer);
71         } else
72                 iotimer_destroy(descriptor);
73 }
74
75 int iotimer_state(struct IOTimerDescriptor *descriptor) {
76         struct _IOTimerDescriptor *timer = descriptor->iotimer;
77         if(timer == NULL) {
78                 iolog_trigger(IOLOG_WARNING, "called iotimer_state for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
79                 return 0;
80         }
81         if((timer->flags & IOTIMERFLAG_ACTIVE))
82                 return 1;
83         else
84                 return 0;
85 }
86
87 void iotimer_set_autoreload(struct IOTimerDescriptor *descriptor, struct timeval *autoreload) {
88         struct _IOTimerDescriptor *timer = descriptor->iotimer;
89         if(timer == NULL) {
90                 iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
91                 return;
92         }
93         if(autoreload) {
94                 timer->flags |= IOTIMERFLAG_PERIODIC;
95                 timer->autoreload = *autoreload;
96                 
97                 if(timer->timeout.tv_sec == 0 && timer->timeout.tv_usec == 0) {
98                         struct timeval now;
99                         gettimeofday(&now, NULL);
100                         timer->timeout = now;
101                         _autoreload_timer(timer);
102                 }
103         } else {
104                 timer->flags &= ~IOTIMERFLAG_PERIODIC;
105         }
106 }
107
108 struct timeval iotimer_get_autoreload(struct IOTimerDescriptor *descriptor) {
109         struct _IOTimerDescriptor *timer = descriptor->iotimer;
110         if(timer == NULL) {
111                 iolog_trigger(IOLOG_WARNING, "called iotimer_get_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
112                 struct timeval tout;
113                 tout.tv_sec = 0;
114                 tout.tv_usec = 0;
115                 return tout;
116         }
117         if((timer->flags & IOTIMERFLAG_PERIODIC))
118                 return timer->autoreload;
119         else {
120                 struct timeval tout;
121                 tout.tv_sec = 0;
122                 tout.tv_usec = 0;
123                 return tout;
124         }
125 }
126
127 void iotimer_set_timeout(struct IOTimerDescriptor *descriptor, struct timeval *timeout) {
128         struct _IOTimerDescriptor *timer = descriptor->iotimer;
129         if(timer == NULL) {
130                 iolog_trigger(IOLOG_WARNING, "called iotimer_set_timeout for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
131                 return;
132         }
133         if(!timeout) {
134                 iolog_trigger(IOLOG_WARNING, "called iotimer_set_timeout without timeout given in %s:%d", __FILE__, __LINE__);
135                 return;
136         }
137         timer->timeout = *timeout;
138         _rearrange_timer(timer);
139 }
140
141 struct timeval iotimer_get_timeout(struct IOTimerDescriptor *descriptor) {
142         struct _IOTimerDescriptor *timer = descriptor->iotimer;
143         if(timer == NULL) {
144                 iolog_trigger(IOLOG_WARNING, "called iotimer_get_timeout for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
145                 struct timeval tout;
146                 tout.tv_sec = 0;
147                 tout.tv_usec = 0;
148                 return tout;
149         }
150         return timer->timeout;
151 }
152
153 void iotimer_set_callback(struct IOTimerDescriptor *descriptor, iotimer_callback *callback) {
154         descriptor->callback = callback;
155 }
156
157 void iotimer_set_persistent(struct IOTimerDescriptor *descriptor, int persistent) {
158         struct _IOTimerDescriptor *timer = descriptor->iotimer;
159         if(timer == NULL) {
160                 iolog_trigger(IOLOG_WARNING, "called iotimer_set_persistent for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
161                 return;
162         }
163         if(persistent)
164                 timer->flags |= IOTIMERFLAG_PERSISTENT;
165         else
166                 timer->flags &= ~IOTIMERFLAG_PERSISTENT;
167 }
168
169 void iotimer_destroy(struct IOTimerDescriptor *descriptor) {
170         struct _IOTimerDescriptor *timer = descriptor->iotimer;
171         if(timer == NULL) {
172                 iolog_trigger(IOLOG_WARNING, "called iotimer_destroy for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
173                 return;
174         }
175         descriptor->iotimer = NULL;
176         _destroy_timer(timer);
177         
178         iogc_add(descriptor);
179 }
180
181 /* internal functions */
182 void _init_timers() {
183         //nothing in here
184 }
185
186 struct _IOTimerDescriptor *_create_timer(struct timeval *timeout) {
187         struct _IOTimerDescriptor *timer = calloc(1, sizeof(*timer));
188         if(!timer) {
189                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
190                 return NULL;
191         }
192         if(timeout)
193                 timer->timeout = *timeout;
194         return timer;
195 }
196
197 static void _rearrange_timer(struct _IOTimerDescriptor *timer) {
198         if((timer->flags & IOTIMERFLAG_IN_LIST)) {
199                 if(timer->prev == NULL)
200                         iotimer_sorted_descriptors = timer->next;
201                 else
202                         timer->prev->next = timer->next;
203                 if(timer->next != NULL)
204                         timer->next->prev = timer->prev;
205                 timer->flags &= ~IOTIMERFLAG_IN_LIST;
206         }
207         if(!(timer->flags & IOTIMERFLAG_ACTIVE))
208                 return;
209         struct _IOTimerDescriptor *ctimer;
210         for(ctimer = iotimer_sorted_descriptors; ctimer; ctimer = ctimer->next) {
211                 if(timeval_is_bigger(ctimer->timeout, timer->timeout)) {
212                         timer->next = ctimer;
213                         timer->prev = ctimer->prev;
214                         if(ctimer->prev)
215                                 ctimer->prev->next = timer;
216                         else
217                                 iotimer_sorted_descriptors = timer;
218                         ctimer->prev = timer;
219                         break;
220                 }
221                 else if(ctimer->next == NULL) {
222                         ctimer->next = timer;
223                         timer->prev = ctimer;
224                         break;
225                 }
226         }
227         if(ctimer == NULL)
228                 iotimer_sorted_descriptors = timer;
229         timer->flags |= IOTIMERFLAG_IN_LIST;
230 }
231
232 void _destroy_timer(struct _IOTimerDescriptor *timer) {
233         if((timer->flags & IOTIMERFLAG_IN_LIST)) {
234                 if(timer->prev == NULL)
235                         iotimer_sorted_descriptors = timer->next;
236                 else
237                         timer->prev->next = timer->next;
238                 if(timer->next != NULL)
239                         timer->next->prev = timer->prev;
240         }
241         free(timer);
242 }
243
244 static void _autoreload_timer(struct _IOTimerDescriptor *timer) {
245         timer->timeout.tv_usec += timer->autoreload.tv_usec;
246         timer->timeout.tv_sec += timer->autoreload.tv_sec;
247         if(timer->timeout.tv_usec > 1000000) {
248                 timer->timeout.tv_sec += (timer->timeout.tv_usec / 1000000);
249                 timer->timeout.tv_usec %= 1000000;
250         }
251         _rearrange_timer(timer);
252 }
253
254 void _trigger_timer() {
255         struct timeval now;
256         struct _IOTimerDescriptor *timer;
257         while(iotimer_sorted_descriptors) {
258                 gettimeofday(&now, NULL);
259                 
260                 timer = iotimer_sorted_descriptors;
261                 if(timeval_is_bigger(timer->timeout, now))
262                         break;
263                 
264                 iotimer_sorted_descriptors = timer->next;
265                 if(timer->next != NULL)
266                         timer->next->prev = timer->prev;
267                 timer->flags &= ~IOTIMERFLAG_IN_LIST;
268                 
269                 if((timer->flags & IOTIMERFLAG_PERIODIC))
270                         _autoreload_timer(timer);
271                 
272                 if(timer->flags & IOTIMERFLAG_PARENT_PUBLIC) {
273                         struct IOTimerDescriptor *descriptor = timer->parent;
274                         if(descriptor->callback)
275                                 descriptor->callback(descriptor);
276                         if(!(timer->flags & IOTIMERFLAG_PERIODIC))
277                                 iotimer_destroy(descriptor);
278                 } else {
279                         if(!(timer->flags & IOTIMERFLAG_PERIODIC))
280                                 _destroy_timer(timer);
281                 }
282         }
283 }
284