]> git.ozlabs.org Git - ccan-lca-2011.git/blob - ccan/tevent/tevent_timed.c
lca2011: hacky import of tevent.
[ccan-lca-2011.git] / ccan / 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 <ccan/tevent/tevent.h>
28 #include <ccan/tevent/tevent_internal.h>
29 #include <ccan/tevent/tevent_util.h>
30 #include <sys/time.h>
31
32 /**
33   compare two timeval structures. 
34   Return -1 if tv1 < tv2
35   Return 0 if tv1 == tv2
36   Return 1 if tv1 > tv2
37 */
38 int tevent_timeval_compare(const struct timeval *tv1, const struct timeval *tv2)
39 {
40         if (tv1->tv_sec  > tv2->tv_sec)  return 1;
41         if (tv1->tv_sec  < tv2->tv_sec)  return -1;
42         if (tv1->tv_usec > tv2->tv_usec) return 1;
43         if (tv1->tv_usec < tv2->tv_usec) return -1;
44         return 0;
45 }
46
47 /**
48   return a zero timeval
49 */
50 struct timeval tevent_timeval_zero(void)
51 {
52         struct timeval tv;
53         tv.tv_sec = 0;
54         tv.tv_usec = 0;
55         return tv;
56 }
57
58 /**
59   return a timeval for the current time
60 */
61 struct timeval tevent_timeval_current(void)
62 {
63         struct timeval tv;
64         gettimeofday(&tv, NULL);
65         return tv;
66 }
67
68 /**
69   return a timeval struct with the given elements
70 */
71 struct timeval tevent_timeval_set(uint32_t secs, uint32_t usecs)
72 {
73         struct timeval tv;
74         tv.tv_sec = secs;
75         tv.tv_usec = usecs;
76         return tv;
77 }
78
79 /**
80   return the difference between two timevals as a timeval
81   if tv1 comes after tv2, then return a zero timeval
82   (this is *tv2 - *tv1)
83 */
84 struct timeval tevent_timeval_until(const struct timeval *tv1,
85                                     const struct timeval *tv2)
86 {
87         struct timeval t;
88         if (tevent_timeval_compare(tv1, tv2) >= 0) {
89                 return tevent_timeval_zero();
90         }
91         t.tv_sec = tv2->tv_sec - tv1->tv_sec;
92         if (tv1->tv_usec > tv2->tv_usec) {
93                 t.tv_sec--;
94                 t.tv_usec = 1000000 - (tv1->tv_usec - tv2->tv_usec);
95         } else {
96                 t.tv_usec = tv2->tv_usec - tv1->tv_usec;
97         }
98         return t;
99 }
100
101 /**
102   return true if a timeval is zero
103 */
104 bool tevent_timeval_is_zero(const struct timeval *tv)
105 {
106         return tv->tv_sec == 0 && tv->tv_usec == 0;
107 }
108
109 struct timeval tevent_timeval_add(const struct timeval *tv, uint32_t secs,
110                                   uint32_t usecs)
111 {
112         struct timeval tv2 = *tv;
113         tv2.tv_sec += secs;
114         tv2.tv_usec += usecs;
115         tv2.tv_sec += tv2.tv_usec / 1000000;
116         tv2.tv_usec = tv2.tv_usec % 1000000;
117
118         return tv2;
119 }
120
121 /**
122   return a timeval in the future with a specified offset
123 */
124 struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs)
125 {
126         struct timeval tv = tevent_timeval_current();
127         return tevent_timeval_add(&tv, secs, usecs);
128 }
129
130 /*
131   destroy a timed event
132 */
133 static int tevent_common_timed_destructor(struct tevent_timer *te)
134 {
135         tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
136                      "Destroying timer event %p \"%s\"\n",
137                      te, te->handler_name);
138
139         if (te->event_ctx) {
140                 DLIST_REMOVE(te->event_ctx->timer_events, te);
141         }
142
143         return 0;
144 }
145
146 static int tevent_common_timed_deny_destructor(struct tevent_timer *te)
147 {
148         return -1;
149 }
150
151 /*
152   add a timed event
153   return NULL on failure (memory allocation error)
154 */
155 struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
156                                              struct timeval next_event,
157                                              tevent_timer_handler_t handler,
158                                              void *private_data,
159                                              const char *handler_name,
160                                              const char *location)
161 {
162         struct tevent_timer *te, *last_te, *cur_te;
163
164         te = talloc(mem_ctx?mem_ctx:ev, struct tevent_timer);
165         if (te == NULL) return NULL;
166
167         te->event_ctx           = ev;
168         te->next_event          = next_event;
169         te->handler             = handler;
170         te->private_data        = private_data;
171         te->handler_name        = handler_name;
172         te->location            = location;
173         te->additional_data     = NULL;
174
175         /* keep the list ordered */
176         last_te = NULL;
177         for (cur_te = ev->timer_events; cur_te; cur_te = cur_te->next) {
178                 /* if the new event comes before the current one break */
179                 if (tevent_timeval_compare(&te->next_event, &cur_te->next_event) < 0) {
180                         break;
181                 }
182
183                 last_te = cur_te;
184         }
185
186         DLIST_ADD_AFTER(ev->timer_events, te, last_te);
187
188         talloc_set_destructor(te, tevent_common_timed_destructor);
189
190         tevent_debug(ev, TEVENT_DEBUG_TRACE,
191                      "Added timed event \"%s\": %p\n",
192                      handler_name, te);
193         return te;
194 }
195
196 /*
197   do a single event loop using the events defined in ev
198
199   return the delay untill the next timed event,
200   or zero if a timed event was triggered
201 */
202 struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev)
203 {
204         struct timeval current_time = tevent_timeval_zero();
205         struct tevent_timer *te = ev->timer_events;
206
207         if (!te) {
208                 /* have a default tick time of 30 seconds. This guarantees
209                    that code that uses its own timeout checking will be
210                    able to proceeed eventually */
211                 return tevent_timeval_set(30, 0);
212         }
213
214         /*
215          * work out the right timeout for the next timed event
216          *
217          * avoid the syscall to gettimeofday() if the timed event should
218          * be triggered directly
219          *
220          * if there's a delay till the next timed event, we're done
221          * with just returning the delay
222          */
223         if (!tevent_timeval_is_zero(&te->next_event)) {
224                 struct timeval delay;
225
226                 current_time = tevent_timeval_current();
227
228                 delay = tevent_timeval_until(&current_time, &te->next_event);
229                 if (!tevent_timeval_is_zero(&delay)) {
230                         return delay;
231                 }
232         }
233
234         /*
235          * ok, we have a timed event that we'll process ...
236          */
237
238         /* deny the handler to free the event */
239         talloc_set_destructor(te, tevent_common_timed_deny_destructor);
240
241         /* We need to remove the timer from the list before calling the
242          * handler because in a semi-async inner event loop called from the
243          * handler we don't want to come across this event again -- vl */
244         DLIST_REMOVE(ev->timer_events, te);
245
246         /*
247          * If the timed event was registered for a zero current_time,
248          * then we pass a zero timeval here too! To avoid the
249          * overhead of gettimeofday() calls.
250          *
251          * otherwise we pass the current time
252          */
253         te->handler(ev, te, current_time, te->private_data);
254
255         /* The destructor isn't necessary anymore, we've already removed the
256          * event from the list. */
257         talloc_set_destructor(te, NULL);
258
259         tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
260                      "Ending timer event %p \"%s\"\n",
261                      te, te->handler_name);
262
263         talloc_free(te);
264
265         return tevent_timeval_zero();
266 }
267