Merge branch 'master' of ssh://jra@git.samba.org/data/git/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
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 "tevent.h"
29 #include "tevent_internal.h"
30 #include "tevent_util.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 static int ev_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 static struct timeval ev_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 static struct timeval ev_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 static struct timeval ev_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 static struct timeval ev_timeval_until(const struct timeval *tv1,
85                                         const struct timeval *tv2)
86 {
87         struct timeval t;
88         if (ev_timeval_compare(tv1, tv2) >= 0) {
89                 return ev_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 ev_timeval_is_zero(const struct timeval *tv)
105 {
106         return tv->tv_sec == 0 && tv->tv_usec == 0;
107 }
108
109 /*
110   destroy a timed event
111 */
112 static int common_event_timed_destructor(struct timed_event *te)
113 {
114         struct event_context *ev = talloc_get_type(te->event_ctx,
115                                                    struct event_context);
116         DLIST_REMOVE(ev->timed_events, te);
117         return 0;
118 }
119
120 static int common_event_timed_deny_destructor(struct timed_event *te)
121 {
122         return -1;
123 }
124
125 /*
126   add a timed event
127   return NULL on failure (memory allocation error)
128 */
129 struct timed_event *common_event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ctx,
130                                            struct timeval next_event, 
131                                            event_timed_handler_t handler, 
132                                            void *private_data) 
133 {
134         struct timed_event *te, *last_te, *cur_te;
135
136         te = talloc(mem_ctx?mem_ctx:ev, struct timed_event);
137         if (te == NULL) return NULL;
138
139         te->event_ctx           = ev;
140         te->next_event          = next_event;
141         te->handler             = handler;
142         te->private_data        = private_data;
143         te->additional_data     = NULL;
144
145         /* keep the list ordered */
146         last_te = NULL;
147         for (cur_te = ev->timed_events; cur_te; cur_te = cur_te->next) {
148                 /* if the new event comes before the current one break */
149                 if (ev_timeval_compare(&te->next_event, &cur_te->next_event) < 0) {
150                         break;
151                 }
152
153                 last_te = cur_te;
154         }
155
156         DLIST_ADD_AFTER(ev->timed_events, te, last_te);
157
158         talloc_set_destructor(te, common_event_timed_destructor);
159
160         return te;
161 }
162
163 /*
164   do a single event loop using the events defined in ev
165
166   return the delay untill the next timed event,
167   or zero if a timed event was triggered
168 */
169 struct timeval common_event_loop_timer_delay(struct event_context *ev)
170 {
171         struct timeval current_time = ev_timeval_zero();
172         struct timed_event *te = ev->timed_events;
173
174         if (!te) {
175                 /* have a default tick time of 30 seconds. This guarantees
176                    that code that uses its own timeout checking will be
177                    able to proceeed eventually */
178                 return ev_timeval_set(30, 0);
179         }
180
181         /*
182          * work out the right timeout for the next timed event
183          *
184          * avoid the syscall to gettimeofday() if the timed event should
185          * be triggered directly
186          *
187          * if there's a delay till the next timed event, we're done
188          * with just returning the delay
189          */
190         if (!ev_timeval_is_zero(&te->next_event)) {
191                 struct timeval delay;
192
193                 current_time = ev_timeval_current();
194
195                 delay = ev_timeval_until(&current_time, &te->next_event);
196                 if (!ev_timeval_is_zero(&delay)) {
197                         return delay;
198                 }
199         }
200
201         /*
202          * ok, we have a timed event that we'll process ...
203          */
204
205         /* deny the handler to free the event */
206         talloc_set_destructor(te, common_event_timed_deny_destructor);
207
208         /* We need to remove the timer from the list before calling the
209          * handler because in a semi-async inner event loop called from the
210          * handler we don't want to come across this event again -- vl */
211         DLIST_REMOVE(ev->timed_events, te);
212
213         /*
214          * If the timed event was registered for a zero current_time,
215          * then we pass a zero timeval here too! To avoid the
216          * overhead of gettimeofday() calls.
217          *
218          * otherwise we pass the current time
219          */
220         te->handler(ev, te, current_time, te->private_data);
221
222         /* The destructor isn't necessary anymore, we've already removed the
223          * event from the list. */
224         talloc_set_destructor(te, NULL);
225
226         talloc_free(te);
227
228         return ev_timeval_zero();
229 }
230