1 /*
2  * Copyright (C) 2010 Red Hat, Inc.
3  *
4  * Author: Angus Salkeld <asalkeld@redhat.com>
5  *
6  * This file is part of libqb.
7  *
8  * libqb is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * libqb is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 #include "os_base.h"
22 #include <pthread.h>
23 
24 #include <qb/qbdefs.h>
25 #include <qb/qblist.h>
26 #include <qb/qbarray.h>
27 #include <qb/qbloop.h>
28 #include "loop_int.h"
29 #include "util_int.h"
30 #include "tlist.h"
31 
32 struct qb_loop_timer {
33 	struct qb_loop_item item;
34 	qb_loop_timer_dispatch_fn dispatch_fn;
35 	enum qb_loop_priority p;
36 	timer_handle timerlist_handle;
37 	enum qb_poll_entry_state state;
38 	int32_t check;
39 	uint32_t install_pos;
40 };
41 
42 struct qb_timer_source {
43 	struct qb_loop_source s;
44 	struct timerlist timerlist;
45 	qb_array_t *timers;
46 	size_t timer_entry_count;
47 	pthread_mutex_t lock;
48 };
49 
50 static void
timer_dispatch(struct qb_loop_item * item,enum qb_loop_priority p)51 timer_dispatch(struct qb_loop_item *item, enum qb_loop_priority p)
52 {
53 	struct qb_loop_timer *timer = (struct qb_loop_timer *)item;
54 
55 	assert(timer->state == QB_POLL_ENTRY_JOBLIST);
56 	timer->check = 0;
57 	timer->dispatch_fn(timer->item.user_data);
58 	timer->state = QB_POLL_ENTRY_EMPTY;
59 }
60 
61 static int32_t expired_timers;
62 static void
make_job_from_tmo(void * data)63 make_job_from_tmo(void *data)
64 {
65 	struct qb_loop_timer *t = (struct qb_loop_timer *)data;
66 	struct qb_loop *l = t->item.source->l;
67 
68 	assert(t->state == QB_POLL_ENTRY_ACTIVE);
69 	qb_loop_level_item_add(&l->level[t->p], &t->item);
70 	t->state = QB_POLL_ENTRY_JOBLIST;
71 	expired_timers++;
72 }
73 
74 static int32_t
expire_the_timers(struct qb_loop_source * s,int32_t ms_timeout)75 expire_the_timers(struct qb_loop_source *s, int32_t ms_timeout)
76 {
77 	struct qb_timer_source *ts = (struct qb_timer_source *)s;
78 	expired_timers = 0;
79 	timerlist_expire(&ts->timerlist);
80 	return expired_timers;
81 }
82 
83 int32_t
qb_loop_timer_msec_duration_to_expire(struct qb_loop_source * timer_source)84 qb_loop_timer_msec_duration_to_expire(struct qb_loop_source * timer_source)
85 {
86 	struct qb_timer_source *my_src = (struct qb_timer_source *)timer_source;
87 	uint64_t left = timerlist_msec_duration_to_expire(&my_src->timerlist);
88 	if (left != -1 && left > 0xFFFFFFFF) {
89 		left = 0xFFFFFFFE;
90 	}
91 	return left;
92 }
93 
94 struct qb_loop_source *
qb_loop_timer_create(struct qb_loop * l)95 qb_loop_timer_create(struct qb_loop *l)
96 {
97 	struct qb_timer_source *my_src = malloc(sizeof(struct qb_timer_source));
98 	if (my_src == NULL) {
99 		return NULL;
100 	}
101 	my_src->s.l = l;
102 	my_src->s.dispatch_and_take_back = timer_dispatch;
103 	my_src->s.poll = expire_the_timers;
104 
105 	timerlist_init(&my_src->timerlist);
106 	my_src->timers = qb_array_create_2(16, sizeof(struct qb_loop_timer), 16);
107 	my_src->timer_entry_count = 0;
108 	pthread_mutex_init(&my_src->lock, NULL);
109 
110 	return (struct qb_loop_source *)my_src;
111 }
112 
113 void
qb_loop_timer_destroy(struct qb_loop * l)114 qb_loop_timer_destroy(struct qb_loop *l)
115 {
116 	struct qb_timer_source *my_src =
117 	    (struct qb_timer_source *)l->timer_source;
118 	qb_array_free(my_src->timers);
119 	free(l->timer_source);
120 }
121 
122 static int32_t
_timer_from_handle_(struct qb_timer_source * s,qb_loop_timer_handle handle_in,struct qb_loop_timer ** timer_pt)123 _timer_from_handle_(struct qb_timer_source *s,
124 		    qb_loop_timer_handle handle_in,
125 		    struct qb_loop_timer **timer_pt)
126 {
127 	int32_t rc;
128 	int32_t check;
129 	uint32_t install_pos;
130 	struct qb_loop_timer *timer;
131 
132 	if (handle_in == 0) {
133 		return -EINVAL;
134 	}
135 
136 	check = handle_in >> 32;
137 	install_pos = handle_in & UINT32_MAX;
138 
139 	rc = qb_array_index(s->timers, install_pos, (void **)&timer);
140 	if (rc != 0) {
141 		return rc;
142 	}
143 	if (timer->check != check) {
144 		return -EINVAL;
145 	}
146 	*timer_pt = timer;
147 	return 0;
148 }
149 
150 static int32_t
_get_empty_array_position_(struct qb_timer_source * s)151 _get_empty_array_position_(struct qb_timer_source *s)
152 {
153 	int32_t install_pos;
154 	int32_t res = 0;
155 	struct qb_loop_timer *timer;
156 
157 	for (install_pos = 0; install_pos < s->timer_entry_count; install_pos++) {
158 		assert(qb_array_index(s->timers, install_pos, (void **)&timer)
159 		       == 0);
160 		if (timer->state == QB_POLL_ENTRY_EMPTY) {
161 			return install_pos;
162 		}
163 	}
164 
165 	res = qb_array_grow(s->timers, s->timer_entry_count + 1);
166 	if (res != 0) {
167 		return res;
168 	}
169 
170 	s->timer_entry_count++;
171 	install_pos = s->timer_entry_count - 1;
172 	return install_pos;
173 }
174 
175 int32_t
qb_loop_timer_add(struct qb_loop * lp,enum qb_loop_priority p,uint64_t nsec_duration,void * data,qb_loop_timer_dispatch_fn timer_fn,qb_loop_timer_handle * timer_handle_out)176 qb_loop_timer_add(struct qb_loop * lp,
177 		  enum qb_loop_priority p,
178 		  uint64_t nsec_duration,
179 		  void *data,
180 		  qb_loop_timer_dispatch_fn timer_fn,
181 		  qb_loop_timer_handle * timer_handle_out)
182 {
183 	struct qb_loop_timer *t;
184 	struct qb_timer_source *my_src;
185 	int32_t i;
186 	struct qb_loop *l = lp;
187 
188 	if (l == NULL) {
189 		l = qb_loop_default_get();
190 	}
191 
192 	if (l == NULL || timer_fn == NULL) {
193 		return -EINVAL;
194 	}
195 	my_src = (struct qb_timer_source *)l->timer_source;
196 
197 	if (pthread_mutex_lock(&my_src->lock)) {
198 		return -errno;
199 	}
200 	i = _get_empty_array_position_(my_src);
201 	assert(qb_array_index(my_src->timers, i, (void **)&t) >= 0);
202 	t->state = QB_POLL_ENTRY_ACTIVE;
203 	t->install_pos = i;
204 	t->item.user_data = data;
205 	t->item.source = (struct qb_loop_source *)my_src;
206 	t->dispatch_fn = timer_fn;
207 	t->p = p;
208 	qb_list_init(&t->item.list);
209 
210 	/* Unlock here to stop anyone else changing the state while we're initializing */
211 	pthread_mutex_unlock(&my_src->lock);
212 
213 	/*
214 	 * Make sure just positive integers are used for the integrity(?)
215 	 * checks within 2^32 address space, if we miss 200 times in a row
216 	 * (just 0 is concerned per specification of random), the PRNG may be
217 	 * broken -> the value is unspecified, subject of previous assignment.
218 	 */
219 	for (i = 0; i < 200; i++) {
220 		t->check = random();
221 
222 		if (t->check > 0) {
223 			break;  /* covers also t->check == UINT32_MAX */
224 		}
225 	}
226 
227 	if (timer_handle_out) {
228 		*timer_handle_out = (((uint64_t) (t->check)) << 32) | t->install_pos;
229 	}
230 	return timerlist_add_duration(&my_src->timerlist,
231 				      make_job_from_tmo, t,
232 				      nsec_duration, &t->timerlist_handle);
233 }
234 
235 int32_t
qb_loop_timer_del(struct qb_loop * lp,qb_loop_timer_handle th)236 qb_loop_timer_del(struct qb_loop * lp, qb_loop_timer_handle th)
237 {
238 	struct qb_timer_source *s;
239 	struct qb_loop_timer *t;
240 	int32_t res;
241 	struct qb_loop *l = lp;
242 
243 	if (l == NULL) {
244 		l = qb_loop_default_get();
245 	}
246 	s = (struct qb_timer_source *)l->timer_source;
247 
248 	res = _timer_from_handle_(s, th, &t);
249 	if (res != 0) {
250 		return res;
251 	}
252 
253 	if (t->state == QB_POLL_ENTRY_DELETED) {
254 		qb_util_log(LOG_WARNING, "timer already deleted");
255 		return 0;
256 	}
257 	if (t->state != QB_POLL_ENTRY_ACTIVE &&
258 	    t->state != QB_POLL_ENTRY_JOBLIST) {
259 		return -EINVAL;
260 	}
261 	if (t->state == QB_POLL_ENTRY_JOBLIST) {
262 		qb_loop_level_item_del(&l->level[t->p], &t->item);
263 	}
264 
265 	if (t->timerlist_handle) {
266 		timerlist_del(&s->timerlist, t->timerlist_handle);
267 	}
268 	t->state = QB_POLL_ENTRY_EMPTY;
269 	return 0;
270 }
271 
272 uint64_t
qb_loop_timer_expire_time_get(struct qb_loop * lp,qb_loop_timer_handle th)273 qb_loop_timer_expire_time_get(struct qb_loop * lp, qb_loop_timer_handle th)
274 {
275 	struct qb_timer_source *s;
276 	struct qb_loop_timer *t;
277 	int32_t res;
278 	struct qb_loop *l = lp;
279 
280 	if (l == NULL) {
281 		l = qb_loop_default_get();
282 	}
283 	s = (struct qb_timer_source *)l->timer_source;
284 
285 	res = _timer_from_handle_(s, th, &t);
286 	if (res != 0) {
287 		return 0;
288 	}
289 
290 	if (t->state != QB_POLL_ENTRY_ACTIVE) {
291 		return 0;
292 	}
293 
294 	return timerlist_expire_time(&s->timerlist, t->timerlist_handle);
295 }
296 
297 uint64_t
qb_loop_timer_expire_time_remaining(struct qb_loop * lp,qb_loop_timer_handle th)298 qb_loop_timer_expire_time_remaining(struct qb_loop * lp, qb_loop_timer_handle th)
299 {
300 
301 	uint64_t current_ns;
302 	/* NOTE: while it does not appear that absolute timers are used anywhere,
303 	 * we may as well respect this pattern in case that changes.
304 	 * Unfortunately, that means we do need to repeat timer fetch code from qb_loop_timer_expire_time_get
305 	 * rather than just a simple call to qb_loop_timer_expire_time_get and qb_util_nano_current_get.
306 	 */
307 
308 	struct qb_timer_source *s;
309 	struct qb_loop_timer *t;
310 	int32_t res;
311 	struct qb_loop *l = lp;
312 
313 	if (l == NULL) {
314 		l = qb_loop_default_get();
315 	}
316 	s = (struct qb_timer_source *)l->timer_source;
317 
318 	res = _timer_from_handle_(s, th, &t);
319 	if (res != 0) {
320 		return 0;
321 	}
322 
323 	struct timerlist_timer *timer = (struct timerlist_timer *)t->timerlist_handle;
324 
325 
326 	if (timer->is_absolute_timer) {
327 		current_ns = qb_util_nano_from_epoch_get();
328 	}
329 	else {
330 		current_ns = qb_util_nano_current_get();
331 	}
332 	uint64_t timer_ns = timerlist_expire_time(&s->timerlist, t->timerlist_handle);
333 	/* since time estimation is racy by nature, I'll try to check the state late,
334 	 * and try to understand that no matter what, the timer might have expired in the mean time
335 	 */
336 	if (t->state != QB_POLL_ENTRY_ACTIVE) {
337 		return 0;
338 	}
339 	if (timer_ns < current_ns) {
340 		return 0; // respect the "expired" contract
341 	}
342 	return timer_ns - current_ns;
343 
344 
345 }
346 
347 int32_t
qb_loop_timer_is_running(qb_loop_t * l,qb_loop_timer_handle th)348 qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th)
349 {
350 	return (qb_loop_timer_expire_time_get(l, th) > 0);
351 }
352