77648316a51718aa09eaabf4e9f3605a6550db92
[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_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
55                 return;
56         }
57         timer->flags |= IOTIMERFLAG_ACTIVE;
58         if(!(timer->flags & IOTIMERFLAG_IN_LIST))
59                 _trigger_timer(timer);
60 }
61
62 void iotimer_set_autoreload(struct IOTimerDescriptor *descriptor, struct timeval *autoreload) {
63         struct _IOTimerDescriptor *timer = descriptor->iotimer;
64         if(timer == NULL) {
65                 iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
66                 return;
67         }
68         if(autoreload) {
69                 timer->flags |= IOTIMERFLAG_PERIODIC;
70                 timer->autoreload = *autoreload;
71                 
72                 if(!(timer->flags & IOTIMERFLAG_IN_LIST)) {
73                         struct timeval now;
74                         gettimeofday(&now, NULL);
75                         timer->timeout = now;
76                         _autoreload_timer(timer);
77                 }
78         } else {
79                 timer->flags &= ~IOTIMERFLAG_PERIODIC;
80         }
81 }
82
83 void iotimer_set_timeout(struct IOTimerDescriptor *descriptor, struct timeval *timeout) {
84         struct _IOTimerDescriptor *timer = descriptor->iotimer;
85         if(timer == NULL) {
86                 iolog_trigger(IOLOG_WARNING, "called iotimer_set_timeout for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
87                 return;
88         }
89         if(!timeout) {
90                 iolog_trigger(IOLOG_WARNING, "called iotimer_set_timeout without timeout given in %s:%d", __FILE__, __LINE__);
91                 return;
92         }
93         timer->timeout = *timeout;
94         _rearrange_timer(timer);
95 }
96
97 void iotimer_set_callback(struct IOTimerDescriptor *descriptor, iotimer_callback *callback) {
98         descriptor->callback = callback;
99 }
100
101 void iotimer_destroy(struct IOTimerDescriptor *descriptor) {
102         struct _IOTimerDescriptor *timer = descriptor->iotimer;
103         if(timer == NULL) {
104                 iolog_trigger(IOLOG_WARNING, "called iotimer_destroy for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
105                 return;
106         }
107         descriptor->iotimer = NULL;
108         _destroy_timer(timer);
109         
110         iogc_add(descriptor);
111 }
112
113 /* internal functions */
114 void _init_timers() {
115         //nothing in here
116 }
117
118 struct _IOTimerDescriptor *_create_timer(struct timeval *timeout) {
119         struct _IOTimerDescriptor *timer = calloc(1, sizeof(*timer));
120         if(!timer) {
121                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
122                 return NULL;
123         }
124         if(timeout) {
125                 timer->timeout = *timeout;
126                 _rearrange_timer(timer);
127         }
128         return timer;
129 }
130
131 static void _rearrange_timer(struct _IOTimerDescriptor *timer) {
132         if((timer->flags & IOTIMERFLAG_IN_LIST)) {
133                 if(timer->prev == NULL)
134                         iotimer_sorted_descriptors = timer->next;
135                 else
136                         timer->prev->next = timer->next;
137                 if(timer->next != NULL)
138                         timer->next->prev = timer->prev;
139         }
140         struct _IOTimerDescriptor *ctimer;
141         for(ctimer = iotimer_sorted_descriptors; ctimer; ctimer = ctimer->next) {
142                 if(timeval_is_bigger(ctimer->timeout, timer->timeout)) {
143                         timer->next = ctimer;
144                         timer->prev = ctimer->prev;
145                         if(ctimer->prev)
146                                 ctimer->prev->next = timer;
147                         else
148                                 iotimer_sorted_descriptors = timer;
149                         ctimer->prev = timer;
150                         break;
151                 }
152                 else if(ctimer->next == NULL) {
153                         ctimer->next = timer;
154                         timer->prev = ctimer;
155                         break;
156                 }
157         }
158         if(ctimer == NULL)
159                 iotimer_sorted_descriptors = timer;
160         timer->flags |= IOTIMERFLAG_IN_LIST;
161 }
162
163 void _destroy_timer(struct _IOTimerDescriptor *timer) {
164         if((timer->flags & IOTIMERFLAG_IN_LIST)) {
165                 if(timer->prev == NULL)
166                         iotimer_sorted_descriptors = timer->next;
167                 else
168                         timer->prev->next = timer->next;
169                 if(timer->next != NULL)
170                         timer->next->prev = timer->prev;
171         }
172         free(timer);
173 }
174
175 static void _autoreload_timer(struct _IOTimerDescriptor *timer) {
176         timer->timeout.tv_usec += timer->autoreload.tv_usec;
177         timer->timeout.tv_sec += timer->autoreload.tv_sec;
178         if(timer->timeout.tv_usec > 1000000) {
179                 timer->timeout.tv_sec += (timer->timeout.tv_usec / 1000000);
180                 timer->timeout.tv_usec %= 1000000;
181         }
182         _rearrange_timer(timer);
183 }
184
185 void _trigger_timer() {
186         struct timeval now;
187         _trigger_timer_start:
188         gettimeofday(&now, NULL);
189         
190         struct _IOTimerDescriptor *timer = iotimer_sorted_descriptors;
191         if(!timer || timeval_is_bigger(timer->timeout, now)) {
192                 return;
193         }
194         iotimer_sorted_descriptors = timer->next;
195         if(timer->next != NULL)
196                 timer->next->prev = timer->prev;
197         timer->flags &= ~IOTIMERFLAG_IN_LIST;
198         
199         if((timer->flags & IOTIMERFLAG_PERIODIC))
200                 _autoreload_timer(timer);
201         
202         if(timer->flags & IOTIMERFLAG_PARENT_PUBLIC) {
203                 struct IOTimerDescriptor *descriptor = timer->parent;
204                 if(descriptor->callback)
205                         descriptor->callback(descriptor);
206                 if(!(timer->flags & IOTIMERFLAG_PERIODIC))
207                         iotimer_destroy(descriptor);
208         } else {
209                 if(!(timer->flags & IOTIMERFLAG_PERIODIC))
210                         _destroy_timer(timer);
211         }
212         
213         goto _trigger_timer_start;
214 }
215