LOOP: add qb_loop_destroy()

Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
Angus Salkeld 2010-10-30 20:51:36 +11:00
parent e211caab96
commit 970a4bc6d5
7 changed files with 231 additions and 91 deletions

View File

@ -58,6 +58,11 @@ typedef void (*qb_loop_poll_low_fds_event_fn) (int32_t not_enough, int32_t fds_a
*/
qb_loop_t * qb_loop_create(void);
/**
*
*/
void qb_loop_destroy(struct qb_loop * l);
/**
* Stop the main loop.
* @param l pointer to the loop instance

View File

@ -25,10 +25,6 @@
#include <qb/qbloop.h>
#include "loop_int.h"
static struct qb_loop_source * timer_source;
static struct qb_loop_source * job_source;
static struct qb_loop_source * fd_source;
static int32_t qb_loop_run_level(struct qb_loop_level *level)
{
struct qb_loop_item *job;
@ -70,15 +66,22 @@ struct qb_loop * qb_loop_create(void)
}
l->stop_requested = QB_FALSE;
qb_list_init(&l->source_head);
// install sources
timer_source = qb_loop_timer_init(l);
job_source = qb_loop_jobs_init(l);
fd_source = qb_loop_poll_init(l);
l->timer_source = qb_loop_timer_create(l);
l->job_source = qb_loop_jobs_create(l);
l->fd_source = qb_loop_poll_create(l);
return l;
}
void qb_loop_destroy(struct qb_loop * l)
{
qb_loop_timer_destroy(l);
qb_loop_jobs_destroy(l);
qb_loop_poll_destroy(l);
free(l);
}
void qb_loop_stop(struct qb_loop *l)
{
l->stop_requested = QB_TRUE;
@ -98,16 +101,16 @@ void qb_loop_run(struct qb_loop *l)
p_stop--;
}
todo += job_source->poll(job_source, 0);
todo += timer_source->poll(timer_source, 0);
todo += l->job_source->poll(l->job_source, 0);
todo += l->timer_source->poll(l->timer_source, 0);
if (todo > 0) {
ms_timeout = 0;
} else {
todo = 0;
ms_timeout = qb_loop_timer_msec_duration_to_expire(timer_source);
ms_timeout = qb_loop_timer_msec_duration_to_expire(l->timer_source);
}
todo += fd_source->poll(fd_source, ms_timeout);
todo += l->fd_source->poll(l->fd_source, ms_timeout);
for (p = QB_LOOP_HIGH; p >= p_stop; p--) {
todo -= qb_loop_run_level(&l->level[p]);

View File

@ -46,23 +46,31 @@ struct qb_loop_source {
void (*dispatch_and_take_back)(struct qb_loop_item *i,
enum qb_loop_priority p);
int32_t (*poll)(struct qb_loop_source* s, int32_t ms_timeout);
struct qb_list_head list;
};
struct qb_loop {
struct qb_loop_level level[3];
int32_t stop_requested;
struct qb_list_head source_head;
struct qb_loop_source * timer_source;
struct qb_loop_source * job_source;
struct qb_loop_source * fd_source;
};
struct qb_loop_source *
qb_loop_jobs_init(struct qb_loop *l);
qb_loop_jobs_create(struct qb_loop *l);
struct qb_loop_source*
qb_loop_timer_init(struct qb_loop *l);
qb_loop_timer_create(struct qb_loop *l);
struct qb_loop_source*
qb_loop_poll_init(struct qb_loop *l);
qb_loop_poll_create(struct qb_loop *l);
void qb_loop_jobs_destroy(struct qb_loop *l);
void qb_loop_timer_destroy(struct qb_loop *l);
void qb_loop_poll_destroy(struct qb_loop *l);
int32_t qb_loop_timer_msec_duration_to_expire(struct qb_loop_source *timer_source);

View File

@ -30,9 +30,6 @@ struct qb_loop_job {
qb_loop_job_dispatch_fn dispatch_fn;
};
static struct qb_loop_source * my_src;
static void job_dispatch(struct qb_loop_item * item,
enum qb_loop_priority p)
{
@ -61,16 +58,19 @@ static int32_t get_more_jobs(struct qb_loop_source* s, int32_t ms_timeout)
}
struct qb_loop_source *
qb_loop_jobs_init(struct qb_loop *l)
qb_loop_jobs_create(struct qb_loop *l)
{
my_src = malloc(sizeof(struct qb_loop_source));
my_src->l = l;
my_src->dispatch_and_take_back = job_dispatch;
my_src->poll = get_more_jobs;
struct qb_loop_source *s = malloc(sizeof(struct qb_loop_source));
s->l = l;
s->dispatch_and_take_back = job_dispatch;
s->poll = get_more_jobs;
qb_list_init(&my_src->list);
qb_list_add_tail(&my_src->list, &l->source_head);
return my_src;
return s;
}
void qb_loop_jobs_destroy(struct qb_loop *l)
{
free(l->job_source);
}
int32_t qb_loop_job_add(struct qb_loop *l,
@ -90,7 +90,7 @@ int32_t qb_loop_job_add(struct qb_loop *l,
job->dispatch_fn = dispatch_fn;
job->item.user_data = data;
job->item.source = my_src;
job->item.source = l->job_source;
qb_list_init(&job->item.list);
qb_list_add_tail(&job->item.list, &l->level[p].wait_head);

View File

@ -62,8 +62,6 @@ struct qb_poll_source {
#endif /* HAVE_EPOLL */
};
static struct qb_poll_source * my_src;
#ifdef HAVE_EPOLL
static int32_t poll_to_epoll_event(int32_t event)
@ -128,7 +126,7 @@ static void poll_fds_usage_check(struct qb_poll_source *s)
}
for (i = 0; i < s->poll_entry_count; i++) {
assert(qb_array_index(my_src->poll_entries, i, (void**)&pe) == 0);
assert(qb_array_index(s->poll_entries, i, (void**)&pe) == 0);
if (pe->ufd.fd != -1) {
socks_used++;
}
@ -179,7 +177,7 @@ static int32_t poll_and_add_to_jobs(struct qb_loop_source* src, int32_t ms_timeo
}
for (i = 0; i < res; i++) {
assert(qb_array_index(my_src->poll_entries, events[i].data.u32, (void**)&pe) == 0);
assert(qb_array_index(s->poll_entries, events[i].data.u32, (void**)&pe) == 0);
if (pe->ufd.fd == -1) {
// empty
continue;
@ -208,7 +206,7 @@ static int32_t poll_and_add_to_jobs(struct qb_loop_source* src, int32_t ms_timeo
poll_fds_usage_check(s);
for (i = 0; i < s->poll_entry_count; i++) {
assert(qb_array_index(my_src->poll_entries, i, (void**)&pe) == 0);
assert(qb_array_index(l->fd_source->poll_entries, i, (void**)&pe) == 0);
memcpy(&s->ufds[i], &pe->ufd, sizeof(struct pollfd));
}
@ -225,7 +223,7 @@ static int32_t poll_and_add_to_jobs(struct qb_loop_source* src, int32_t ms_timeo
// empty
continue;
}
assert(qb_array_index(my_src->poll_entries, i, (void**)&pe) == 0);
assert(qb_array_index(l->fd_source->poll_entries, i, (void**)&pe) == 0);
if (s->ufds[i].revents == pe->ufd.revents) {
// entry already in the job queue.
continue;
@ -241,35 +239,44 @@ static int32_t poll_and_add_to_jobs(struct qb_loop_source* src, int32_t ms_timeo
#endif /* HAVE_EPOLL */
struct qb_loop_source*
qb_loop_poll_init(struct qb_loop *l)
qb_loop_poll_create(struct qb_loop *l)
{
my_src = malloc(sizeof(struct qb_poll_source));
my_src->s.l = l;
my_src->s.dispatch_and_take_back = poll_dispatch_and_take_back;
my_src->s.poll = poll_and_add_to_jobs;
struct qb_poll_source *s = malloc(sizeof(struct qb_poll_source));
s->s.l = l;
s->s.dispatch_and_take_back = poll_dispatch_and_take_back;
s->s.poll = poll_and_add_to_jobs;
my_src->poll_entries = qb_array_create(128, sizeof(struct qb_poll_entry));
my_src->poll_entry_count = 0;
my_src->low_fds_event_fn = NULL;
my_src->not_enough_fds = 0;
s->poll_entries = qb_array_create(128, sizeof(struct qb_poll_entry));
s->poll_entry_count = 0;
s->low_fds_event_fn = NULL;
s->not_enough_fds = 0;
#ifdef HAVE_EPOLL
my_src->epollfd = epoll_create1(EPOLL_CLOEXEC);
my_src->events = 0;
s->epollfd = epoll_create1(EPOLL_CLOEXEC);
s->events = 0;
#else
my_src->ufds = 0;
s->ufds = 0;
#endif /* HAVE_EPOLL */
qb_list_init(&my_src->s.list);
qb_list_add_tail(&my_src->s.list, &l->source_head);
return (struct qb_loop_source*)my_src;
return (struct qb_loop_source*)s;
}
void qb_loop_poll_destroy(struct qb_loop *l)
{
struct qb_poll_source * s = (struct qb_poll_source *)l->fd_source;
qb_array_free(s->poll_entries);
#ifdef HAVE_EPOLL
close(s->epollfd);
#endif /* HAVE_EPOLL */
free(s);
}
int32_t qb_loop_poll_low_fds_event_set(
qb_loop_t *l,
qb_loop_poll_low_fds_event_fn fn)
{
my_src->low_fds_event_fn = fn;
struct qb_poll_source * s = (struct qb_poll_source *)l->fd_source;
s->low_fds_event_fn = fn;
return 0;
}
@ -292,10 +299,11 @@ int32_t qb_loop_poll_add(struct qb_loop *l,
#else
struct pollfd *ufds;
#endif /* HAVE_EPOLL */
struct qb_poll_source * s = (struct qb_poll_source *)l->fd_source;
for (found = 0, install_pos = 0;
install_pos < my_src->poll_entry_count; install_pos++) {
assert(qb_array_index(my_src->poll_entries, install_pos, (void**)&pe) == 0);
install_pos < s->poll_entry_count; install_pos++) {
assert(qb_array_index(s->poll_entries, install_pos, (void**)&pe) == 0);
if (pe->ufd.fd == -1) {
found = 1;
break;
@ -306,50 +314,50 @@ int32_t qb_loop_poll_add(struct qb_loop *l,
/*
* Grow pollfd list
*/
res = qb_array_grow(my_src->poll_entries,
my_src->poll_entry_count + 1);
res = qb_array_grow(s->poll_entries,
s->poll_entry_count + 1);
if (res != 0) {
return res;
}
#ifdef HAVE_EPOLL
new_size = (my_src->poll_entry_count+ 1) * sizeof(struct epoll_event);
ev = realloc(my_src->events, new_size);
new_size = (s->poll_entry_count+ 1) * sizeof(struct epoll_event);
ev = realloc(s->events, new_size);
if (ev == NULL) {
return -ENOMEM;
}
my_src->events = ev;
s->events = ev;
#else
new_size = (my_src->poll_entry_count+ 1) * sizeof(struct pollfd);
ufds = realloc(my_src->ufds, new_size);
new_size = (s->poll_entry_count+ 1) * sizeof(struct pollfd);
ufds = realloc(s->ufds, new_size);
if (ufds == NULL) {
return -ENOMEM;
}
my_src->ufds = ufds;
s->ufds = ufds;
#endif /* HAVE_EPOLL */
my_src->poll_entry_count += 1;
install_pos = my_src->poll_entry_count - 1;
s->poll_entry_count += 1;
install_pos = s->poll_entry_count - 1;
}
/*
* Install new dispatch handler
*/
assert(qb_array_index(my_src->poll_entries, install_pos, (void**)&pe) == 0);
assert(qb_array_index(s->poll_entries, install_pos, (void**)&pe) == 0);
pe->install_pos = install_pos;
pe->ufd.fd = fd;
pe->ufd.events = events;
pe->ufd.revents = 0;
pe->dispatch_fn = dispatch_fn;
pe->item.user_data = data;
pe->item.source = (struct qb_loop_source*)my_src;
pe->item.source = (struct qb_loop_source*)l->fd_source;
pe->p = p;
#ifdef HAVE_EPOLL
ev = &my_src->events[install_pos];
ev = &s->events[install_pos];
ev->events = poll_to_epoll_event(events);
ev->data.u64 = 0; /* valgrind */
ev->data.u32 = install_pos;
if (epoll_ctl(my_src->epollfd, EPOLL_CTL_ADD, fd, ev) == -1) {
if (epoll_ctl(s->epollfd, EPOLL_CTL_ADD, fd, ev) == -1) {
res = -errno;
qb_util_log(LOG_ERR, "epoll_ctl(add) : %s", strerror(-res));
}
@ -368,12 +376,13 @@ int32_t qb_loop_poll_mod(struct qb_loop *l,
int32_t i;
int32_t res = 0;
struct qb_poll_entry *pe;
struct qb_poll_source * s = (struct qb_poll_source *)l->fd_source;
/*
* Find file descriptor to modify events and dispatch function
*/
for (i = 0; i < my_src->poll_entry_count; i++) {
assert(qb_array_index(my_src->poll_entries, i, (void**)&pe) == 0);
for (i = 0; i < s->poll_entry_count; i++) {
assert(qb_array_index(s->poll_entries, i, (void**)&pe) == 0);
if (pe->ufd.fd != fd) {
continue;
}
@ -382,9 +391,9 @@ int32_t qb_loop_poll_mod(struct qb_loop *l,
pe->p = p;
if (pe->ufd.events != events) {
#ifdef HAVE_EPOLL
my_src->events[i].events = poll_to_epoll_event(events);
my_src->events[i].data.u32 = i;
if (epoll_ctl(my_src->epollfd, EPOLL_CTL_MOD, fd, &my_src->events[i]) == -1) {
s->events[i].events = poll_to_epoll_event(events);
s->events[i].data.u32 = i;
if (epoll_ctl(s->epollfd, EPOLL_CTL_MOD, fd, &s->events[i]) == -1) {
res = -errno;
qb_util_log(LOG_ERR, "epoll_ctl(mod) : %s", strerror(-res));
}
@ -402,12 +411,13 @@ int32_t qb_loop_poll_del(struct qb_loop *l, int32_t fd)
int32_t i;
int32_t res = 0;
struct qb_poll_entry *pe;
struct qb_poll_source * s = (struct qb_poll_source *)l->fd_source;
/*
* Find file descriptor to modify events and dispatch function
*/
for (i = 0; i < my_src->poll_entry_count; i++) {
assert(qb_array_index(my_src->poll_entries, i, (void**)&pe) == 0);
for (i = 0; i < s->poll_entry_count; i++) {
assert(qb_array_index(s->poll_entries, i, (void**)&pe) == 0);
if (pe->ufd.fd != fd) {
continue;
}
@ -415,15 +425,15 @@ int32_t qb_loop_poll_del(struct qb_loop *l, int32_t fd)
pe->ufd.events = 0;
pe->ufd.revents = 0;
#ifdef HAVE_EPOLL
if (epoll_ctl(my_src->epollfd, EPOLL_CTL_DEL, fd, NULL) == -1) {
if (epoll_ctl(s->epollfd, EPOLL_CTL_DEL, fd, NULL) == -1) {
res = -errno;
qb_util_log(LOG_ERR, "epoll_ctl(del) : %s",
strerror(-res));
}
#else
my_src->ufds[i].fd = -1;
my_src->ufds[i].events = 0;
my_src->ufds[i].revents = 0;
s->ufds[i].fd = -1;
s->ufds[i].events = 0;
s->ufds[i].revents = 0;
#endif /* HAVE_EPOLL */
return res;
}

View File

@ -38,8 +38,6 @@ struct qb_timer_source {
struct timerlist timerlist;
};
static struct qb_timer_source * my_src;
static void timer_dispatch(struct qb_loop_item * item,
enum qb_loop_priority p)
{
@ -69,6 +67,7 @@ static int32_t expire_the_timers(struct qb_loop_source* s, int32_t ms_timeout)
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;
@ -77,21 +76,24 @@ int32_t qb_loop_timer_msec_duration_to_expire(struct qb_loop_source *timer_sourc
}
struct qb_loop_source*
qb_loop_timer_init(struct qb_loop *l)
qb_loop_timer_create(struct qb_loop *l)
{
my_src = malloc(sizeof(struct qb_timer_source));
struct qb_timer_source * my_src = malloc(sizeof(struct qb_timer_source));
my_src->s.l = l;
my_src->s.dispatch_and_take_back = timer_dispatch;
my_src->s.poll = expire_the_timers;
qb_list_init(&my_src->s.list);
qb_list_add_tail(&my_src->s.list, &l->source_head);
timerlist_init(&my_src->timerlist);
return (struct qb_loop_source*)my_src;
}
void qb_loop_timer_destroy(struct qb_loop *l)
{
free(l->timer_source);
}
int32_t qb_loop_timer_add(struct qb_loop *l,
enum qb_loop_priority p,
int32_t msec_duration,
@ -100,7 +102,12 @@ int32_t qb_loop_timer_add(struct qb_loop *l,
qb_loop_timer_handle * timer_handle_out)
{
struct qb_loop_timer *t;
struct qb_timer_source * my_src;
if (l == NULL || timer_fn == NULL) {
return -EINVAL;
}
my_src = (struct qb_timer_source *)l->timer_source;
if (timer_handle_out == NULL) {
return -ENOENT;
}
@ -119,6 +126,7 @@ int32_t qb_loop_timer_add(struct qb_loop *l,
int32_t qb_loop_timer_del(struct qb_loop *l, qb_loop_timer_handle th)
{
struct qb_timer_source * my_src = (struct qb_timer_source *)l->timer_source;
if (th == NULL) {
return -EINVAL;
}
@ -129,6 +137,7 @@ int32_t qb_loop_timer_del(struct qb_loop *l, qb_loop_timer_handle th)
uint64_t qb_loop_timer_expire_time_get(struct qb_loop *l, qb_loop_timer_handle th)
{
struct qb_timer_source * my_src = (struct qb_timer_source *)l->timer_source;
if (th == 0) {
return 0;
}

View File

@ -99,7 +99,7 @@ START_TEST(test_loop_job_input)
ck_assert_int_eq(res, -EINVAL);
res = qb_loop_job_add(l, QB_LOOP_LOW, NULL, NULL);
ck_assert_int_eq(res, -EINVAL);
qb_loop_destroy(l);
}
END_TEST
@ -116,6 +116,7 @@ START_TEST(test_loop_job_1)
qb_loop_run(l);
ck_assert_int_eq(job_1_run_count, 1);
qb_loop_destroy(l);
}
END_TEST
@ -132,6 +133,7 @@ START_TEST(test_loop_job_4)
ck_assert_int_eq(job_1_run_count, 1);
ck_assert_int_eq(job_2_run_count, 1);
ck_assert_int_eq(job_3_run_count, 1);
qb_loop_destroy(l);
}
END_TEST
@ -147,11 +149,11 @@ START_TEST(test_loop_job_nuts)
qb_loop_run(l);
fail_if(job_1_run_count < 500);
qb_loop_destroy(l);
}
END_TEST
static Suite *rb_suite(void)
static Suite *loop_job_suite(void)
{
TCase *tc;
Suite *s = suite_create("qb_loop_job");
@ -175,6 +177,110 @@ static Suite *rb_suite(void)
return s;
}
/*
* -----------------------------------------------------------------------
* Timers
*/
START_TEST(test_loop_timer_input)
{
int32_t res;
qb_loop_timer_handle th;
qb_loop_t *l = qb_loop_create();
fail_if(l == NULL);
res = qb_loop_timer_add(NULL, QB_LOOP_LOW, 5, NULL, job_2, &th);
ck_assert_int_eq(res, -EINVAL);
res = qb_loop_timer_add(l, QB_LOOP_LOW, 5, l, NULL, &th);
ck_assert_int_eq(res, -EINVAL);
res = qb_loop_timer_add(l, QB_LOOP_LOW, 5, l, job_1, NULL);
ck_assert_int_eq(res, -ENOENT);
qb_loop_destroy(l);
}
END_TEST
struct qb_stop_watch {
uint64_t start;
uint64_t end;
qb_loop_t *l;
int32_t ms_timer;
uint64_t total;
int32_t count;
};
static void stop_watch_tmo(void*data)
{
qb_loop_timer_handle th;
struct qb_stop_watch *sw = (struct qb_stop_watch *)data;
sw->end = qb_util_nano_current_get();
sw->total += (sw->end - sw->start) / QB_TIME_NS_IN_MSEC;
sw->start = sw->end;
sw->count++;
if (sw->count < 50) {
qb_loop_timer_add(sw->l, QB_LOOP_HIGH, sw->ms_timer, data, stop_watch_tmo, &th);
} else {
printf("average timeout for %d ms timer is %ld\n", sw->ms_timer,
sw->total/sw->count);
if (sw->ms_timer == 100) {
qb_loop_stop(sw->l);
}
}
}
static void start_timer(qb_loop_t *l, struct qb_stop_watch *sw, int32_t timeout)
{
qb_loop_timer_handle th;
int32_t res;
sw->l = l;
sw->count = 0;
sw->total = 0;
sw->ms_timer = timeout;
sw->start = qb_util_nano_current_get();
res = qb_loop_timer_add(sw->l, QB_LOOP_LOW, sw->ms_timer, sw, stop_watch_tmo, &th);
ck_assert_int_eq(res, 0);
}
START_TEST(test_loop_timer_basic)
{
int32_t i;
int32_t max = RAND_MAX / 125;
int32_t tmo;
struct qb_stop_watch sw[11];
qb_loop_t *l = qb_loop_create();
fail_if(l == NULL);
for (i = 0; i < 10; i++) {
tmo = QB_MAX(1, random() / max);
start_timer(l, &sw[i], tmo);
}
start_timer(l, &sw[i], 100);
qb_loop_run(l);
qb_loop_destroy(l);
}
END_TEST
static Suite *loop_timer_suite(void)
{
TCase *tc;
Suite *s = suite_create("qb_loop_timers");
tc = tcase_create("limits");
tcase_add_test(tc, test_loop_timer_input);
suite_add_tcase(s, tc);
tc = tcase_create("basic");
tcase_add_test(tc, test_loop_timer_basic);
tcase_set_timeout(tc, 30);
suite_add_tcase(s, tc);
return s;
}
static void libqb_log_fn(const char *file_name,
int32_t file_line, int32_t severity, const char *msg)
{
@ -184,9 +290,8 @@ static void libqb_log_fn(const char *file_name,
int32_t main(void)
{
int32_t number_failed;
Suite *s = rb_suite();
SRunner *sr = srunner_create(s);
SRunner *sr = srunner_create(loop_job_suite());
srunner_add_suite (sr, loop_timer_suite());
qb_util_set_log_function(libqb_log_fn);