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(&current_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