From e97958ac3bf644cda9b0aee7b94b7ab93415d21d Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 31 Aug 2011 11:14:36 -0700 Subject: [PATCH] unix: Split out c-ares integration into separate file --- config-unix.mk | 7 +- src/unix/cares.c | 179 ++++++++++++++++++++++++++++++++++++++++++++ src/unix/internal.h | 5 +- src/uv-unix.c | 154 +------------------------------------ uv.gyp | 1 + 5 files changed, 190 insertions(+), 156 deletions(-) create mode 100644 src/unix/cares.c diff --git a/config-unix.mk b/config-unix.mk index 0ae9acca..32adcb23 100644 --- a/config-unix.mk +++ b/config-unix.mk @@ -85,8 +85,8 @@ endif RUNNER_LIBS= RUNNER_SRC=test/runner-unix.c -uv.a: src/uv-unix.o src/unix/fs.o src/unix/udp.o src/uv-common.o src/uv-platform.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS) - $(AR) rcs uv.a src/uv-unix.o src/unix/fs.o src/unix/udp.o src/uv-platform.o src/uv-common.o src/unix/uv-eio.o src/unix/ev/ev.o \ +uv.a: src/uv-unix.o src/unix/fs.o src/unix/udp.o src/unix/cares.o src/uv-common.o src/uv-platform.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS) + $(AR) rcs uv.a src/uv-unix.o src/unix/fs.o src/unix/udp.o src/unix/cares.o src/uv-platform.o src/uv-common.o src/unix/uv-eio.o src/unix/ev/ev.o \ src/unix/eio/eio.o $(CARES_OBJS) src/uv-platform.o: src/unix/$(UV_OS_FILE) include/uv.h include/uv-private/uv-unix.h @@ -98,6 +98,9 @@ src/uv-unix.o: src/uv-unix.c include/uv.h include/uv-private/uv-unix.h src/unix/ src/unix/fs.o: src/unix/fs.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h $(CC) $(CSTDFLAG) $(CPPFLAGS) -Isrc/ $(CFLAGS) -c src/unix/fs.c -o src/unix/fs.o +src/unix/cares.o: src/unix/cares.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h + $(CC) $(CSTDFLAG) $(CPPFLAGS) -Isrc/ $(CFLAGS) -c src/unix/cares.c -o src/unix/cares.o + src/unix/udp.o: src/unix/udp.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h $(CC) $(CSTDFLAG) $(CPPFLAGS) -Isrc/ $(CFLAGS) -c src/unix/udp.c -o src/unix/udp.o diff --git a/src/unix/cares.c b/src/unix/cares.c new file mode 100644 index 00000000..f5e8da3b --- /dev/null +++ b/src/unix/cares.c @@ -0,0 +1,179 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + + +#define container_of ngx_queue_data + +/* + * This is called once per second by loop->timer. It is used to + * constantly callback into c-ares for possibly processing timeouts. + */ +static void uv__ares_timeout(struct ev_loop* ev, struct ev_timer* watcher, + int revents) { + uv_loop_t* loop = container_of(ev, uv_loop_t, ev); + assert(watcher == &loop->timer); + assert(revents == EV_TIMER); + assert(!uv_ares_handles_empty(loop)); + ares_process_fd(loop->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); +} + + +static void uv__ares_io(struct ev_loop* ev, struct ev_io* watcher, + int revents) { + uv_loop_t* loop = container_of(ev, uv_loop_t, ev); + + /* Reset the idle timer */ + ev_timer_again(ev, &loop->timer); + + /* Process DNS responses */ + ares_process_fd(loop->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; + + return 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_loop_t* loop = data; + uv_ares_task_t* h; + + h = uv_find_ares_handle(loop, sock); + + if (read || write) { + if (!h) { + /* New socket */ + + /* If this is the first socket then start the timer. */ + if (!ev_is_active(&loop->timer)) { + assert(uv_ares_handles_empty(loop)); + ev_timer_again(loop->ev, &loop->timer); + } + + h = uv__ares_task_create(sock); + uv_add_ares_handle(loop, h); + } + + if (read) { + ev_io_start(loop->ev, &h->read_watcher); + } else { + ev_io_stop(loop->ev, &h->read_watcher); + } + + if (write) { + ev_io_start(loop->ev, &h->write_watcher); + } else { + ev_io_stop(loop->ev, &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(loop->ev, &h->read_watcher); + ev_io_stop(loop->ev, &h->write_watcher); + + uv_remove_ares_handle(h); + free(h); + + if (uv_ares_handles_empty(loop)) { + ev_timer_stop(loop->ev, &loop->timer); + } + } +} + + +/* c-ares integration initialize and terminate */ +/* TODO: share this with windows? */ +int uv_ares_init_options(uv_loop_t* loop, ares_channel *channelptr, + struct ares_options *options, int optmask) { + int rc; + + /* only allow single init at a time */ + if (loop->channel != NULL) { + uv_err_new_artificial(loop, UV_EALREADY); + return -1; + } + + /* set our callback as an option */ + options->sock_state_cb = uv__ares_sockstate_cb; + options->sock_state_cb_data = loop; + 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) { + loop->channel = *channelptr; + } + + /* + * Initialize the timeout timer. The timer won't be started until the + * first socket is opened. + */ + ev_init(&loop->timer, uv__ares_timeout); + loop->timer.repeat = 1.0; + + return rc; +} + + +/* TODO share this with windows? */ +void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) { + /* only allow destroy if did init */ + if (loop->channel != NULL) { + ev_timer_stop(loop->ev, &loop->timer); + ares_destroy(channel); + loop->channel = NULL; + } +} diff --git a/src/unix/internal.h b/src/unix/internal.h index 50983a70..a8806c60 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -27,12 +27,15 @@ int uv__close(int fd); void uv__req_init(uv_req_t*); +void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, uv_handle_type type); + uv_err_t uv_err_new(uv_loop_t* loop, int sys_error); uv_err_t uv_err_new_artificial(uv_loop_t* loop, int code); +void uv_fatal_error(const int errorno, const char* syscall); + int uv__nonblock(int fd, int set) __attribute__((unused)); int uv__cloexec(int fd, int set) __attribute__((unused)); int uv__socket(int domain, int type, int protocol); -void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, uv_handle_type type); /* udp */ void uv__udp_destroy(uv_udp_t* handle); diff --git a/src/uv-unix.c b/src/uv-unix.c index b3433eef..a6c62e2e 100644 --- a/src/uv-unix.c +++ b/src/uv-unix.c @@ -85,8 +85,6 @@ extern char **environ; # endif -#define container_of ngx_queue_data - static uv_loop_t default_loop_struct; static uv_loop_t* default_loop_ptr; @@ -138,7 +136,7 @@ void uv_init() { /* 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) { +void uv_fatal_error(const int errorno, const char* syscall) { char* buf = NULL; const char* errmsg; @@ -1621,156 +1619,6 @@ int64_t uv_timer_get_repeat(uv_timer_t* timer) { } -/* - * This is called once per second by loop->timer. It is used to - * constantly callback into c-ares for possibly processing timeouts. - */ -static void uv__ares_timeout(struct ev_loop* ev, struct ev_timer* watcher, - int revents) { - uv_loop_t* loop = container_of(ev, uv_loop_t, ev); - assert(watcher == &loop->timer); - assert(revents == EV_TIMER); - assert(!uv_ares_handles_empty(loop)); - ares_process_fd(loop->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); -} - - -static void uv__ares_io(struct ev_loop* ev, struct ev_io* watcher, - int revents) { - uv_loop_t* loop = container_of(ev, uv_loop_t, ev); - - /* Reset the idle timer */ - ev_timer_again(ev, &loop->timer); - - /* Process DNS responses */ - ares_process_fd(loop->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; - - return 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_loop_t* loop = data; - uv_ares_task_t* h; - - h = uv_find_ares_handle(loop, sock); - - if (read || write) { - if (!h) { - /* New socket */ - - /* If this is the first socket then start the timer. */ - if (!ev_is_active(&loop->timer)) { - assert(uv_ares_handles_empty(loop)); - ev_timer_again(loop->ev, &loop->timer); - } - - h = uv__ares_task_create(sock); - uv_add_ares_handle(loop, h); - } - - if (read) { - ev_io_start(loop->ev, &h->read_watcher); - } else { - ev_io_stop(loop->ev, &h->read_watcher); - } - - if (write) { - ev_io_start(loop->ev, &h->write_watcher); - } else { - ev_io_stop(loop->ev, &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(loop->ev, &h->read_watcher); - ev_io_stop(loop->ev, &h->write_watcher); - - uv_remove_ares_handle(h); - free(h); - - if (uv_ares_handles_empty(loop)) { - ev_timer_stop(loop->ev, &loop->timer); - } - } -} - - -/* c-ares integration initialize and terminate */ -/* TODO: share this with windows? */ -int uv_ares_init_options(uv_loop_t* loop, ares_channel *channelptr, - struct ares_options *options, int optmask) { - int rc; - - /* only allow single init at a time */ - if (loop->channel != NULL) { - uv_err_new_artificial(loop, UV_EALREADY); - return -1; - } - - /* set our callback as an option */ - options->sock_state_cb = uv__ares_sockstate_cb; - options->sock_state_cb_data = loop; - 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) { - loop->channel = *channelptr; - } - - /* - * Initialize the timeout timer. The timer won't be started until the - * first socket is opened. - */ - ev_init(&loop->timer, uv__ares_timeout); - loop->timer.repeat = 1.0; - - return rc; -} - - -/* TODO share this with windows? */ -void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) { - /* only allow destroy if did init */ - if (loop->channel != NULL) { - ev_timer_stop(loop->ev, &loop->timer); - ares_destroy(channel); - loop->channel = NULL; - } -} - - static int uv_getaddrinfo_done(eio_req* req) { uv_getaddrinfo_t* handle = req->data; diff --git a/uv.gyp b/uv.gyp index 1b283683..c88ebf89 100644 --- a/uv.gyp +++ b/uv.gyp @@ -146,6 +146,7 @@ 'src/unix/uv-eio.h', 'src/unix/fs.c', 'src/unix/udp.c', + 'src/unix/cares.c', 'src/unix/internal.h', 'src/unix/eio/ecb.h', 'src/unix/eio/eio.c',