Implement c-ares on unix.

This commit is contained in:
Ryan Dahl 2011-06-28 21:08:17 +02:00
parent 5785961893
commit 796621c773
3 changed files with 193 additions and 31 deletions

View File

@ -35,41 +35,35 @@ int bynamecallbacksig;
int ares_byaddrcallbacks;
int byaddrcallbacksig;
static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) {
uv_buf_t buf;
buf.base = (char*)malloc(size);
buf.len = size;
return buf;
}
static void aresbynamecallback( void *arg,
int status,
int timeouts,
struct hostent *hostent) {
int * iargs;
ASSERT(arg != NULL);
iargs = (int*)arg;
ASSERT(*iargs == bynamecallbacksig);
ASSERT(timeouts == 0);
int * iargs;
ASSERT(arg != NULL);
iargs = (int*)arg;
ASSERT(*iargs == bynamecallbacksig);
ASSERT(timeouts == 0);
ares_bynamecallbacks++;
printf("aresbynamecallback %d\n", ares_bynamecallbacks++);
}
static void aresbyaddrcallback( void *arg,
int status,
int timeouts,
struct hostent *hostent) {
int * iargs;
ASSERT(arg != NULL);
iargs = (int*)arg;
ASSERT(*iargs == byaddrcallbacksig);
ASSERT(timeouts == 0);
int * iargs;
ASSERT(arg != NULL);
iargs = (int*)arg;
ASSERT(*iargs == byaddrcallbacksig);
ASSERT(timeouts == 0);
ares_byaddrcallbacks++;
printf("aresbyaddrcallback %d\n", ares_byaddrcallbacks++);
}
static void prep_tcploopback()
{
static void prep_tcploopback() {
/* for test, use echo server - TCP port TEST_PORT on loopback */
struct sockaddr_in test_server = uv_ip4_addr("127.0.0.1", 0);
int rc;
@ -97,7 +91,7 @@ TEST_IMPL(gethostbyname) {
return 1;
}
uv_init(alloc_cb);
uv_init();
printf("Start basic gethostbyname test\n");
prep_tcploopback();

181
uv-unix.c
View File

@ -19,6 +19,7 @@
*/
#include "uv.h"
#include "uv-common.h"
#include <stddef.h> /* NULL */
#include <stdio.h> /* printf */
@ -44,6 +45,18 @@
static uv_err_t last_err;
struct uv_ares_data_s {
ares_channel channel;
/*
* While the channel is active this timer is called once per second to be sure
* that we're always calling ares_process. See the warning above the
* definition of ares_timeout().
*/
ev_timer timer;
};
static struct uv_ares_data_s ares_data;
void uv__tcp_io(EV_P_ ev_io* watcher, int revents);
void uv__next(EV_P_ ev_idle* watcher, int revents);
@ -67,6 +80,30 @@ void uv_flag_set(uv_handle_t* handle, int flag) {
}
/* TODO Share this code with Windows. */
/* TODO Expose callback to user to handle fatal error like V8 does. */
static void uv_fatal_error(const int errorno, const char* syscall) {
char* buf = NULL;
const char* errmsg;
if (buf) {
errmsg = buf;
} else {
errmsg = "Unknown error";
}
if (syscall) {
fprintf(stderr, "\nlibuv fatal error. %s: (%d) %s\n", syscall, errorno,
errmsg);
} else {
fprintf(stderr, "\nlibuv fatal error. (%d) %s\n", errorno, errmsg);
}
*((char*)NULL) = 0xff; /* Force debug break */
abort();
}
uv_err_t uv_last_error() {
return last_err;
}
@ -1303,17 +1340,145 @@ int64_t uv_timer_get_repeat(uv_timer_t* timer) {
return (int64_t)(1000 * timer->timer_watcher.repeat);
}
/* c-ares integration initialize and terminate */
int uv_ares_init_options(ares_channel *channelptr,
struct ares_options *options,
int optmask) {
int r;
r = ares_init_options(channelptr, options, optmask);
return r;
/*
* This is called once per second by ares_data.timer. It is used to
* constantly callback into c-ares for possibly processing timeouts.
*/
static void uv__ares_timeout(EV_P_ struct ev_timer* watcher, int revents) {
assert(watcher == &ares_data.timer);
assert(revents == EV_TIMER);
assert(!uv_ares_handles_empty());
ares_process_fd(ares_data.channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
}
static void uv__ares_io(EV_P_ struct ev_io* watcher, int revents) {
/* Reset the idle timer */
ev_timer_again(EV_A_ &ares_data.timer);
/* Process DNS responses */
ares_process_fd(ares_data.channel,
revents & EV_READ ? watcher->fd : ARES_SOCKET_BAD,
revents & EV_WRITE ? watcher->fd : ARES_SOCKET_BAD);
}
/* Allocates and returns a new uv_ares_task_t */
static uv_ares_task_t* uv__ares_task_create(int fd) {
uv_ares_task_t* h = malloc(sizeof(uv_ares_task_t));
if (h == NULL) {
uv_fatal_error(ENOMEM, "malloc");
}
h->sock = fd;
ev_io_init(&h->read_watcher, uv__ares_io, fd, EV_READ);
ev_io_init(&h->write_watcher, uv__ares_io, fd, EV_WRITE);
h->read_watcher.data = h;
h->write_watcher.data = h;
}
/* Callback from ares when socket operation is started */
static void uv__ares_sockstate_cb(void* data, ares_socket_t sock,
int read, int write) {
uv_ares_task_t* h = uv_find_ares_handle(sock);
if (read || write) {
if (!h) {
/* New socket */
/* If this is the first socket then start the timer. */
if (!ev_is_active(&ares_data.timer)) {
assert(uv_ares_handles_empty());
ev_timer_again(EV_DEFAULT_UC_ &ares_data.timer);
}
h = uv__ares_task_create(sock);
uv_add_ares_handle(h);
}
if (read) {
ev_io_start(EV_DEFAULT_UC_ &h->read_watcher);
} else {
ev_io_stop(EV_DEFAULT_UC_ &h->read_watcher);
}
if (write) {
ev_io_start(EV_DEFAULT_UC_ &h->write_watcher);
} else {
ev_io_stop(EV_DEFAULT_UC_ &h->write_watcher);
}
} else {
/*
* read == 0 and write == 0 this is c-ares's way of notifying us that
* the socket is now closed. We must free the data associated with
* socket.
*/
assert(h && "When an ares socket is closed we should have a handle for it");
ev_io_stop(EV_DEFAULT_UC_ &h->read_watcher);
ev_io_stop(EV_DEFAULT_UC_ &h->write_watcher);
uv_remove_ares_handle(h);
free(h);
if (uv_ares_handles_empty()) {
ev_timer_stop(EV_DEFAULT_UC_ &ares_data.timer);
}
}
}
/* c-ares integration initialize and terminate */
/* TODO: share this with windows? */
int uv_ares_init_options(ares_channel *channelptr,
struct ares_options *options,
int optmask) {
int rc;
/* only allow single init at a time */
if (ares_data.channel != NULL) {
uv_err_new_artificial(NULL, UV_EALREADY);
return -1;
}
/* set our callback as an option */
options->sock_state_cb = uv__ares_sockstate_cb;
options->sock_state_cb_data = &ares_data;
optmask |= ARES_OPT_SOCK_STATE_CB;
/* We do the call to ares_init_option for caller. */
rc = ares_init_options(channelptr, options, optmask);
/* if success, save channel */
if (rc == ARES_SUCCESS) {
ares_data.channel = *channelptr;
}
/*
* Initialize the timeout timer. The timer won't be started until the
* first socket is opened.
*/
ev_init(&ares_data.timer, uv__ares_timeout);
ares_data.timer.repeat = 1.0;
return rc;
}
/* TODO share this with windows? */
void uv_ares_destroy(ares_channel channel) {
ares_destroy(channel);
/* only allow destroy if did init */
if (ares_data.channel != NULL) {
ev_timer_stop(EV_DEFAULT_UC_ &ares_data.timer);
ares_destroy(channel);
ares_data.channel = NULL;
}
}

View File

@ -102,7 +102,10 @@ typedef struct {
#define UV_ARES_ACTION_PRIVATE_FIELDS /* TODO */
#define UV_ARES_TASK_PRIVATE_FIELDS /* TODO */
#define UV_ARES_TASK_PRIVATE_FIELDS \
int sock; \
ev_io read_watcher; \
ev_io write_watcher;
#define UV_GETADDRINFO_PRIVATE_FIELDS /* TODO */