write and run methods for eventloop extension

This commit is contained in:
Willem Toorop 2014-10-10 14:48:52 +02:00
parent 2172a7280a
commit f61d2ae262
7 changed files with 426 additions and 436 deletions

View File

@ -759,11 +759,11 @@ getdns_context_request_count_changed(getdns_context *context, uint32_t prev_rc)
return;
if (context->outbound_requests->count)
context->extension->vmt->schedule_read(
context->extension->vmt->schedule(
context->extension, ub_fd(context->unbound_ctx),
TIMEOUT_FOREVER, &context->ub_event);
else
context->extension->vmt->clear_read(
context->extension->vmt->clear(
context->extension, &context->ub_event);
}
@ -806,6 +806,7 @@ rebuild_ub_ctx(struct getdns_context* context) {
context->ub_event.userarg = context;
context->ub_event.read_cb = getdns_context_ub_read_cb;
context->ub_event.write_cb = NULL;
context->ub_event.timeout_cb = NULL;
return GETDNS_RETURN_GOOD;
@ -1811,17 +1812,15 @@ getdns_context_get_num_pending_requests(struct getdns_context* context,
{
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
if (context->outbound_requests->count &&
context->extension == &context->mini_event.loop)
getdns_mini_event_handle_timeouts(
&context->mini_event, next_timeout);
if (context->outbound_requests->count)
context->extension->vmt->run_once(context->extension, 0);
return context->outbound_requests->count;
}
/* process async reqs */
getdns_return_t getdns_context_process_async(struct getdns_context* context)
getdns_return_t
getdns_context_process_async(struct getdns_context* context)
{
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
@ -1831,17 +1830,18 @@ getdns_return_t getdns_context_process_async(struct getdns_context* context)
context->processing = 0;
return GETDNS_RETURN_GENERIC_ERROR;
}
if (context->extension == &context->mini_event.loop
&& getdns_mini_event_handle_select(&context->mini_event, NULL)) {
context->processing = 0;
return GETDNS_RETURN_GENERIC_ERROR;
}
// reset the processing flag
context->processing = 0;
context->extension->vmt->run_once(context->extension, 0);
return GETDNS_RETURN_GOOD;
}
void
getdns_context_run(getdns_context *context)
{
context->extension->vmt->run(context->extension);
}
typedef struct timeout_accumulator {
getdns_transaction_t* ids;
int idx;
@ -1872,8 +1872,6 @@ cancel_outstanding_requests(struct getdns_context* context, int fire_callback) {
getdns_return_t
getdns_context_detach_eventloop(struct getdns_context* context)
{
getdns_return_t r = GETDNS_RETURN_GOOD;
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
/* When called from within a callback, do not execute pending
@ -1886,13 +1884,10 @@ getdns_context_detach_eventloop(struct getdns_context* context)
context->processing = 1;
/* cancel all outstanding requests */
cancel_outstanding_requests(context, 1);
r = context->extension->vmt->cleanup(context->extension);
if (r == GETDNS_RETURN_GOOD) {
context->extension = &context->mini_event.loop;
r = getdns_mini_event_init(context, &context->mini_event);
}
context->processing = 0;
return r;
context->extension->vmt->cleanup(context->extension);
context->extension = &context->mini_event.loop;
return getdns_mini_event_init(context, &context->mini_event);
}
getdns_return_t
@ -1921,11 +1916,12 @@ getdns_context_schedule_timeout(getdns_context *context, uint64_t timeout,
/* Initialize eev_data struct */
el_ev->userarg = userarg;
el_ev->read_cb = NULL;
el_ev->write_cb = NULL;
el_ev->timeout_cb = callback;
el_ev->ev = NULL;
return context->extension->vmt->schedule_timeout(
context->extension, timeout, el_ev);
return context->extension->vmt->schedule(
context->extension, -1, timeout, el_ev);
}
getdns_return_t
@ -1937,8 +1933,7 @@ getdns_context_clear_timeout(getdns_context* context,
RETURN_IF_NULL(el_ev->timeout_cb, GETDNS_RETURN_GOOD);
if (el_ev->timeout_cb) {
context->extension->vmt->clear_timeout(
context->extension, el_ev);
context->extension->vmt->clear(context->extension, el_ev);
el_ev->timeout_cb = NULL;
}
return GETDNS_RETURN_GOOD;

View File

@ -1,9 +1,7 @@
/**
* \file
* \brief Public interfaces to getdns, include in your application to use getdns API.
* \file libev.c
* \brief Eventloop extension for libev
*
* This source was taken from the original pseudo-implementation by
* Paul Hoffman.
*/
/*
@ -33,11 +31,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/time.h>
#include <stdio.h>
#include "getdns/getdns_ext_libev.h"
#include "config.h"
#include "types-internal.h"
#include "config.h"
#ifdef HAVE_LIBEV_EV_H
#include <libev/ev.h>
@ -45,65 +41,58 @@
#include <ev.h>
#endif
#define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code;
typedef struct getdns_libev {
getdns_eventloop_vmt *vmt;
struct ev_loop *loop;
struct mem_funcs mf;
} getdns_libev;
static getdns_return_t getdns_libev_cleanup(getdns_eventloop *loop);
static getdns_return_t getdns_libev_schedule_read(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *ev);
static getdns_return_t getdns_libev_schedule_timeout
(getdns_eventloop *loop, uint64_t timeout, getdns_eventloop_event *ev);
static getdns_return_t getdns_libev_clear_event
(getdns_eventloop *loop, getdns_eventloop_event *ev);
static getdns_eventloop_vmt getdns_libev_vmt = {
getdns_libev_cleanup,
getdns_libev_schedule_read,
getdns_libev_clear_event,
getdns_libev_schedule_timeout,
getdns_libev_clear_event,
};
getdns_return_t
getdns_extension_set_libev_loop(getdns_context *context,
struct ev_loop *loop)
static void
getdns_libev_run(getdns_eventloop *loop)
{
getdns_libev *ext;
getdns_return_t r;
RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT);
RETURN_IF_NULL(loop, GETDNS_RETURN_INVALID_PARAMETER);
if ((r = getdns_context_detach_eventloop(context)))
return r;
ext = GETDNS_MALLOC(*priv_getdns_context_mf(context), getdns_libev);
ext->vmt = &getdns_libev_vmt;
ext->loop = loop;
ext->mf = *priv_getdns_context_mf(context);
return getdns_context_set_eventloop(context, (getdns_eventloop *)&ext);
(void) ev_run(((getdns_libev *)loop)->loop, 0);
}
static getdns_return_t
static void
getdns_libev_run_once(getdns_eventloop *loop, int blocking)
{
(void) ev_run(((getdns_libev *)loop)->loop,
blocking ? EVRUN_ONCE : EVRUN_NOWAIT);
}
static void
getdns_libev_cleanup(getdns_eventloop *loop)
{
getdns_libev *ext = (getdns_libev *)loop;
GETDNS_FREE(ext->mf, ext);
return GETDNS_RETURN_GOOD;
}
typedef struct io_timer {
ev_io io;
ev_io read;
ev_io write;
ev_timer timer;
} io_timer;
static getdns_return_t
getdns_libev_clear(getdns_eventloop *loop, getdns_eventloop_event *el_ev)
{
getdns_libev *ext = (getdns_libev *)loop;
io_timer *my_ev = (io_timer *)el_ev->ev;
assert(my_ev);
if (el_ev->read_cb)
ev_io_stop(ext->loop, &my_ev->read);
if (el_ev->write_cb)
ev_io_stop(ext->loop, &my_ev->write);
if (el_ev->timeout_cb)
ev_timer_stop(ext->loop, &my_ev->timer);
GETDNS_FREE(ext->mf, el_ev->ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GOOD;
}
static void
getdns_libev_read_cb(struct ev_loop *l, struct ev_io *io, int revents)
{
@ -112,6 +101,14 @@ getdns_libev_read_cb(struct ev_loop *l, struct ev_io *io, int revents)
el_ev->read_cb(el_ev->userarg);
}
static void
getdns_libev_write_cb(struct ev_loop *l, struct ev_io *io, int revents)
{
getdns_eventloop_event *el_ev = (getdns_eventloop_event *)io->data;
assert(el_ev->write_cb);
el_ev->read_cb(el_ev->userarg);
}
static void
getdns_libev_timeout_cb(struct ev_loop *l, struct ev_timer *timer, int revent)
{
@ -121,7 +118,7 @@ getdns_libev_timeout_cb(struct ev_loop *l, struct ev_timer *timer, int revent)
}
static getdns_return_t
getdns_libev_schedule_read(getdns_eventloop *loop,
getdns_libev_schedule(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *el_ev)
{
getdns_libev *ext = (getdns_libev *)loop;
@ -130,11 +127,9 @@ getdns_libev_schedule_read(getdns_eventloop *loop,
ev_timer *my_timer;
ev_tstamp to = ((ev_tstamp)timeout) / 1000;
if (fd < 0) el_ev->read_cb = NULL;
if (timeout == TIMEOUT_FOREVER) el_ev->timeout_cb = NULL;
if (!el_ev->read_cb && !el_ev->timeout_cb)
return GETDNS_RETURN_GOOD; /* Nothing to schedule */
assert(el_ev);
assert(!(el_ev->read_cb || el_ev->write_cb) || fd >= 0);
assert( el_ev->read_cb || el_ev->write_cb || el_ev->timeout_cb);
if (!(my_ev = GETDNS_MALLOC(ext->mf, io_timer)))
return GETDNS_RETURN_MEMORY_ERROR;
@ -142,43 +137,52 @@ getdns_libev_schedule_read(getdns_eventloop *loop,
el_ev->ev = my_ev;
if (el_ev->read_cb) {
my_io = &my_ev->io;
my_io->data = el_ev;
my_io = &my_ev->read;
ev_io_init(my_io, getdns_libev_read_cb, fd, EV_READ);
ev_io_start(ext->loop, &my_ev->io);
my_io->data = el_ev;
ev_io_start(ext->loop, my_io);
}
if (el_ev->write_cb) {
my_io = &my_ev->write;
ev_io_init(my_io, getdns_libev_write_cb, fd, EV_WRITE);
my_io->data = el_ev;
ev_io_start(ext->loop, my_io);
}
if (el_ev->timeout_cb) {
my_timer = &my_ev->timer;
my_timer->data = el_ev;
ev_timer_init(my_timer, getdns_libev_timeout_cb, to, 0);
ev_timer_start(ext->loop, &my_ev->timer);
my_timer->data = el_ev;
ev_timer_start(ext->loop, my_timer);
}
return GETDNS_RETURN_GOOD;
}
static getdns_return_t
getdns_libev_schedule_timeout(getdns_eventloop *loop,
uint64_t timeout, getdns_eventloop_event *el_ev)
getdns_return_t
getdns_extension_set_libev_loop(getdns_context *context,
struct ev_loop *loop)
{
return getdns_libev_schedule_read(loop, -1, timeout, el_ev);
static getdns_eventloop_vmt getdns_libev_vmt = {
getdns_libev_cleanup,
getdns_libev_schedule,
getdns_libev_clear,
getdns_libev_run,
getdns_libev_run_once
};
getdns_libev *ext;
getdns_return_t r;
if (!context)
return GETDNS_RETURN_BAD_CONTEXT;
if (!loop)
return GETDNS_RETURN_INVALID_PARAMETER;
if ((r = getdns_context_detach_eventloop(context)))
return r;
ext = GETDNS_MALLOC(*priv_getdns_context_mf(context), getdns_libev);
ext->vmt = &getdns_libev_vmt;
ext->loop = loop;
ext->mf = *priv_getdns_context_mf(context);
return getdns_context_set_eventloop(context, (getdns_eventloop *)&ext);
}
static getdns_return_t
getdns_libev_clear_event(getdns_eventloop *loop,
getdns_eventloop_event *el_ev)
{
getdns_libev *ext = (getdns_libev *)loop;
io_timer *my_ev = (io_timer *)el_ev->ev;
assert(my_ev);
if (el_ev->read_cb)
ev_io_stop(ext->loop, &my_ev->io);
if (el_ev->timeout_cb)
ev_timer_stop(ext->loop, &my_ev->timer);
GETDNS_FREE(ext->mf, el_ev->ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GOOD;
}

View File

@ -1,9 +1,7 @@
/**
* \file
* \brief Public interfaces to getdns, include in your application to use getdns API.
* \file libevent.c
* \brief Eventloop extension for libevent
*
* This source was taken from the original pseudo-implementation by
* Paul Hoffman.
*/
/*
@ -35,8 +33,8 @@
#include <sys/time.h>
#include "getdns/getdns_ext_libevent.h"
#include "config.h"
#include "types-internal.h"
#include "config.h"
#ifdef HAVE_EVENT2_EVENT_H
# include <event2/event.h>
@ -46,7 +44,6 @@
# define event_free free
# define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg))
#endif
#define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code;
#ifndef HAVE_EVENT_BASE_FREE
#define event_base_free(x) /* nop */
@ -86,49 +83,37 @@ typedef struct getdns_libevent {
struct mem_funcs mf;
} getdns_libevent;
static getdns_return_t getdns_libevent_cleanup(getdns_eventloop *loop);
static getdns_return_t getdns_libevent_schedule_read(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *ev);
static getdns_return_t getdns_libevent_schedule_timeout
(getdns_eventloop *loop, uint64_t timeout, getdns_eventloop_event *ev);
static getdns_return_t getdns_libevent_clear_event
(getdns_eventloop *loop, getdns_eventloop_event *ev);
static getdns_eventloop_vmt getdns_libevent_vmt = {
getdns_libevent_cleanup,
getdns_libevent_schedule_read,
getdns_libevent_clear_event,
getdns_libevent_schedule_timeout,
getdns_libevent_clear_event,
};
getdns_return_t
getdns_extension_set_libevent_base(getdns_context *context,
struct event_base *base)
static void
getdns_libevent_run(getdns_eventloop *loop)
{
getdns_libevent *ext;
getdns_return_t r;
RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT);
RETURN_IF_NULL(base, GETDNS_RETURN_INVALID_PARAMETER);
if ((r = getdns_context_detach_eventloop(context)))
return r;
ext = GETDNS_MALLOC(*priv_getdns_context_mf(context), getdns_libevent);
ext->vmt = &getdns_libevent_vmt;
ext->base = base;
ext->mf = *priv_getdns_context_mf(context);
return getdns_context_set_eventloop(context, (getdns_eventloop *)&ext);
(void) event_base_dispatch(((getdns_libevent *)loop)->base);
}
static getdns_return_t
static void
getdns_libevent_run_once(getdns_eventloop *loop, int blocking)
{
/* Is this possible with libevent? */
}
static void
getdns_libevent_cleanup(getdns_eventloop *loop)
{
getdns_libevent *ext = (getdns_libevent *)loop;
GETDNS_FREE(ext->mf, ext);
}
static getdns_return_t
getdns_libevent_clear(getdns_eventloop *loop, getdns_eventloop_event *el_ev)
{
struct event *my_ev = (struct event *)el_ev->ev;
assert(my_ev);
if (event_del(my_ev) != 0)
return GETDNS_RETURN_GENERIC_ERROR;
event_free(my_ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GOOD;
}
@ -140,6 +125,9 @@ getdns_libevent_callback(evutil_socket_t fd, short bits, void *arg)
if (bits & EV_READ) {
assert(el_ev->read_cb);
el_ev->read_cb(el_ev->userarg);
} else if (bits & EV_WRITE) {
assert(el_ev->write_cb);
el_ev->write_cb(el_ev->userarg);
} else if (bits & EV_TIMEOUT) {
assert(el_ev->timeout_cb);
el_ev->timeout_cb(el_ev->userarg);
@ -148,51 +136,61 @@ getdns_libevent_callback(evutil_socket_t fd, short bits, void *arg)
}
static getdns_return_t
getdns_libevent_schedule_read(getdns_eventloop *loop,
getdns_libevent_schedule(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *el_ev)
{
getdns_libevent *ext = (getdns_libevent *)loop;
struct event *my_ev;
struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
short bits = ((fd >= 0 && el_ev->read_cb ? EV_READ|EV_PERSIST : 0) |
(timeout != TIMEOUT_FOREVER && el_ev->timeout_cb ? EV_TIMEOUT : 0));
if (!bits)
return GETDNS_RETURN_GOOD; /* Nothing to schedule */
assert(el_ev);
assert(!(el_ev->read_cb || el_ev->write_cb) || fd >= 0);
assert( el_ev->read_cb || el_ev->write_cb || el_ev->timeout_cb);
if (!(my_ev = event_new(
ext->base, fd, bits, getdns_libevent_callback, el_ev)))
my_ev = event_new(ext->base, fd, (
(el_ev->read_cb ? EV_READ|EV_PERSIST : 0) |
(el_ev->write_cb ? EV_WRITE|EV_PERSIST : 0) |
(el_ev->timeout_cb ? EV_TIMEOUT : 0)),
getdns_libevent_callback, el_ev);
if (!my_ev)
return GETDNS_RETURN_MEMORY_ERROR;
el_ev->ev = my_ev;
if (event_add(my_ev,
(timeout != TIMEOUT_FOREVER && el_ev->timeout_cb ? &tv : NULL)))
goto error;
return GETDNS_RETURN_GOOD;
error:
event_free(my_ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GENERIC_ERROR;
}
static getdns_return_t
getdns_libevent_schedule_timeout(getdns_eventloop *loop,
uint64_t timeout, getdns_eventloop_event *el_ev)
{
return getdns_libevent_schedule_read(loop, -1, timeout, el_ev);
}
static getdns_return_t
getdns_libevent_clear_event(getdns_eventloop *loop,
getdns_eventloop_event *el_ev)
{
assert(el_ev->ev);
if (event_del(el_ev->ev) != 0)
if (event_add(my_ev, el_ev->timeout_cb ? &tv : NULL)) {
event_free(my_ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GENERIC_ERROR;
event_free(el_ev->ev);
el_ev->ev = NULL;
}
return GETDNS_RETURN_GOOD;
}
getdns_return_t
getdns_extension_set_libevent_base(getdns_context *context,
struct event_base *base)
{
static getdns_eventloop_vmt getdns_libevent_vmt = {
getdns_libevent_cleanup,
getdns_libevent_schedule,
getdns_libevent_clear,
getdns_libevent_run,
getdns_libevent_run_once
};
getdns_libevent *ext;
getdns_return_t r;
if (!context)
return GETDNS_RETURN_BAD_CONTEXT;
if (!base)
return GETDNS_RETURN_INVALID_PARAMETER;
if ((r = getdns_context_detach_eventloop(context)))
return r;
ext = GETDNS_MALLOC(*priv_getdns_context_mf(context), getdns_libevent);
ext->vmt = &getdns_libevent_vmt;
ext->base = base;
ext->mf = *priv_getdns_context_mf(context);
return getdns_context_set_eventloop(context, (getdns_eventloop *)&ext);
}

View File

@ -32,36 +32,159 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "extension/libmini_event.h"
#include "util/mini_event.h"
#include "context.h"
static void
getdns_mini_event_cleanup(getdns_eventloop *loop)
{
getdns_mini_event *ext = (getdns_mini_event *)loop;
getdns_event_base_free(ext->base);
}
void
getdns_mini_event_destroy(getdns_mini_event *ext)
{
assert(ext);
ext->loop.vmt->cleanup(&ext->loop);
GETDNS_FREE(ext->mf, ext);
}
void getdns_handle_timeouts(struct getdns_event_base* base,
struct timeval* now, struct timeval* wait);
int getdns_handle_select(struct getdns_event_base* base, struct timeval* wait);
static getdns_return_t getdns_mini_event_cleanup(getdns_eventloop *loop);
static getdns_return_t getdns_mini_event_schedule_read(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *ev);
static getdns_return_t getdns_mini_event_schedule_timeout
(getdns_eventloop *loop, uint64_t timeout, getdns_eventloop_event *ev);
static getdns_return_t getdns_mini_event_clear_event
(getdns_eventloop *loop, getdns_eventloop_event *ev);
static int
getdns_mini_event_settime(getdns_mini_event *ext)
{
if (gettimeofday(&ext->time_tv, NULL) < 0)
return -1;
ext->time_secs = (time_t)ext->time_tv.tv_sec;
return 0;
}
static getdns_eventloop_vmt getdns_mini_event_vmt = {
getdns_mini_event_cleanup,
getdns_mini_event_schedule_read,
getdns_mini_event_clear_event,
getdns_mini_event_schedule_timeout,
getdns_mini_event_clear_event,
};
static void
getdns_mini_event_run(getdns_eventloop *loop)
{
getdns_mini_event *ext = (getdns_mini_event *)loop;
struct timeval wait;
if (ext->n_events == 0 || getdns_mini_event_settime(ext) < 0)
return;
do (void) getdns_handle_timeouts(ext->base, &ext->time_tv, &wait);
while (!getdns_handle_select(ext->base, &wait) && ext->n_events);
}
static void
getdns_mini_event_run_once(getdns_eventloop *loop, int blocking)
{
static struct timeval immediately = { 0, 0 };
getdns_mini_event *ext = (getdns_mini_event *)loop;
struct timeval wait;
if (blocking) {
if (getdns_mini_event_settime(ext) < 0)
return;
getdns_handle_timeouts(ext->base, &ext->time_tv, &wait);
(void) getdns_handle_select(ext->base, &wait);
} else {
if (getdns_handle_select(ext->base, &immediately) < 0)
return;
getdns_handle_timeouts(ext->base, &ext->time_tv, &wait);
}
}
static getdns_return_t
getdns_mini_event_clear(getdns_eventloop *loop, getdns_eventloop_event *el_ev)
{
getdns_mini_event *ext = (getdns_mini_event *)loop;
assert(el_ev->ev);
if (getdns_event_del(el_ev->ev) != 0)
return GETDNS_RETURN_GENERIC_ERROR;
GETDNS_FREE(ext->mf, el_ev->ev);
el_ev->ev = NULL;
ext->n_events--;
return GETDNS_RETURN_GOOD;
}
static void
getdns_mini_event_callback(int fd, short bits, void *arg)
{
getdns_eventloop_event *el_ev = (getdns_eventloop_event *)arg;
if (bits & EV_READ) {
assert(el_ev->read_cb);
el_ev->read_cb(el_ev->userarg);
} else if (bits & EV_WRITE) {
assert(el_ev->write_cb);
el_ev->write_cb(el_ev->userarg);
} else if (bits & EV_TIMEOUT) {
assert(el_ev->timeout_cb);
el_ev->timeout_cb(el_ev->userarg);
} else
assert(ASSERT_UNREACHABLE);
}
static getdns_return_t
getdns_mini_event_schedule(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *el_ev)
{
getdns_mini_event *ext = (getdns_mini_event *)loop;
struct getdns_event *my_ev;
struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
assert(el_ev);
assert(!(el_ev->read_cb || el_ev->write_cb) || fd >= 0);
assert( el_ev->read_cb || el_ev->write_cb || el_ev->timeout_cb);
if (!(my_ev = GETDNS_MALLOC(ext->mf, struct getdns_event)))
return GETDNS_RETURN_MEMORY_ERROR;
el_ev->ev = my_ev;
getdns_event_set(my_ev, fd, (
(el_ev->read_cb ? EV_READ|EV_PERSIST : 0) |
(el_ev->write_cb ? EV_WRITE|EV_PERSIST : 0) |
(el_ev->timeout_cb ? EV_TIMEOUT : 0)),
getdns_mini_event_callback, el_ev);
if (getdns_mini_event_settime(ext))
goto error;
(void) getdns_event_base_set(ext->base, my_ev);
if (getdns_event_add(my_ev, el_ev->timeout_cb ? &tv : NULL))
goto error;
ext->n_events++;
return GETDNS_RETURN_GOOD;
error:
GETDNS_FREE(ext->mf, my_ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GENERIC_ERROR;
}
getdns_return_t
getdns_mini_event_init(getdns_context *context, getdns_mini_event *ext)
{
if (!context) return GETDNS_RETURN_BAD_CONTEXT;
if (!ext) return GETDNS_RETURN_INVALID_PARAMETER;
static getdns_eventloop_vmt getdns_mini_event_vmt = {
getdns_mini_event_cleanup,
getdns_mini_event_schedule,
getdns_mini_event_clear,
getdns_mini_event_run,
getdns_mini_event_run_once
};
if (!context)
return GETDNS_RETURN_BAD_CONTEXT;
if (!ext)
return GETDNS_RETURN_INVALID_PARAMETER;
ext->loop.vmt = &getdns_mini_event_vmt;
ext->base = getdns_event_init(&ext->time_secs, &ext->time_tv);
@ -81,124 +204,3 @@ getdns_mini_event_create(getdns_context *context, getdns_mini_event **ext)
*ext = GETDNS_MALLOC(context->mf, getdns_mini_event);
return getdns_mini_event_init(context, *ext);
}
void
getdns_mini_event_destroy(getdns_mini_event *ext)
{
if (ext) {
ext->loop.vmt->cleanup(&ext->loop);
GETDNS_FREE(ext->mf, ext);
}
}
static getdns_return_t
getdns_mini_event_cleanup(getdns_eventloop *loop)
{
getdns_mini_event *ext = (getdns_mini_event *)loop;
getdns_event_base_free(ext->base);
return GETDNS_RETURN_GOOD;
}
static int
getdns_mini_event_settime(getdns_mini_event *ext)
{
if (gettimeofday(&ext->time_tv, NULL) < 0)
return -1;
ext->time_secs = (time_t)ext->time_tv.tv_sec;
return 0;
}
/** Call timeouts handlers, and return how long to wait for next one or -1 */
void
getdns_mini_event_handle_timeouts(getdns_mini_event *ext, struct timeval *wait)
{
struct timeval dispose = { 0, 0 };
if (getdns_mini_event_settime(ext) == 0)
getdns_handle_timeouts(
ext->base, &ext->time_tv, wait ? wait : &dispose);
}
/** Call select and callbacks for that */
getdns_return_t
getdns_mini_event_handle_select(getdns_mini_event *ext, struct timeval* wait)
{
static struct timeval immediately = { 0, 0 };
return getdns_handle_select(ext->base, wait ? wait : &immediately)
? GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD;
}
static void
getdns_mini_event_callback(int fd, short bits, void *arg)
{
getdns_eventloop_event *el_ev = (getdns_eventloop_event *)arg;
if (bits & EV_READ) {
assert(el_ev->read_cb);
el_ev->read_cb(el_ev->userarg);
} else if (bits & EV_TIMEOUT) {
assert(el_ev->timeout_cb);
el_ev->timeout_cb(el_ev->userarg);
} else
assert(ASSERT_UNREACHABLE);
}
static getdns_return_t
getdns_mini_event_schedule_read(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *el_ev)
{
getdns_mini_event *ext = (getdns_mini_event *)loop;
struct getdns_event *my_ev;
struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
short bits =
((fd >= 0 && el_ev->read_cb ? EV_READ : 0) |
(timeout!=TIMEOUT_FOREVER && el_ev->timeout_cb ? EV_TIMEOUT : 0));
if (!bits)
return GETDNS_RETURN_GOOD; /* Nothing to schedule */
if (!(my_ev = GETDNS_MALLOC(ext->mf, struct getdns_event)))
return GETDNS_RETURN_MEMORY_ERROR;
el_ev->ev = my_ev;
getdns_event_set(my_ev, fd, bits, getdns_mini_event_callback, el_ev);
if (getdns_mini_event_settime(ext))
goto error;
(void) getdns_event_base_set(ext->base, my_ev);
if (getdns_event_add(my_ev, (
timeout != TIMEOUT_FOREVER && el_ev->timeout_cb ? &tv : NULL)))
goto error;
return GETDNS_RETURN_GOOD;
error:
GETDNS_FREE(ext->mf, my_ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GENERIC_ERROR;
}
static getdns_return_t
getdns_mini_event_schedule_timeout(getdns_eventloop *loop,
uint64_t timeout, getdns_eventloop_event *el_ev)
{
return getdns_mini_event_schedule_read(loop, -1, timeout, el_ev);
}
static getdns_return_t
getdns_mini_event_clear_event(getdns_eventloop *loop,
getdns_eventloop_event *el_ev)
{
getdns_mini_event *ext = (getdns_mini_event *)loop;
assert(el_ev->ev);
if (getdns_event_del(el_ev->ev) != 0)
return GETDNS_RETURN_GENERIC_ERROR;
GETDNS_FREE(ext->mf, el_ev->ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GOOD;
}
/* libmini_event.c */

View File

@ -34,6 +34,7 @@
#ifndef _GETDNS_LIBMINI_EVENT_H_
#define _GETDNS_LIBMINI_EVENT_H_
#include "config.h"
#include "util/mini_event.h"
#include "types-internal.h"
@ -42,6 +43,7 @@ typedef struct getdns_mini_event {
time_t time_secs;
struct timeval time_tv;
struct getdns_event_base *base;
size_t n_events;
struct mem_funcs mf;
} getdns_mini_event;
@ -54,12 +56,4 @@ getdns_mini_event_create(getdns_context *ctxt, getdns_mini_event **mini_event);
void
getdns_mini_event_destroy(getdns_mini_event *mini_event);
/** Call timeouts handlers, and return how long to wait for next one or -1 */
void
getdns_mini_event_handle_timeouts(getdns_mini_event *ext, struct timeval *wait);
/** Call select and callbacks for that */
getdns_return_t
getdns_mini_event_handle_select(getdns_mini_event *ext, struct timeval* wait);
#endif /* _GETDNS_LIBMINI_EVENT_H_ */

View File

@ -1,9 +1,7 @@
/**
* \file
* \brief Public interfaces to getdns, include in your application to use getdns API.
* \file libuv.c
* \brief Eventloop extension for libuv.
*
* This source was taken from the original pseudo-implementation by
* Paul Hoffman.
*/
/*
@ -33,70 +31,66 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/time.h>
#include <stdio.h>
#include <uv.h>
#include "getdns/getdns_ext_libuv.h"
#include "types-internal.h"
#define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code;
typedef struct getdns_libuv {
getdns_eventloop_vmt *vmt;
uv_loop_t *loop;
struct mem_funcs mf;
} getdns_libuv;
static getdns_return_t getdns_libuv_cleanup(getdns_eventloop *loop);
static getdns_return_t getdns_libuv_schedule_read(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *ev);
static getdns_return_t getdns_libuv_schedule_timeout
(getdns_eventloop *loop, uint64_t timeout, getdns_eventloop_event *ev);
static getdns_return_t getdns_libuv_clear_event
(getdns_eventloop *loop, getdns_eventloop_event *ev);
static getdns_eventloop_vmt getdns_libuv_vmt = {
getdns_libuv_cleanup,
getdns_libuv_schedule_read,
getdns_libuv_clear_event,
getdns_libuv_schedule_timeout,
getdns_libuv_clear_event,
};
getdns_return_t
getdns_extension_set_libuv_loop(getdns_context *context, uv_loop_t *loop)
static void
getdns_libuv_run(getdns_eventloop *loop)
{
getdns_libuv *ext;
getdns_return_t r;
RETURN_IF_NULL(context, GETDNS_RETURN_BAD_CONTEXT);
RETURN_IF_NULL(loop, GETDNS_RETURN_INVALID_PARAMETER);
if ((r = getdns_context_detach_eventloop(context)))
return r;
ext = GETDNS_MALLOC(*priv_getdns_context_mf(context), getdns_libuv);
ext->vmt = &getdns_libuv_vmt;
ext->loop = loop;
ext->mf = *priv_getdns_context_mf(context);
return getdns_context_set_eventloop(context, (getdns_eventloop *)&ext);
(void) uv_run(((getdns_libuv *)loop)->loop, UV_RUN_DEFAULT);
}
static getdns_return_t
static void
getdns_libuv_run_once(getdns_eventloop *loop, int blocking)
{
(void) uv_run(((getdns_libuv *)loop)->loop,
blocking ? UV_RUN_ONCE : UV_RUN_NOWAIT);
}
static void
getdns_libuv_cleanup(getdns_eventloop *loop)
{
getdns_libuv *ext = (getdns_libuv *)loop;
GETDNS_FREE(ext->mf, ext);
return GETDNS_RETURN_GOOD;
}
typedef struct poll_timer {
uv_poll_t poll;
uv_poll_t read;
uv_poll_t write;
uv_timer_t timer;
} poll_timer;
static getdns_return_t
getdns_libuv_clear(getdns_eventloop *loop, getdns_eventloop_event *el_ev)
{
getdns_libuv *ext = (getdns_libuv *)loop;
poll_timer *my_ev = (poll_timer *)el_ev->ev;
assert(my_ev);
if (el_ev->read_cb) {
uv_poll_stop(&my_ev->read);
uv_close((uv_handle_t *)&my_ev->read, NULL);
}
if (el_ev->write_cb) {
uv_poll_stop(&my_ev->write);
uv_close((uv_handle_t *)&my_ev->write, NULL);
}
if (el_ev->timeout_cb)
uv_timer_stop(&my_ev->timer);
uv_close((uv_handle_t *)&my_ev->timer, NULL);
GETDNS_FREE(ext->mf, el_ev->ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GOOD;
}
static void
getdns_libuv_read_cb(uv_poll_t *poll, int status, int events)
@ -106,6 +100,14 @@ getdns_libuv_read_cb(uv_poll_t *poll, int status, int events)
el_ev->read_cb(el_ev->userarg);
}
static void
getdns_libuv_write_cb(uv_poll_t *poll, int status, int events)
{
getdns_eventloop_event *el_ev = (getdns_eventloop_event *)poll->data;
assert(el_ev->write_cb);
el_ev->write_cb(el_ev->userarg);
}
static void
getdns_libuv_timeout_cb(uv_timer_t *timer, int status)
{
@ -115,7 +117,7 @@ getdns_libuv_timeout_cb(uv_timer_t *timer, int status)
}
static getdns_return_t
getdns_libuv_schedule_read(getdns_eventloop *loop,
getdns_libuv_schedule(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *el_ev)
{
getdns_libuv *ext = (getdns_libuv *)loop;
@ -123,11 +125,9 @@ getdns_libuv_schedule_read(getdns_eventloop *loop,
uv_poll_t *my_poll;
uv_timer_t *my_timer;
if (fd < 0) el_ev->read_cb = NULL;
if (timeout == TIMEOUT_FOREVER) el_ev->timeout_cb = NULL;
if (!el_ev->read_cb && !el_ev->timeout_cb)
return GETDNS_RETURN_GOOD; /* Nothing to schedule */
assert(el_ev);
assert(!(el_ev->read_cb || el_ev->write_cb) || fd >= 0);
assert( el_ev->read_cb || el_ev->write_cb || el_ev->timeout_cb);
if (!(my_ev = GETDNS_MALLOC(ext->mf, poll_timer)))
return GETDNS_RETURN_MEMORY_ERROR;
@ -135,11 +135,17 @@ getdns_libuv_schedule_read(getdns_eventloop *loop,
el_ev->ev = my_ev;
if (el_ev->read_cb) {
my_poll = &my_ev->poll;
my_poll = &my_ev->read;
uv_poll_init(ext->loop, my_poll, fd);
my_poll->data = el_ev;
uv_poll_start(my_poll, UV_READABLE, getdns_libuv_read_cb);
}
if (el_ev->write_cb) {
my_poll = &my_ev->write;
uv_poll_init(ext->loop, my_poll, fd);
my_poll->data = el_ev;
uv_poll_start(my_poll, UV_WRITABLE, getdns_libuv_write_cb);
}
if (el_ev->timeout_cb) {
my_timer = &my_ev->timer;
uv_timer_init(ext->loop, my_timer);
@ -149,32 +155,31 @@ getdns_libuv_schedule_read(getdns_eventloop *loop,
return GETDNS_RETURN_GOOD;
}
static getdns_return_t
getdns_libuv_schedule_timeout(getdns_eventloop *loop,
uint64_t timeout, getdns_eventloop_event *el_ev)
getdns_return_t
getdns_extension_set_libuv_loop(getdns_context *context, uv_loop_t *loop)
{
return getdns_libuv_schedule_read(loop, -1, timeout, el_ev);
static getdns_eventloop_vmt getdns_libuv_vmt = {
getdns_libuv_cleanup,
getdns_libuv_schedule,
getdns_libuv_clear,
getdns_libuv_run,
getdns_libuv_run_once
};
getdns_libuv *ext;
getdns_return_t r;
if (!context)
return GETDNS_RETURN_BAD_CONTEXT;
if (!loop)
return GETDNS_RETURN_INVALID_PARAMETER;
if ((r = getdns_context_detach_eventloop(context)))
return r;
ext = GETDNS_MALLOC(*priv_getdns_context_mf(context), getdns_libuv);
ext->vmt = &getdns_libuv_vmt;
ext->loop = loop;
ext->mf = *priv_getdns_context_mf(context);
return getdns_context_set_eventloop(context, (getdns_eventloop *)&ext);
}
static getdns_return_t
getdns_libuv_clear_event(getdns_eventloop *loop,
getdns_eventloop_event *el_ev)
{
getdns_libuv *ext = (getdns_libuv *)loop;
poll_timer *my_ev = (poll_timer *)el_ev->ev;
assert(my_ev);
if (el_ev->read_cb) {
uv_poll_stop(&my_ev->poll);
uv_close((uv_handle_t *)&my_ev->poll, NULL);
}
if (el_ev->timeout_cb)
uv_timer_stop(&my_ev->timer);
uv_close((uv_handle_t *)&my_ev->timer, NULL);
GETDNS_FREE(ext->mf, el_ev->ev);
el_ev->ev = NULL;
return GETDNS_RETURN_GOOD;
}

View File

@ -73,6 +73,7 @@ typedef void (*getdns_eventloop_callback)(void *userarg);
typedef struct getdns_eventloop_event {
void *userarg;
getdns_eventloop_callback read_cb;
getdns_eventloop_callback write_cb;
getdns_eventloop_callback timeout_cb;
/* Pointer to the underlying event
@ -86,45 +87,32 @@ typedef struct getdns_eventloop {
getdns_eventloop_vmt *vmt;
} getdns_eventloop;
/* Call the extension to clean up data allocated on initialization. */
typedef getdns_return_t (*getdns_eventloop_cleanup)(getdns_eventloop *loop);
/* A prototype for a method having no arguments and not return value. */
typedef void (*getdns_eventloop_noargs)(getdns_eventloop *loop);
/* Call the extension to schedule an event that will trigger when
* file descriptor fd will become readble.
/* Call the extension to schedule an event
*
* The getdns_eventloop_event must be provided by the caller with the callbacks
* and userarg therein already supplied (by the caller). This function must set
* and userarg therein already supplied (by the caller). This function will set
* the ev pointer (in the getdns_eventloop_event) to refer to the underlying
* (extension) event.
*/
typedef getdns_return_t (*getdns_eventloop_schedule_read)(getdns_eventloop *loop,
typedef getdns_return_t (*getdns_eventloop_schedule)(getdns_eventloop *loop,
int fd, uint64_t timeout, getdns_eventloop_event *ev);
/* Call the extension to free a read event. */
typedef getdns_return_t (*getdns_eventloop_clear_read)
/* Call the extension to clean a scheduled event */
typedef getdns_return_t (*getdns_eventloop_clear)
(getdns_eventloop *loop, getdns_eventloop_event *ev);
/* Call the extension to schedule a timer.
*
* The getdns_eventloop_event must be provided by the caller with the timeout
* callback and userarg therein already supplied (by the caller).
* This function must set the ev pointer (in the getdns_eventloop_event)
* to refer to the underlying (extension) event.
*/
typedef getdns_return_t (*getdns_eventloop_schedule_timeout)
(getdns_eventloop *loop, uint64_t timeout, getdns_eventloop_event *ev);
typedef void (*getdns_eventloop_run_once)(getdns_eventloop *loop,int blocking);
/* Call the extension to free a timer. */
typedef getdns_return_t (*getdns_eventloop_clear_timeout)
(getdns_eventloop *loop, getdns_eventloop_event *ev);
/* Virtual Method Table */
struct getdns_eventloop_vmt {
getdns_eventloop_cleanup cleanup;
getdns_eventloop_schedule_read schedule_read;
getdns_eventloop_clear_read clear_read;
getdns_eventloop_schedule_timeout schedule_timeout;
getdns_eventloop_clear_timeout clear_timeout;
getdns_eventloop_noargs cleanup;
getdns_eventloop_schedule schedule;
getdns_eventloop_clear clear;
getdns_eventloop_noargs run;
getdns_eventloop_run_once run_once;
};
/* set an event loop extension on the context */
@ -136,6 +124,10 @@ getdns_context_set_eventloop(getdns_context* context,
getdns_return_t
getdns_context_detach_eventloop(getdns_context *context);
/* Run the context's event loop until nothing more to do */
void
getdns_context_run(getdns_context *context);
#ifdef __cplusplus
}
#endif