Implement c-ares on unix.
This commit is contained in:
parent
5785961893
commit
796621c773
@ -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
181
uv-unix.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user