mirror of
https://salsa.debian.org/ha-team/libqb
synced 2025-12-27 07:29:23 +00:00
280 lines
6.5 KiB
C
280 lines
6.5 KiB
C
/*
|
|
* Copyright (C) 2010 Red Hat, Inc.
|
|
*
|
|
* Author: Angus Salkeld <asalkeld@redhat.com>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "os_base.h"
|
|
#include <pthread.h>
|
|
|
|
#include <qb/qbdefs.h>
|
|
#include <qb/qblist.h>
|
|
#include <qb/qbarray.h>
|
|
#include <qb/qbloop.h>
|
|
#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;
|
|
uint32_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(64, sizeof(struct qb_loop_timer));
|
|
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;
|
|
uint32_t check;
|
|
uint32_t install_pos;
|
|
struct qb_loop_timer *timer;
|
|
|
|
if (handle_in == 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
check = ((uint32_t) (((uint64_t) handle_in) >> 32));
|
|
install_pos = handle_in & 0xffffffff;
|
|
|
|
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);
|
|
|
|
for (i = 0; i < 200; i++) {
|
|
t->check = random();
|
|
|
|
if (t->check != 0 && t->check != 0xffffffff) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|