c81825bf92dc4286b6bc2b17379004387ed29a1f
[metze/samba/wip.git] / source / lib / events / events_timed.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    common events code for timed events
5
6    Copyright (C) Andrew Tridgell        2003-2006
7    Copyright (C) Stefan Metzmacher      2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include <sys/time.h>
24 #include <time.h>
25 #include "replace.h"
26 #include "system/filesys.h"
27 #include "system/select.h"
28 #include "events.h"
29 #include "events_internal.h"
30
31 /**
32   compare two timeval structures. 
33   Return -1 if tv1 < tv2
34   Return 0 if tv1 == tv2
35   Return 1 if tv1 > tv2
36 */
37 static int ev_timeval_compare(const struct timeval *tv1, const struct timeval *tv2)
38 {
39         if (tv1->tv_sec  > tv2->tv_sec)  return 1;
40         if (tv1->tv_sec  < tv2->tv_sec)  return -1;
41         if (tv1->tv_usec > tv2->tv_usec) return 1;
42         if (tv1->tv_usec < tv2->tv_usec) return -1;
43         return 0;
44 }
45
46 /**
47   return a zero timeval
48 */
49 static struct timeval ev_timeval_zero(void)
50 {
51         struct timeval tv;
52         tv.tv_sec = 0;
53         tv.tv_usec = 0;
54         return tv;
55 }
56
57 /**
58   return a timeval for the current time
59 */
60 static struct timeval ev_timeval_current(void)
61 {
62         struct timeval tv;
63         gettimeofday(&tv, NULL);
64         return tv;
65 }
66
67 /**
68   return a timeval struct with the given elements
69 */
70 static struct timeval ev_timeval_set(uint32_t secs, uint32_t usecs)
71 {
72         struct timeval tv;
73         tv.tv_sec = secs;
74         tv.tv_usec = usecs;
75         return tv;
76 }
77
78 /**
79   return the difference between two timevals as a timeval
80   if tv1 comes after tv2, then return a zero timeval
81   (this is *tv2 - *tv1)
82 */
83 static struct timeval ev_timeval_until(const struct timeval *tv1,
84                                         const struct timeval *tv2)
85 {
86         struct timeval t;
87         if (ev_timeval_compare(tv1, tv2) >= 0) {
88                 return ev_timeval_zero();
89         }
90         t.tv_sec = tv2->tv_sec - tv1->tv_sec;
91         if (tv1->tv_usec > tv2->tv_usec) {
92                 t.tv_sec--;
93                 t.tv_usec = 1000000 - (tv1->tv_usec - tv2->tv_usec);
94         } else {
95                 t.tv_usec = tv2->tv_usec - tv1->tv_usec;
96         }
97         return t;
98 }
99
100 /**
101   return true if a timeval is zero
102 */
103 bool ev_timeval_is_zero(const struct timeval *tv)
104 {
105         return tv->tv_sec == 0 && tv->tv_usec == 0;
106 }
107
108 /*
109   destroy a timed event
110 */
111 static int common_event_timed_destructor(struct timed_event *te)
112 {
113         struct event_context *ev = talloc_get_type(te->event_ctx,
114                                                    struct event_context);
115         DLIST_REMOVE(ev->timed_events, te);
116         return 0;
117 }
118
119 static int common_event_timed_deny_destructor(struct timed_event *te)
120 {
121         return -1;
122 }
123
124 /*
125   add a timed event
126   return NULL on failure (memory allocation error)
127 */
128 struct timed_event *common_event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ctx,
129                                            struct timeval next_event, 
130                                            event_timed_handler_t handler, 
131                                            void *private_data) 
132 {
133         struct timed_event *te, *last_te, *cur_te;
134
135         te = talloc(mem_ctx?mem_ctx:ev, struct timed_event);
136         if (te == NULL) return NULL;
137
138         te->event_ctx           = ev;
139         te->next_event          = next_event;
140         te->handler             = handler;
141         te->private_data        = private_data;
142         te->additional_data     = NULL;
143
144         /* keep the list ordered */
145         last_te = NULL;
146         for (cur_te = ev->timed_events; cur_te; cur_te = cur_te->next) {
147                 /* if the new event comes before the current one break */
148                 if (ev_timeval_compare(&te->next_event, &cur_te->next_event) < 0) {
149                         break;
150                 }
151
152                 last_te = cur_te;
153         }
154
155         DLIST_ADD_AFTER(ev->timed_events, te, last_te);
156
157         talloc_set_destructor(te, common_event_timed_destructor);
158
159         return te;
160 }
161
162 /*
163   do a single event loop using the events defined in ev
164
165   return the delay untill the next timed event,
166   or zero if a timed event was triggered
167 */
168 struct timeval common_event_loop_timer_delay(struct event_context *ev)
169 {
170         struct timeval current_time = ev_timeval_zero();
171         struct timed_event *te = ev->timed_events;
172
173         if (!te) {
174                 /* have a default tick time of 30 seconds. This guarantees
175                    that code that uses its own timeout checking will be
176                    able to proceeed eventually */
177                 return ev_timeval_set(30, 0);
178         }
179
180         /*
181          * work out the right timeout for the next timed event
182          *
183          * avoid the syscall to gettimeofday() if the timed event should
184          * be triggered directly
185          *
186          * if there's a delay till the next timed event, we're done
187          * with just returning the delay
188          */
189         if (!ev_timeval_is_zero(&te->next_event)) {
190                 struct timeval delay;
191
192                 current_time = ev_timeval_current();
193
194                 delay = ev_timeval_until(&current_time, &te->next_event);
195                 if (!ev_timeval_is_zero(&delay)) {
196                         return delay;
197                 }
198         }
199
200         /*
201          * ok, we have a timed event that we'll process ...
202          */
203
204         /* deny the handler to free the event */
205         talloc_set_destructor(te, common_event_timed_deny_destructor);
206
207         /* We need to remove the timer from the list before calling the
208          * handler because in a semi-async inner event loop called from the
209          * handler we don't want to come across this event again -- vl */
210         DLIST_REMOVE(ev->timed_events, te);
211
212         /*
213          * If the timed event was registered for a zero current_time,
214          * then we pass a zero timeval here too! To avoid the
215          * overhead of gettimeofday() calls.
216          *
217          * otherwise we pass the current time
218          */
219         te->handler(ev, te, current_time, te->private_data);
220
221         /* The destructor isn't necessary anymore, we've already removed the
222          * event from the list. */
223         talloc_set_destructor(te, NULL);
224
225         talloc_free(te);
226
227         return ev_timeval_zero();
228 }
229