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*/ 39int 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*/ 51struct 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*/ 62struct 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*/ 72struct 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*/ 85struct 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*/ 105bool tevent_timeval_is_zero(const struct timeval *tv) 106{ 107 return tv->tv_sec == 0 && tv->tv_usec == 0; 108} 109 110struct 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*/ 125struct 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*/ 134static int tevent_common_timed_destructor(struct tevent_timer *te) 135{ 136 tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE, 137 "Destroying timer event %p \"%s\"\n", 138 te, te->handler_name); 139 140 if (te->event_ctx) { 141 DLIST_REMOVE(te->event_ctx->timer_events, te); 142 } 143 144 return 0; 145} 146 147static int tevent_common_timed_deny_destructor(struct tevent_timer *te) 148{ 149 return -1; 150} 151 152/* 153 add a timed event 154 return NULL on failure (memory allocation error) 155*/ 156struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_CTX *mem_ctx, 157 struct timeval next_event, 158 tevent_timer_handler_t handler, 159 void *private_data, 160 const char *handler_name, 161 const char *location) 162{ 163 struct tevent_timer *te, *last_te, *cur_te; 164 165 te = talloc(mem_ctx?mem_ctx:ev, struct tevent_timer); 166 if (te == NULL) return NULL; 167 168 te->event_ctx = ev; 169 te->next_event = next_event; 170 te->handler = handler; 171 te->private_data = private_data; 172 te->handler_name = handler_name; 173 te->location = location; 174 te->additional_data = NULL; 175 176 /* keep the list ordered */ 177 last_te = NULL; 178 for (cur_te = ev->timer_events; cur_te; cur_te = cur_te->next) { 179 /* if the new event comes before the current one break */ 180 if (tevent_timeval_compare(&te->next_event, &cur_te->next_event) < 0) { 181 break; 182 } 183 184 last_te = cur_te; 185 } 186 187 DLIST_ADD_AFTER(ev->timer_events, te, last_te); 188 189 talloc_set_destructor(te, tevent_common_timed_destructor); 190 191 tevent_debug(ev, TEVENT_DEBUG_TRACE, 192 "Added timed event \"%s\": %p\n", 193 handler_name, te); 194 return te; 195} 196 197/* 198 do a single event loop using the events defined in ev 199 200 return the delay untill the next timed event, 201 or zero if a timed event was triggered 202*/ 203struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev) 204{ 205 struct timeval current_time = tevent_timeval_zero(); 206 struct tevent_timer *te = ev->timer_events; 207 208 if (!te) { 209 /* have a default tick time of 30 seconds. This guarantees 210 that code that uses its own timeout checking will be 211 able to proceeed eventually */ 212 return tevent_timeval_set(30, 0); 213 } 214 215 /* 216 * work out the right timeout for the next timed event 217 * 218 * avoid the syscall to gettimeofday() if the timed event should 219 * be triggered directly 220 * 221 * if there's a delay till the next timed event, we're done 222 * with just returning the delay 223 */ 224 if (!tevent_timeval_is_zero(&te->next_event)) { 225 struct timeval delay; 226 227 current_time = tevent_timeval_current(); 228 229 delay = tevent_timeval_until(¤t_time, &te->next_event); 230 if (!tevent_timeval_is_zero(&delay)) { 231 return delay; 232 } 233 } 234 235 /* 236 * ok, we have a timed event that we'll process ... 237 */ 238 239 /* deny the handler to free the event */ 240 talloc_set_destructor(te, tevent_common_timed_deny_destructor); 241 242 /* We need to remove the timer from the list before calling the 243 * handler because in a semi-async inner event loop called from the 244 * handler we don't want to come across this event again -- vl */ 245 DLIST_REMOVE(ev->timer_events, te); 246 247 /* 248 * If the timed event was registered for a zero current_time, 249 * then we pass a zero timeval here too! To avoid the 250 * overhead of gettimeofday() calls. 251 * 252 * otherwise we pass the current time 253 */ 254 te->handler(ev, te, current_time, te->private_data); 255 256 /* The destructor isn't necessary anymore, we've already removed the 257 * event from the list. */ 258 talloc_set_destructor(te, NULL); 259 260 tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE, 261 "Ending timer event %p \"%s\"\n", 262 te, te->handler_name); 263 264 talloc_free(te); 265 266 return tevent_timeval_zero(); 267} 268 269