Merge branch 'master' of ctdb into 'master' of samba
[sfrench/samba-autobuild/.git] / lib / tevent / tevent_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-2009
8
9      ** NOTE! The following LGPL license applies to the tevent
10      ** library. This does NOT imply that all of Samba is released
11      ** under the LGPL
12
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 3 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "replace.h"
28 #include "system/time.h"
29 #include "tevent.h"
30 #include "tevent_internal.h"
31 #include "tevent_util.h"
32
33 /**
34   compare two timeval structures. 
35   Return -1 if tv1 < tv2
36   Return 0 if tv1 == tv2
37   Return 1 if tv1 > tv2
38 */
39 int tevent_timeval_compare(const struct timeval *tv1, const struct timeval *tv2)
40 {
41         if (tv1->tv_sec  > tv2->tv_sec)  return 1;
42         if (tv1->tv_sec  < tv2->tv_sec)  return -1;
43         if (tv1->tv_usec > tv2->tv_usec) return 1;
44         if (tv1->tv_usec < tv2->tv_usec) return -1;
45         return 0;
46 }
47
48 /**
49   return a zero timeval
50 */
51 struct timeval tevent_timeval_zero(void)
52 {
53         struct timeval tv;
54         tv.tv_sec = 0;
55         tv.tv_usec = 0;
56         return tv;
57 }
58
59 /**
60   return a timeval for the current time
61 */
62 struct timeval tevent_timeval_current(void)
63 {
64         struct timeval tv;
65         gettimeofday(&tv, NULL);
66         return tv;
67 }
68
69 /**
70   return a timeval struct with the given elements
71 */
72 struct timeval tevent_timeval_set(uint32_t secs, uint32_t usecs)
73 {
74         struct timeval tv;
75         tv.tv_sec = secs;
76         tv.tv_usec = usecs;
77         return tv;
78 }
79
80 /**
81   return the difference between two timevals as a timeval
82   if tv1 comes after tv2, then return a zero timeval
83   (this is *tv2 - *tv1)
84 */
85 struct timeval tevent_timeval_until(const struct timeval *tv1,
86                                     const struct timeval *tv2)
87 {
88         struct timeval t;
89         if (tevent_timeval_compare(tv1, tv2) >= 0) {
90                 return tevent_timeval_zero();
91         }
92         t.tv_sec = tv2->tv_sec - tv1->tv_sec;
93         if (tv1->tv_usec > tv2->tv_usec) {
94                 t.tv_sec--;
95                 t.tv_usec = 1000000 - (tv1->tv_usec - tv2->tv_usec);
96         } else {
97                 t.tv_usec = tv2->tv_usec - tv1->tv_usec;
98         }
99         return t;
100 }
101
102 /**
103   return true if a timeval is zero
104 */
105 bool tevent_timeval_is_zero(const struct timeval *tv)
106 {
107         return tv->tv_sec == 0 && tv->tv_usec == 0;
108 }
109
110 struct timeval tevent_timeval_add(const struct timeval *tv, uint32_t secs,
111                                   uint32_t usecs)
112 {
113         struct timeval tv2 = *tv;
114         tv2.tv_sec += secs;
115         tv2.tv_usec += usecs;
116         tv2.tv_sec += tv2.tv_usec / 1000000;
117         tv2.tv_usec = tv2.tv_usec % 1000000;
118
119         return tv2;
120 }
121
122 /**
123   return a timeval in the future with a specified offset
124 */
125 struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs)
126 {
127         struct timeval tv = tevent_timeval_current();
128         return tevent_timeval_add(&tv, secs, usecs);
129 }
130
131 /*
132   destroy a timed event
133 */
134 static int tevent_common_timed_destructor(struct tevent_timer *te)
135 {
136         if (te->event_ctx == NULL) {
137                 return 0;
138         }
139
140         tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
141                      "Destroying timer event %p \"%s\"\n",
142                      te, te->handler_name);
143
144         if (te->event_ctx->last_zero_timer == te) {
145                 te->event_ctx->last_zero_timer = DLIST_PREV(te);
146         }
147         DLIST_REMOVE(te->event_ctx->timer_events, te);
148
149         return 0;
150 }
151
152 static int tevent_common_timed_deny_destructor(struct tevent_timer *te)
153 {
154         return -1;
155 }
156
157 /*
158   add a timed event
159   return NULL on failure (memory allocation error)
160 */
161 static struct tevent_timer *tevent_common_add_timer_internal(
162                                         struct tevent_context *ev,
163                                         TALLOC_CTX *mem_ctx,
164                                         struct timeval next_event,
165                                         tevent_timer_handler_t handler,
166                                         void *private_data,
167                                         const char *handler_name,
168                                         const char *location,
169                                         bool optimize_zero)
170 {
171         struct tevent_timer *te, *prev_te, *cur_te;
172
173         te = talloc(mem_ctx?mem_ctx:ev, struct tevent_timer);
174         if (te == NULL) return NULL;
175
176         te->event_ctx           = ev;
177         te->next_event          = next_event;
178         te->handler             = handler;
179         te->private_data        = private_data;
180         te->handler_name        = handler_name;
181         te->location            = location;
182         te->additional_data     = NULL;
183
184         if (ev->timer_events == NULL) {
185                 ev->last_zero_timer = NULL;
186         }
187
188         /* keep the list ordered */
189         prev_te = NULL;
190         if (optimize_zero && tevent_timeval_is_zero(&te->next_event)) {
191                 /*
192                  * Some callers use zero tevent_timer
193                  * instead of tevent_immediate events.
194                  *
195                  * As these can happen very often,
196                  * we remember the last zero timer
197                  * in the list.
198                  */
199                 prev_te = ev->last_zero_timer;
200                 ev->last_zero_timer = te;
201         } else {
202                 /*
203                  * we traverse the list from the tail
204                  * because it's much more likely that
205                  * timers are added at the end of the list
206                  */
207                 for (cur_te = DLIST_TAIL(ev->timer_events);
208                      cur_te != NULL;
209                      cur_te = DLIST_PREV(cur_te))
210                 {
211                         int ret;
212
213                         /*
214                          * if the new event comes before the current
215                          * we continue searching
216                          */
217                         ret = tevent_timeval_compare(&te->next_event,
218                                                      &cur_te->next_event);
219                         if (ret < 0) {
220                                 continue;
221                         }
222
223                         break;
224                 }
225
226                 prev_te = cur_te;
227         }
228
229         DLIST_ADD_AFTER(ev->timer_events, te, prev_te);
230
231         talloc_set_destructor(te, tevent_common_timed_destructor);
232
233         tevent_debug(ev, TEVENT_DEBUG_TRACE,
234                      "Added timed event \"%s\": %p\n",
235                      handler_name, te);
236         return te;
237 }
238
239 struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev,
240                                              TALLOC_CTX *mem_ctx,
241                                              struct timeval next_event,
242                                              tevent_timer_handler_t handler,
243                                              void *private_data,
244                                              const char *handler_name,
245                                              const char *location)
246 {
247         /*
248          * do not use optimization, there are broken Samba
249          * versions which use tevent_common_add_timer()
250          * without using tevent_common_loop_timer_delay(),
251          * it just uses DLIST_REMOVE(ev->timer_events, te)
252          * and would leave ev->last_zero_timer behind.
253          */
254         return tevent_common_add_timer_internal(ev, mem_ctx, next_event,
255                                                 handler, private_data,
256                                                 handler_name, location,
257                                                 false);
258 }
259
260 struct tevent_timer *tevent_common_add_timer_v2(struct tevent_context *ev,
261                                                 TALLOC_CTX *mem_ctx,
262                                                 struct timeval next_event,
263                                                 tevent_timer_handler_t handler,
264                                                 void *private_data,
265                                                 const char *handler_name,
266                                                 const char *location)
267 {
268         /*
269          * Here we turn on last_zero_timer optimization
270          */
271         return tevent_common_add_timer_internal(ev, mem_ctx, next_event,
272                                                 handler, private_data,
273                                                 handler_name, location,
274                                                 true);
275 }
276
277 /*
278   do a single event loop using the events defined in ev
279
280   return the delay until the next timed event,
281   or zero if a timed event was triggered
282 */
283 struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev)
284 {
285         struct timeval current_time = tevent_timeval_zero();
286         struct tevent_timer *te = ev->timer_events;
287
288         if (!te) {
289                 /* have a default tick time of 30 seconds. This guarantees
290                    that code that uses its own timeout checking will be
291                    able to proceed eventually */
292                 return tevent_timeval_set(30, 0);
293         }
294
295         /*
296          * work out the right timeout for the next timed event
297          *
298          * avoid the syscall to gettimeofday() if the timed event should
299          * be triggered directly
300          *
301          * if there's a delay till the next timed event, we're done
302          * with just returning the delay
303          */
304         if (!tevent_timeval_is_zero(&te->next_event)) {
305                 struct timeval delay;
306
307                 current_time = tevent_timeval_current();
308
309                 delay = tevent_timeval_until(&current_time, &te->next_event);
310                 if (!tevent_timeval_is_zero(&delay)) {
311                         return delay;
312                 }
313         }
314
315         /*
316          * ok, we have a timed event that we'll process ...
317          */
318
319         /* deny the handler to free the event */
320         talloc_set_destructor(te, tevent_common_timed_deny_destructor);
321
322         /* We need to remove the timer from the list before calling the
323          * handler because in a semi-async inner event loop called from the
324          * handler we don't want to come across this event again -- vl */
325         if (ev->last_zero_timer == te) {
326                 ev->last_zero_timer = DLIST_PREV(te);
327         }
328         DLIST_REMOVE(ev->timer_events, te);
329
330         tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
331                      "Running timer event %p \"%s\"\n",
332                      te, te->handler_name);
333
334         /*
335          * If the timed event was registered for a zero current_time,
336          * then we pass a zero timeval here too! To avoid the
337          * overhead of gettimeofday() calls.
338          *
339          * otherwise we pass the current time
340          */
341         te->handler(ev, te, current_time, te->private_data);
342
343         /* The destructor isn't necessary anymore, we've already removed the
344          * event from the list. */
345         talloc_set_destructor(te, NULL);
346
347         tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
348                      "Ending timer event %p \"%s\"\n",
349                      te, te->handler_name);
350
351         talloc_free(te);
352
353         return tevent_timeval_zero();
354 }
355