[IOMultiplexerV2] alpha
[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_callback(struct IOTimerDescriptor *descriptor, iotimer_callback *callback) {
84         descriptor->callback = callback;
85 }
86
87 void iotimer_destroy(struct IOTimerDescriptor *descriptor) {
88         struct _IOTimerDescriptor *timer = descriptor->iotimer;
89         if(timer == NULL) {
90                 iolog_trigger(IOLOG_WARNING, "called iotimer_destroy for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
91                 return;
92         }
93         descriptor->iotimer = NULL;
94         _destroy_timer(timer);
95         
96         iogc_add(descriptor);
97 }
98
99 /* internal functions */
100 void _init_timers() {
101         //nothing in here
102 }
103
104 struct _IOTimerDescriptor *_create_timer(struct timeval *timeout) {
105         struct _IOTimerDescriptor *timer = calloc(1, sizeof(*timer));
106     if(!timer) {
107         iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
108         return NULL;
109     }
110         if(timeout) {
111                 timer->timeout = *timeout;
112                 _rearrange_timer(timer);
113         }
114         return timer;
115 }
116
117 static void _rearrange_timer(struct _IOTimerDescriptor *timer) {
118         if((timer->flags & IOTIMERFLAG_IN_LIST)) {
119                 if(timer->prev == NULL)
120                         iotimer_sorted_descriptors = timer->next;
121                 else
122                         timer->prev->next = timer->next;
123                 if(timer->next != NULL)
124                         timer->next->prev = timer->prev;
125         }
126         struct _IOTimerDescriptor *ctimer;
127         for(ctimer = iotimer_sorted_descriptors; ctimer; ctimer = ctimer->next) {
128                 if(timeval_is_bigger(ctimer->timeout, timer->timeout)) {
129                         timer->next = ctimer;
130                         timer->prev = ctimer->prev;
131                         if(ctimer->prev)
132                                 ctimer->prev->next = timer;
133                         else
134                                 iotimer_sorted_descriptors = timer;
135                         ctimer->prev = timer;
136                         break;
137                 }
138                 else if(ctimer->next == NULL) {
139                         ctimer->next = timer;
140                         timer->prev = ctimer;
141                         break;
142                 }
143         }
144         if(ctimer == NULL)
145                 iotimer_sorted_descriptors = timer;
146         timer->flags |= IOTIMERFLAG_IN_LIST;
147 }
148
149 void _destroy_timer(struct _IOTimerDescriptor *timer) {
150         if((timer->flags & IOTIMERFLAG_IN_LIST)) {
151                 if(timer->prev == NULL)
152                         iotimer_sorted_descriptors = timer->next;
153                 else
154                         timer->prev->next = timer->next;
155                 if(timer->next != NULL)
156                         timer->next->prev = timer->prev;
157         }
158         free(timer);
159 }
160
161 static void _autoreload_timer(struct _IOTimerDescriptor *timer) {
162         timer->timeout.tv_usec += timer->autoreload.tv_usec;
163         timer->timeout.tv_sec += timer->autoreload.tv_sec;
164         if(timer->timeout.tv_usec > 1000000) {
165                 timer->timeout.tv_sec += (timer->timeout.tv_usec / 1000000);
166                 timer->timeout.tv_usec %= 1000000;
167         }
168         _rearrange_timer(timer);
169 }
170
171 void _trigger_timer() {
172         struct timeval now;
173         _trigger_timer_start:
174         gettimeofday(&now, NULL);
175         
176         struct _IOTimerDescriptor *timer = iotimer_sorted_descriptors;
177         if(!timer || timeval_is_bigger(timer->timeout, now)) {
178                 return;
179         }
180         iotimer_sorted_descriptors = timer->next;
181         if(timer->next != NULL)
182                 timer->next->prev = timer->prev;
183         timer->flags &= ~IOTIMERFLAG_IN_LIST;
184         
185         if((timer->flags & IOTIMERFLAG_PERIODIC))
186                 _autoreload_timer(timer);
187         
188         if(timer->flags & IOTIMERFLAG_PARENT_PUBLIC) {
189                 struct IOTimerDescriptor *descriptor = timer->parent;
190                 if(descriptor->callback)
191                         descriptor->callback(descriptor);
192                 if(!(timer->flags & IOTIMERFLAG_PERIODIC))
193                         iotimer_destroy(descriptor);
194         } else {
195                 if(!(timer->flags & IOTIMERFLAG_PERIODIC))
196                         _destroy_timer(timer);
197         }
198         
199         goto _trigger_timer_start;
200 }
201