/* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include #include #include #include #include #include "loop_int.h" #include "util_int.h" #include "tlist.h" struct qb_loop_timer { struct qb_loop_item item; qb_loop_timer_dispatch_fn dispatch_fn; enum qb_loop_priority p; timer_handle timerlist_handle; enum qb_poll_entry_state state; int32_t check; uint32_t install_pos; }; struct qb_timer_source { struct qb_loop_source s; struct timerlist timerlist; qb_array_t *timers; size_t timer_entry_count; }; static void timer_dispatch(struct qb_loop_item *item, enum qb_loop_priority p) { struct qb_loop_timer *timer = (struct qb_loop_timer *)item; assert(timer->state == QB_POLL_ENTRY_JOBLIST); timer->check = 0; timer->dispatch_fn(timer->item.user_data); timer->state = QB_POLL_ENTRY_EMPTY; } static int32_t expired_timers; static void make_job_from_tmo(void *data) { struct qb_loop_timer *t = (struct qb_loop_timer *)data; struct qb_loop *l = t->item.source->l; assert(t->state == QB_POLL_ENTRY_ACTIVE); qb_loop_level_item_add(&l->level[t->p], &t->item); t->state = QB_POLL_ENTRY_JOBLIST; expired_timers++; } static int32_t expire_the_timers(struct qb_loop_source *s, int32_t ms_timeout) { struct qb_timer_source *ts = (struct qb_timer_source *)s; expired_timers = 0; timerlist_expire(&ts->timerlist); return expired_timers; } int32_t qb_loop_timer_msec_duration_to_expire(struct qb_loop_source * timer_source) { struct qb_timer_source *my_src = (struct qb_timer_source *)timer_source; uint64_t left = timerlist_msec_duration_to_expire(&my_src->timerlist); if (left != -1 && left > 0xFFFFFFFF) { left = 0xFFFFFFFE; } return left; } struct qb_loop_source * qb_loop_timer_create(struct qb_loop *l) { struct qb_timer_source *my_src = malloc(sizeof(struct qb_timer_source)); if (my_src == NULL) { return NULL; } my_src->s.l = l; my_src->s.dispatch_and_take_back = timer_dispatch; my_src->s.poll = expire_the_timers; timerlist_init(&my_src->timerlist); my_src->timers = qb_array_create_2(16, sizeof(struct qb_loop_timer), 16); my_src->timer_entry_count = 0; return (struct qb_loop_source *)my_src; } void qb_loop_timer_destroy(struct qb_loop *l) { struct qb_timer_source *my_src = (struct qb_timer_source *)l->timer_source; qb_array_free(my_src->timers); free(l->timer_source); } static int32_t _timer_from_handle_(struct qb_timer_source *s, qb_loop_timer_handle handle_in, struct qb_loop_timer **timer_pt) { int32_t rc; int32_t check; uint32_t install_pos; struct qb_loop_timer *timer; if (handle_in == 0) { return -EINVAL; } check = handle_in >> 32; install_pos = handle_in & UINT32_MAX; rc = qb_array_index(s->timers, install_pos, (void **)&timer); if (rc != 0) { return rc; } if (timer->check != check) { return -EINVAL; } *timer_pt = timer; return 0; } static int32_t _get_empty_array_position_(struct qb_timer_source *s) { int32_t install_pos; int32_t res = 0; struct qb_loop_timer *timer; for (install_pos = 0; install_pos < s->timer_entry_count; install_pos++) { assert(qb_array_index(s->timers, install_pos, (void **)&timer) == 0); if (timer->state == QB_POLL_ENTRY_EMPTY) { return install_pos; } } res = qb_array_grow(s->timers, s->timer_entry_count + 1); if (res != 0) { return res; } s->timer_entry_count++; install_pos = s->timer_entry_count - 1; return install_pos; } 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) { struct qb_loop_timer *t; struct qb_timer_source *my_src; int32_t i; struct qb_loop *l = lp; if (l == NULL) { l = qb_loop_default_get(); } if (l == NULL || timer_fn == NULL) { return -EINVAL; } my_src = (struct qb_timer_source *)l->timer_source; i = _get_empty_array_position_(my_src); assert(qb_array_index(my_src->timers, i, (void **)&t) >= 0); t->state = QB_POLL_ENTRY_ACTIVE; t->install_pos = i; t->item.user_data = data; t->item.source = (struct qb_loop_source *)my_src; t->dispatch_fn = timer_fn; t->p = p; qb_list_init(&t->item.list); /* * Make sure just positive integers are used for the integrity(?) * checks within 2^32 address space, if we miss 200 times in a row * (just 0 is concerned per specification of random), the PRNG may be * broken -> the value is unspecified, subject of previous assignment. */ for (i = 0; i < 200; i++) { t->check = random(); if (t->check > 0) { break; /* covers also t->check == UINT32_MAX */ } } if (timer_handle_out) { *timer_handle_out = (((uint64_t) (t->check)) << 32) | t->install_pos; } return timerlist_add_duration(&my_src->timerlist, make_job_from_tmo, t, nsec_duration, &t->timerlist_handle); } int32_t qb_loop_timer_del(struct qb_loop * lp, qb_loop_timer_handle th) { struct qb_timer_source *s; struct qb_loop_timer *t; int32_t res; struct qb_loop *l = lp; if (l == NULL) { l = qb_loop_default_get(); } s = (struct qb_timer_source *)l->timer_source; res = _timer_from_handle_(s, th, &t); if (res != 0) { return res; } if (t->state == QB_POLL_ENTRY_DELETED) { qb_util_log(LOG_WARNING, "timer already deleted"); return 0; } if (t->state != QB_POLL_ENTRY_ACTIVE && t->state != QB_POLL_ENTRY_JOBLIST) { return -EINVAL; } if (t->state == QB_POLL_ENTRY_JOBLIST) { qb_loop_level_item_del(&l->level[t->p], &t->item); } if (t->timerlist_handle) { timerlist_del(&s->timerlist, t->timerlist_handle); } t->state = QB_POLL_ENTRY_EMPTY; return 0; } uint64_t qb_loop_timer_expire_time_get(struct qb_loop * lp, qb_loop_timer_handle th) { struct qb_timer_source *s; struct qb_loop_timer *t; int32_t res; struct qb_loop *l = lp; if (l == NULL) { l = qb_loop_default_get(); } s = (struct qb_timer_source *)l->timer_source; res = _timer_from_handle_(s, th, &t); if (res != 0) { return 0; } if (t->state != QB_POLL_ENTRY_ACTIVE) { return 0; } return timerlist_expire_time(&s->timerlist, t->timerlist_handle); } int32_t qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th) { return (qb_loop_timer_expire_time_get(l, th) > 0); }