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
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
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
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 if (timerlist_expire(&ts->timerlist) != 0) {
80 qb_util_log(LOG_ERR, "timerlist_expire failed");
81 }
82 return expired_timers;
83 }
84
85 int32_t
86 qb_loop_timer_msec_duration_to_expire(struct qb_loop_source * timer_source)
87 {
88 struct qb_timer_source *my_src = (struct qb_timer_source *)timer_source;
89 uint64_t left = timerlist_msec_duration_to_expire(&my_src->timerlist);
90 if (left != -1 && left > 0xFFFFFFFF) {
91 left = 0xFFFFFFFE;
92 }
93 return left;
94 }
95
96 struct qb_loop_source *
97 qb_loop_timer_create(struct qb_loop *l)
98 {
99 struct qb_timer_source *my_src = malloc(sizeof(struct qb_timer_source));
100 if (my_src == NULL) {
101 return NULL;
102 }
103 my_src->s.l = l;
104 my_src->s.dispatch_and_take_back = timer_dispatch;
105 my_src->s.poll = expire_the_timers;
106
107 timerlist_init(&my_src->timerlist);
108 my_src->timers = qb_array_create_2(16, sizeof(struct qb_loop_timer), 16);
109 my_src->timer_entry_count = 0;
110 pthread_mutex_init(&my_src->lock, NULL);
111
112 return (struct qb_loop_source *)my_src;
113 }
114
115 void
116 qb_loop_timer_destroy(struct qb_loop *l)
117 {
118 struct qb_timer_source *my_src =
119 (struct qb_timer_source *)l->timer_source;
120
121 timerlist_destroy(&my_src->timerlist);
122 qb_array_free(my_src->timers);
123 free(l->timer_source);
124 }
125
126 static int32_t
127 _timer_from_handle_(struct qb_timer_source *s,
128 qb_loop_timer_handle handle_in,
129 struct qb_loop_timer **timer_pt)
130 {
131 int32_t rc;
132 int32_t check;
133 uint32_t install_pos;
134 struct qb_loop_timer *timer;
135
136 if (handle_in == 0) {
137 return -EINVAL;
138 }
139
140 check = handle_in >> 32;
141 install_pos = handle_in & UINT32_MAX;
142
143 rc = qb_array_index(s->timers, install_pos, (void **)&timer);
144 if (rc != 0) {
145 return rc;
146 }
147 if (timer->check != check) {
148 return -EINVAL;
149 }
150 *timer_pt = timer;
151 return 0;
152 }
153
154 static int32_t
155 _get_empty_array_position_(struct qb_timer_source *s)
156 {
157 int32_t install_pos;
158 int32_t res = 0;
159 struct qb_loop_timer *timer;
160
161 for (install_pos = 0; install_pos < s->timer_entry_count; install_pos++) {
162 assert(qb_array_index(s->timers, install_pos, (void **)&timer)
163 == 0);
164 if (timer->state == QB_POLL_ENTRY_EMPTY) {
165 return install_pos;
166 }
167 }
168
169 res = qb_array_grow(s->timers, s->timer_entry_count + 1);
170 if (res != 0) {
171 return res;
172 }
173
174 s->timer_entry_count++;
175 install_pos = s->timer_entry_count - 1;
176 return install_pos;
177 }
178
179 int32_t
180 qb_loop_timer_add(struct qb_loop * lp,
181 enum qb_loop_priority p,
182 uint64_t nsec_duration,
183 void *data,
184 qb_loop_timer_dispatch_fn timer_fn,
185 qb_loop_timer_handle * timer_handle_out)
186 {
187 struct qb_loop_timer *t;
188 struct qb_timer_source *my_src;
189 int32_t i;
190 int res;
191 struct qb_loop *l = lp;
192
193 if (l == NULL) {
194 l = qb_loop_default_get();
195 }
196
197 if (l == NULL || timer_fn == NULL) {
198 return -EINVAL;
199 }
200 my_src = (struct qb_timer_source *)l->timer_source;
201
202 if ( (res=pthread_mutex_lock(&my_src->lock))) {
203 return -res;
204 }
205 i = _get_empty_array_position_(my_src);
206 assert(qb_array_index(my_src->timers, i, (void **)&t) >= 0);
207 t->state = QB_POLL_ENTRY_ACTIVE;
208 t->install_pos = i;
209 t->item.user_data = data;
210 t->item.source = (struct qb_loop_source *)my_src;
211 t->dispatch_fn = timer_fn;
212 t->p = p;
213 qb_list_init(&t->item.list);
214
215 /* Unlock here to stop anyone else changing the state while we're initializing */
216 pthread_mutex_unlock(&my_src->lock);
217
218 /*
219 * Make sure just positive integers are used for the integrity(?)
220 * checks within 2^32 address space, if we miss 200 times in a row
221 * (just 0 is concerned per specification of random), the PRNG may be
222 * broken -> the value is unspecified, subject of previous assignment.
223 */
224 for (i = 0; i < 200; i++) {
|
(1) Event dont_call: |
"random" should not be used for security-related applications, because linear congruential algorithms are too easy to break. |
|
(2) Event remediation: |
Use a compliant random number generator, such as "/dev/random" or "/dev/urandom" on Unix-like systems, and CNG (Cryptography API: Next Generation) on Windows. |
225 t->check = random();
226
227 if (t->check > 0) {
228 break; /* covers also t->check == UINT32_MAX */
229 }
230 }
231
232 if (timer_handle_out) {
233 *timer_handle_out = (((uint64_t) (t->check)) << 32) | t->install_pos;
234 }
235 return timerlist_add_duration(&my_src->timerlist,
236 make_job_from_tmo, t,
237 nsec_duration, &t->timerlist_handle);
238 }
239
240 int32_t
241 qb_loop_timer_del(struct qb_loop * lp, qb_loop_timer_handle th)
242 {
243 struct qb_timer_source *s;
244 struct qb_loop_timer *t;
245 int32_t res;
246 struct qb_loop *l = lp;
247
248 if (l == NULL) {
249 l = qb_loop_default_get();
250 }
251 s = (struct qb_timer_source *)l->timer_source;
252
253 res = _timer_from_handle_(s, th, &t);
254 if (res != 0) {
255 return res;
256 }
257
258 if (t->state == QB_POLL_ENTRY_DELETED) {
259 qb_util_log(LOG_WARNING, "timer already deleted");
260 return 0;
261 }
262 if (t->state != QB_POLL_ENTRY_ACTIVE &&
263 t->state != QB_POLL_ENTRY_JOBLIST) {
264 return -EINVAL;
265 }
266 if (t->state == QB_POLL_ENTRY_JOBLIST) {
267 qb_loop_level_item_del(&l->level[t->p], &t->item);
268 }
269
270 if (t->timerlist_handle) {
271 if (timerlist_del(&s->timerlist, t->timerlist_handle) != 0) {
272 qb_util_log(LOG_ERR, "Could not delete timer from timerlist");
273 }
274 }
275 t->state = QB_POLL_ENTRY_EMPTY;
276 return 0;
277 }
278
279 uint64_t
280 qb_loop_timer_expire_time_get(struct qb_loop * lp, qb_loop_timer_handle th)
281 {
282 struct qb_timer_source *s;
283 struct qb_loop_timer *t;
284 int32_t res;
285 struct qb_loop *l = lp;
286
287 if (l == NULL) {
288 l = qb_loop_default_get();
289 }
290 s = (struct qb_timer_source *)l->timer_source;
291
292 res = _timer_from_handle_(s, th, &t);
293 if (res != 0) {
294 return 0;
295 }
296
297 if (t->state != QB_POLL_ENTRY_ACTIVE) {
298 return 0;
299 }
300
301 return timerlist_expire_time(&s->timerlist, t->timerlist_handle);
302 }
303
304 uint64_t
305 qb_loop_timer_expire_time_remaining(struct qb_loop * lp, qb_loop_timer_handle th)
306 {
307
308 uint64_t current_ns;
309 /* NOTE: while it does not appear that absolute timers are used anywhere,
310 * we may as well respect this pattern in case that changes.
311 * Unfortunately, that means we do need to repeat timer fetch code from qb_loop_timer_expire_time_get
312 * rather than just a simple call to qb_loop_timer_expire_time_get and qb_util_nano_current_get.
313 */
314
315 struct qb_timer_source *s;
316 struct qb_loop_timer *t;
317 int32_t res;
318 struct qb_loop *l = lp;
319
320 if (l == NULL) {
321 l = qb_loop_default_get();
322 }
323 s = (struct qb_timer_source *)l->timer_source;
324
325 res = _timer_from_handle_(s, th, &t);
326 if (res != 0) {
327 return 0;
328 }
329 if (t->state != QB_POLL_ENTRY_ACTIVE) {
330 return 0;
331 }
332
333 struct timerlist_timer *timer = (struct timerlist_timer *)t->timerlist_handle;
334 if (timer->is_absolute_timer) {
335 current_ns = qb_util_nano_from_epoch_get();
336 }
337 else {
338 current_ns = qb_util_nano_current_get();
339 }
340 uint64_t timer_ns = timerlist_expire_time(&s->timerlist, t->timerlist_handle);
341 if (timer_ns < current_ns) {
342 return 0; // respect the "expired" contract
343 }
344 return timer_ns - current_ns;
345
346
347 }
348
349 int32_t
350 qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th)
351 {
352 return (qb_loop_timer_expire_time_get(l, th) > 0);
353 }
354