windows, unix: share c-ares glue code
This commit is contained in:
parent
25316a3f92
commit
c06edd4c88
@ -37,15 +37,15 @@ RUNNER_LINKFLAGS=$(LINKFLAGS)
|
||||
RUNNER_LIBS=-lws2_32 -lpsapi -liphlpapi
|
||||
RUNNER_SRC=test/runner-win.c
|
||||
|
||||
uv.a: $(WIN_OBJS) src/uv-common.o $(CARES_OBJS)
|
||||
$(AR) rcs uv.a src/win/*.o src/uv-common.o $(CARES_OBJS)
|
||||
uv.a: $(WIN_OBJS) src/cares.o src/uv-common.o $(CARES_OBJS)
|
||||
$(AR) rcs uv.a $(WIN_OBJS) src/cares.o src/uv-common.o $(CARES_OBJS)
|
||||
|
||||
src/win/%.o: src/win/%.c src/win/internal.h
|
||||
src/%.o: src/%.c include/uv.h include/uv-private/uv-win.h
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
src/win/%.o: src/win/%.c include/uv.h include/uv-private/uv-win.h src/win/internal.h
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
src/uv-common.o: src/uv-common.c include/uv.h include/uv-private/uv-win.h
|
||||
$(CC) $(CFLAGS) -c src/uv-common.c -o src/uv-common.o
|
||||
|
||||
EIO_CPPFLAGS += $(CPPFLAGS)
|
||||
EIO_CPPFLAGS += -DEIO_STACKSIZE=65536
|
||||
EIO_CPPFLAGS += -D_GNU_SOURCE
|
||||
|
||||
@ -21,14 +21,13 @@
|
||||
E=
|
||||
CSTDFLAG=--std=c89 -pedantic -Wall -Wextra -Wno-unused-parameter
|
||||
CFLAGS += -g
|
||||
CPPFLAGS += -Isrc/unix/ev
|
||||
CPPFLAGS += -Isrc -Isrc/unix/ev
|
||||
LINKFLAGS=-lm
|
||||
|
||||
CPPFLAGS += -D_LARGEFILE_SOURCE
|
||||
CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
|
||||
OBJS += src/unix/async.o
|
||||
OBJS += src/unix/cares.o
|
||||
OBJS += src/unix/core.o
|
||||
OBJS += src/unix/dl.o
|
||||
OBJS += src/unix/error.o
|
||||
@ -129,14 +128,14 @@ endif
|
||||
RUNNER_LIBS=
|
||||
RUNNER_SRC=test/runner-unix.c
|
||||
|
||||
uv.a: $(OBJS) src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS)
|
||||
$(AR) rcs uv.a $(OBJS) src/uv-common.o src/unix/uv-eio.o src/unix/ev/ev.o src/unix/eio/eio.o $(CARES_OBJS)
|
||||
uv.a: $(OBJS) src/cares.o src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS)
|
||||
$(AR) rcs uv.a $(OBJS) src/cares.o src/uv-common.o src/unix/uv-eio.o src/unix/ev/ev.o src/unix/eio/eio.o $(CARES_OBJS)
|
||||
|
||||
src/%.o: src/%.c include/uv.h include/uv-private/uv-unix.h
|
||||
$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||
|
||||
src/unix/%.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h
|
||||
$(CC) $(CSTDFLAG) $(CPPFLAGS) -Isrc $(CFLAGS) -c $< -o $@
|
||||
|
||||
src/uv-common.o: src/uv-common.c include/uv.h include/uv-private/uv-unix.h
|
||||
$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c src/uv-common.c -o src/uv-common.o
|
||||
$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||
|
||||
src/unix/ev/ev.o: src/unix/ev/ev.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c src/unix/ev/ev.c -o src/unix/ev/ev.o -DEV_CONFIG_H=\"$(EV_CONFIG)\"
|
||||
|
||||
@ -90,13 +90,6 @@ typedef struct {
|
||||
#endif
|
||||
|
||||
#define UV_LOOP_PRIVATE_FIELDS \
|
||||
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(). \
|
||||
*/ \
|
||||
uv_timer_t timer; \
|
||||
/* Poll result queue */ \
|
||||
eio_channel uv_eio_channel; \
|
||||
struct ev_loop* ev; \
|
||||
|
||||
@ -231,23 +231,17 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
|
||||
uv_idle_t* next_idle_handle; \
|
||||
/* This handle holds the peer sockets for the fast variant of uv_poll_t */ \
|
||||
SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT]; \
|
||||
/* State used by uv_ares. */ \
|
||||
ares_channel ares_chan; \
|
||||
int ares_active_sockets; \
|
||||
uv_timer_t ares_polling_timer; \
|
||||
/* Counter to keep track of active tcp streams */ \
|
||||
unsigned int active_tcp_streams; \
|
||||
/* Counter to keep track of active udp streams */ \
|
||||
unsigned int active_udp_streams;
|
||||
|
||||
#define UV_HANDLE_TYPE_PRIVATE \
|
||||
UV_ARES_EVENT,
|
||||
/* empty */
|
||||
|
||||
#define UV_REQ_TYPE_PRIVATE \
|
||||
/* TODO: remove the req suffix */ \
|
||||
UV_ACCEPT, \
|
||||
UV_ARES_EVENT_REQ, \
|
||||
UV_ARES_CLEANUP_REQ, \
|
||||
UV_FS_EVENT_REQ, \
|
||||
UV_POLL_REQ, \
|
||||
UV_PROCESS_EXIT, \
|
||||
|
||||
@ -1645,8 +1645,13 @@ struct uv_counters_s {
|
||||
|
||||
struct uv_loop_s {
|
||||
UV_LOOP_PRIVATE_FIELDS
|
||||
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(). */
|
||||
uv_timer_t ares_timer; \
|
||||
/* RB_HEAD(uv__ares_tasks, uv_ares_task_t) */
|
||||
struct uv__ares_tasks { uv_ares_task_t* rbh_root; } uv_ares_handles_;
|
||||
struct uv__ares_tasks { uv_ares_task_t* rbh_root; } ares_handles;
|
||||
/* Diagnostic counters */
|
||||
uv_counters_t counters;
|
||||
/* The last error */
|
||||
|
||||
179
src/cares.c
Normal file
179
src/cares.c
Normal file
@ -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 "uv-common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* This is called once per second by loop->timer. It is used to constantly */
|
||||
/* call back into c-ares for possibly processing timeouts. */
|
||||
static void uv__ares_timeout(uv_timer_t* handle, int status) {
|
||||
assert(!uv_ares_handles_empty(handle->loop));
|
||||
ares_process_fd(handle->loop->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
|
||||
}
|
||||
|
||||
|
||||
static void uv__ares_poll_cb(uv_poll_t* watcher, int status, int events) {
|
||||
uv_loop_t* loop = watcher->loop;
|
||||
uv_ares_task_t* task = container_of(watcher, uv_ares_task_t, poll_watcher);
|
||||
|
||||
/* Reset the idle timer */
|
||||
uv_timer_again(&loop->ares_timer);
|
||||
|
||||
if (status < 0) {
|
||||
/* An error happened. Just pretend that the socket is both readable and */
|
||||
/* writable. */
|
||||
ares_process_fd(loop->channel, task->sock, task->sock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Process DNS responses */
|
||||
ares_process_fd(loop->channel,
|
||||
events & UV_READABLE ? task->sock : ARES_SOCKET_BAD,
|
||||
events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD);
|
||||
}
|
||||
|
||||
|
||||
static void uv__ares_poll_close_cb(uv_handle_t* watcher) {
|
||||
uv_ares_task_t* task = container_of(watcher, uv_ares_task_t, poll_watcher);
|
||||
free(task);
|
||||
}
|
||||
|
||||
|
||||
/* Allocates and returns a new uv_ares_task_t */
|
||||
static uv_ares_task_t* uv__ares_task_create(uv_loop_t* loop, ares_socket_t sock) {
|
||||
uv_ares_task_t* task = (uv_ares_task_t*) malloc(sizeof *task);
|
||||
|
||||
if (task == NULL) {
|
||||
/* Out of memory. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task->loop = loop;
|
||||
task->sock = sock;
|
||||
|
||||
if (uv_poll_init_socket(loop, &task->poll_watcher, sock) < 0) {
|
||||
/* This should never happen. */
|
||||
free(task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
/* 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 = (uv_loop_t*) data;
|
||||
uv_ares_task_t* task;
|
||||
|
||||
task = uv_find_ares_handle(loop, sock);
|
||||
|
||||
if (read || write) {
|
||||
if (!task) {
|
||||
/* New socket */
|
||||
|
||||
/* If this is the first socket then start the timer. */
|
||||
if (!uv_is_active((uv_handle_t*) &loop->ares_timer)) {
|
||||
assert(uv_ares_handles_empty(loop));
|
||||
uv_timer_start(&loop->ares_timer, uv__ares_timeout, 1000, 1000);
|
||||
}
|
||||
|
||||
task = uv__ares_task_create(loop, sock);
|
||||
if (task == NULL) {
|
||||
/* This should never happen unless we're out of memory or something */
|
||||
/* is seriously wrong. The socket won't be polled, but the the query */
|
||||
/* will eventually time out. */
|
||||
return;
|
||||
}
|
||||
|
||||
uv_add_ares_handle(loop, task);
|
||||
}
|
||||
|
||||
/* This should never fail. If it fails anyway, the query will eventually */
|
||||
/* time out. */
|
||||
uv_poll_start(&task->poll_watcher,
|
||||
(read ? UV_READABLE : 0) | (write ? UV_WRITABLE : 0),
|
||||
uv__ares_poll_cb);
|
||||
|
||||
} 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(task &&
|
||||
"When an ares socket is closed we should have a handle for it");
|
||||
|
||||
uv_remove_ares_handle(task);
|
||||
uv_close((uv_handle_t*) &task->poll_watcher, uv__ares_poll_close_cb);
|
||||
|
||||
if (uv_ares_handles_empty(loop)) {
|
||||
uv_timer_stop(&loop->ares_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* C-ares integration initialize and terminate */
|
||||
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__set_artificial_error(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. */
|
||||
uv_timer_init(loop, &loop->ares_timer);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) {
|
||||
/* Only allow destroy if did init. */
|
||||
if (loop->channel) {
|
||||
uv_timer_stop(&loop->ares_timer);
|
||||
ares_destroy(channel);
|
||||
loop->channel = NULL;
|
||||
}
|
||||
}
|
||||
179
src/unix/cares.c
179
src/unix/cares.c
@ -1,179 +0,0 @@
|
||||
/* 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 <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
/*
|
||||
* 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(uv_timer_t* handle, int status) {
|
||||
assert(!uv_ares_handles_empty(handle->loop));
|
||||
ares_process_fd(handle->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 = ev_userdata(ev);
|
||||
|
||||
assert(ev == loop->ev);
|
||||
|
||||
/* Reset the idle timer */
|
||||
uv_timer_again(&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(uv_loop_t* loop, int fd) {
|
||||
uv_ares_task_t* h = malloc(sizeof(uv_ares_task_t));
|
||||
|
||||
if (h == NULL) {
|
||||
uv_fatal_error(ENOMEM, "malloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->loop = loop;
|
||||
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;
|
||||
|
||||
assert((uv_loop_t*)loop->timer.data == loop);
|
||||
|
||||
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 (!uv__is_active(&loop->timer)) {
|
||||
assert(uv_ares_handles_empty(loop));
|
||||
uv_timer_start(&loop->timer, uv__ares_timeout, 1000, 1000);
|
||||
}
|
||||
|
||||
h = uv__ares_task_create(loop, 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)) {
|
||||
uv_timer_stop(&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__set_artificial_error(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.
|
||||
*/
|
||||
uv_timer_init(loop, &loop->timer);
|
||||
loop->timer.data = loop;
|
||||
|
||||
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) {
|
||||
uv_timer_stop(&loop->timer);
|
||||
ares_destroy(channel);
|
||||
loop->channel = NULL;
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,6 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h> /* abort */
|
||||
#include <stddef.h> /* offsetof */
|
||||
|
||||
#if __STRICT_ANSI__
|
||||
# define inline __inline
|
||||
@ -64,9 +63,6 @@
|
||||
# define HAVE_KQUEUE 1
|
||||
#endif
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
((type *) ((char *) (ptr) - offsetof(type, member)))
|
||||
|
||||
#define UNREACHABLE() \
|
||||
do { \
|
||||
assert(0 && "unreachable code"); \
|
||||
|
||||
@ -41,7 +41,7 @@ int uv__loop_init(uv_loop_t* loop, int default_loop) {
|
||||
ngx_queue_init(&loop->active_reqs);
|
||||
#endif
|
||||
|
||||
RB_INIT(&loop->uv_ares_handles_);
|
||||
RB_INIT(&loop->ares_handles);
|
||||
ngx_queue_init(&loop->idle_handles);
|
||||
ngx_queue_init(&loop->check_handles);
|
||||
ngx_queue_init(&loop->prepare_handles);
|
||||
|
||||
@ -212,7 +212,7 @@ RB_GENERATE_STATIC(uv__ares_tasks, uv_ares_task_s, node, cmp_ares_tasks)
|
||||
/* add ares handle to list */
|
||||
void uv_add_ares_handle(uv_loop_t* loop, uv_ares_task_t* handle) {
|
||||
assert(loop == handle->loop);
|
||||
RB_INSERT(uv__ares_tasks, &loop->uv_ares_handles_, handle);
|
||||
RB_INSERT(uv__ares_tasks, &loop->ares_handles, handle);
|
||||
}
|
||||
|
||||
|
||||
@ -220,19 +220,19 @@ void uv_add_ares_handle(uv_loop_t* loop, uv_ares_task_t* handle) {
|
||||
uv_ares_task_t* uv_find_ares_handle(uv_loop_t* loop, ares_socket_t sock) {
|
||||
uv_ares_task_t handle;
|
||||
handle.sock = sock;
|
||||
return RB_FIND(uv__ares_tasks, &loop->uv_ares_handles_, &handle);
|
||||
return RB_FIND(uv__ares_tasks, &loop->ares_handles, &handle);
|
||||
}
|
||||
|
||||
|
||||
/* remove ares handle in list */
|
||||
void uv_remove_ares_handle(uv_ares_task_t* handle) {
|
||||
RB_REMOVE(uv__ares_tasks, &handle->loop->uv_ares_handles_, handle);
|
||||
RB_REMOVE(uv__ares_tasks, &handle->loop->ares_handles, handle);
|
||||
}
|
||||
|
||||
|
||||
/* Returns 1 if the uv_ares_handles_ list is empty. 0 otherwise. */
|
||||
/* Returns 1 if the ares_handles list is empty. 0 otherwise. */
|
||||
int uv_ares_handles_empty(uv_loop_t* loop) {
|
||||
return RB_EMPTY(&loop->uv_ares_handles_);
|
||||
return RB_EMPTY(&loop->ares_handles);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
#define UV_COMMON_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "tree.h"
|
||||
@ -35,6 +37,9 @@
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
#define container_of(ptr, type, member) \
|
||||
((type *) ((char *) (ptr) - offsetof(type, member)))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define UNUSED /* empty */
|
||||
#else
|
||||
@ -54,17 +59,8 @@ enum {
|
||||
|
||||
struct uv_ares_task_s {
|
||||
UV_HANDLE_FIELDS
|
||||
#if _WIN32
|
||||
struct uv_req_s ares_req;
|
||||
SOCKET sock;
|
||||
HANDLE h_wait;
|
||||
WSAEVENT h_event;
|
||||
HANDLE h_close_event;
|
||||
#else
|
||||
int sock;
|
||||
ev_io read_watcher;
|
||||
ev_io write_watcher;
|
||||
#endif
|
||||
ares_socket_t sock;
|
||||
uv_poll_t poll_watcher;
|
||||
RB_ENTRY(uv_ares_task_s) node;
|
||||
};
|
||||
|
||||
|
||||
288
src/win/cares.c
288
src/win/cares.c
@ -1,288 +0,0 @@
|
||||
/* 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 <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Subclass of uv_handle_t. Used for integration of c-ares.
|
||||
*/
|
||||
struct uv_ares_action_s {
|
||||
UV_HANDLE_FIELDS
|
||||
struct uv_req_s ares_req;
|
||||
SOCKET sock;
|
||||
int read;
|
||||
int write;
|
||||
};
|
||||
|
||||
|
||||
/* default timeout per socket request if ares does not specify value */
|
||||
/* use 20 sec */
|
||||
#define ARES_TIMEOUT_MS 20000
|
||||
|
||||
|
||||
/* thread pool callback when socket is signalled */
|
||||
static void CALLBACK uv_ares_socksignal_tp(void* parameter,
|
||||
BOOLEAN timerfired) {
|
||||
WSANETWORKEVENTS network_events;
|
||||
uv_ares_task_t* sockhandle;
|
||||
uv_ares_action_t* selhandle;
|
||||
uv_req_t* uv_ares_req;
|
||||
uv_loop_t* loop;
|
||||
|
||||
assert(parameter != NULL);
|
||||
|
||||
if (parameter != NULL) {
|
||||
sockhandle = (uv_ares_task_t*) parameter;
|
||||
loop = sockhandle->loop;
|
||||
|
||||
/* clear socket status for this event */
|
||||
/* do not fail if error, thread may run after socket close */
|
||||
/* The code assumes that c-ares will write all pending data in the */
|
||||
/* callback, unless the socket would block. We can clear the state here */
|
||||
/* to avoid unnecessary signals. */
|
||||
WSAEnumNetworkEvents(sockhandle->sock,
|
||||
sockhandle->h_event,
|
||||
&network_events);
|
||||
|
||||
/* setup new handle */
|
||||
selhandle = (uv_ares_action_t*)malloc(sizeof(uv_ares_action_t));
|
||||
if (selhandle == NULL) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
|
||||
}
|
||||
selhandle->type = UV_ARES_EVENT;
|
||||
selhandle->close_cb = NULL;
|
||||
selhandle->data = sockhandle->data;
|
||||
selhandle->sock = sockhandle->sock;
|
||||
selhandle->read =
|
||||
(network_events.lNetworkEvents & (FD_READ | FD_CONNECT)) ? 1 : 0;
|
||||
selhandle->write =
|
||||
(network_events.lNetworkEvents & (FD_WRITE | FD_CONNECT)) ? 1 : 0;
|
||||
|
||||
uv_ares_req = &selhandle->ares_req;
|
||||
uv_req_init(loop, uv_ares_req);
|
||||
uv_ares_req->type = UV_ARES_EVENT_REQ;
|
||||
uv_ares_req->data = selhandle;
|
||||
|
||||
/* post ares needs to called */
|
||||
POST_COMPLETION_FOR_REQ(loop, uv_ares_req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* periodically call ares to check for timeouts */
|
||||
static void uv_ares_poll(uv_timer_t* handle, int status) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
if (loop->ares_chan != NULL && loop->ares_active_sockets > 0) {
|
||||
ares_process_fd(loop->ares_chan, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* callback from ares when socket operation is started */
|
||||
static void uv_ares_sockstate_cb(void *data, ares_socket_t sock, int read,
|
||||
int write) {
|
||||
/* look to see if we have a handle for this socket in our list */
|
||||
uv_loop_t* loop = (uv_loop_t*) data;
|
||||
uv_ares_task_t* uv_handle_ares = uv_find_ares_handle(loop, sock);
|
||||
|
||||
if (read == 0 && write == 0) {
|
||||
/* if read and write are 0, cleanup existing data */
|
||||
/* The code assumes that c-ares does a callback with read = 0 and */
|
||||
/* write = 0 when the socket is closed. After we receive this we stop */
|
||||
/* monitoring the socket. */
|
||||
if (uv_handle_ares != NULL) {
|
||||
uv_req_t* uv_ares_req;
|
||||
|
||||
uv_handle_ares->h_close_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
/* remove Wait */
|
||||
if (uv_handle_ares->h_wait) {
|
||||
UnregisterWaitEx(uv_handle_ares->h_wait,
|
||||
uv_handle_ares->h_close_event);
|
||||
uv_handle_ares->h_wait = NULL;
|
||||
}
|
||||
|
||||
/* detach socket from the event */
|
||||
WSAEventSelect(sock, NULL, 0);
|
||||
if (uv_handle_ares->h_event != WSA_INVALID_EVENT) {
|
||||
WSACloseEvent(uv_handle_ares->h_event);
|
||||
uv_handle_ares->h_event = WSA_INVALID_EVENT;
|
||||
}
|
||||
/* remove handle from list */
|
||||
uv_remove_ares_handle(uv_handle_ares);
|
||||
uv__handle_stop(uv_handle_ares);
|
||||
|
||||
/* Post request to cleanup the Task */
|
||||
uv_ares_req = &uv_handle_ares->ares_req;
|
||||
uv_req_init(loop, uv_ares_req);
|
||||
uv_ares_req->type = UV_ARES_CLEANUP_REQ;
|
||||
uv_ares_req->data = uv_handle_ares;
|
||||
|
||||
/* post ares done with socket - finish cleanup when all threads done. */
|
||||
POST_COMPLETION_FOR_REQ(loop, uv_ares_req);
|
||||
} else {
|
||||
assert(0);
|
||||
uv_fatal_error(ERROR_INVALID_DATA, "ares_SockStateCB");
|
||||
}
|
||||
} else {
|
||||
if (uv_handle_ares == NULL) {
|
||||
/* setup new handle */
|
||||
/* The code assumes that c-ares will call us when it has an open socket.
|
||||
We need to call into c-ares when there is something to read,
|
||||
or when it becomes writable. */
|
||||
uv_handle_ares = (uv_ares_task_t*)malloc(sizeof(uv_ares_task_t));
|
||||
if (uv_handle_ares == NULL) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
|
||||
}
|
||||
uv_handle_ares->type = UV_ARES_TASK;
|
||||
uv_handle_ares->close_cb = NULL;
|
||||
uv_handle_ares->loop = loop;
|
||||
uv_handle_ares->data = loop;
|
||||
uv_handle_ares->sock = sock;
|
||||
uv_handle_ares->h_wait = NULL;
|
||||
uv_handle_ares->flags = 0;
|
||||
|
||||
/* create an event to wait on socket signal */
|
||||
uv_handle_ares->h_event = WSACreateEvent();
|
||||
if (uv_handle_ares->h_event == WSA_INVALID_EVENT) {
|
||||
uv_fatal_error(WSAGetLastError(), "WSACreateEvent");
|
||||
}
|
||||
|
||||
/* tie event to socket */
|
||||
if (SOCKET_ERROR == WSAEventSelect(sock,
|
||||
uv_handle_ares->h_event,
|
||||
FD_READ | FD_WRITE | FD_CONNECT)) {
|
||||
uv_fatal_error(WSAGetLastError(), "WSAEventSelect");
|
||||
}
|
||||
|
||||
/* add handle to list */
|
||||
uv_add_ares_handle(loop, uv_handle_ares);
|
||||
uv__handle_start(uv_handle_ares);
|
||||
|
||||
/*
|
||||
* we have a single polling timer for all ares sockets.
|
||||
* This is preferred to using ares_timeout. See ares_timeout.c warning.
|
||||
* if timer is not running start it, and keep socket count
|
||||
*/
|
||||
if (loop->ares_active_sockets == 0) {
|
||||
uv_timer_init(loop, &loop->ares_polling_timer);
|
||||
uv_timer_start(&loop->ares_polling_timer, uv_ares_poll, 1000L, 1000L);
|
||||
}
|
||||
loop->ares_active_sockets++;
|
||||
|
||||
/* specify thread pool function to call when event is signaled */
|
||||
if (RegisterWaitForSingleObject(&uv_handle_ares->h_wait,
|
||||
uv_handle_ares->h_event,
|
||||
uv_ares_socksignal_tp,
|
||||
(void*)uv_handle_ares,
|
||||
INFINITE,
|
||||
WT_EXECUTEINWAITTHREAD) == 0) {
|
||||
uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
|
||||
}
|
||||
} else {
|
||||
/* found existing handle. */
|
||||
assert(uv_handle_ares->type == UV_ARES_TASK);
|
||||
assert(uv_handle_ares->data != NULL);
|
||||
assert(uv_handle_ares->h_event != WSA_INVALID_EVENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* called via uv_poll when ares completion port signaled */
|
||||
void uv_process_ares_event_req(uv_loop_t* loop, uv_ares_action_t* handle,
|
||||
uv_req_t* req) {
|
||||
ares_process_fd(loop->ares_chan,
|
||||
handle->read ? handle->sock : INVALID_SOCKET,
|
||||
handle->write ? handle->sock : INVALID_SOCKET);
|
||||
|
||||
/* release handle for select here */
|
||||
free(handle);
|
||||
}
|
||||
|
||||
|
||||
/* called via uv_poll when ares is finished with socket */
|
||||
void uv_process_ares_cleanup_req(uv_loop_t* loop, uv_ares_task_t* handle,
|
||||
uv_req_t* req) {
|
||||
/* check for event complete without waiting */
|
||||
unsigned int signaled = WaitForSingleObject(handle->h_close_event, 0);
|
||||
|
||||
if (signaled != WAIT_TIMEOUT) {
|
||||
uv__handle_stop(loop);
|
||||
|
||||
/* close event handle and free uv handle memory */
|
||||
CloseHandle(handle->h_close_event);
|
||||
free(handle);
|
||||
|
||||
/* decrement active count. if it becomes 0 stop polling */
|
||||
if (loop->ares_active_sockets > 0) {
|
||||
loop->ares_active_sockets--;
|
||||
if (loop->ares_active_sockets == 0) {
|
||||
uv_close((uv_handle_t*) &loop->ares_polling_timer, NULL);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* still busy - repost and try again */
|
||||
POST_COMPLETION_FOR_REQ(loop, req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* set ares SOCK_STATE callback to our handler */
|
||||
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->ares_chan != NULL) {
|
||||
return UV_EALREADY;
|
||||
}
|
||||
|
||||
/* 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->ares_chan = *channelptr;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* release memory */
|
||||
void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) {
|
||||
/* only allow destroy if did init */
|
||||
if (loop->ares_chan != NULL) {
|
||||
ares_destroy(channel);
|
||||
loop->ares_chan = NULL;
|
||||
}
|
||||
}
|
||||
@ -79,7 +79,6 @@ static void uv_loop_init(uv_loop_t* loop) {
|
||||
loop->endgame_handles = NULL;
|
||||
|
||||
RB_INIT(&loop->timers);
|
||||
RB_INIT(&loop->uv_ares_handles_);
|
||||
|
||||
loop->check_handles = NULL;
|
||||
loop->prepare_handles = NULL;
|
||||
@ -91,8 +90,8 @@ static void uv_loop_init(uv_loop_t* loop) {
|
||||
|
||||
memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets);
|
||||
|
||||
loop->ares_active_sockets = 0;
|
||||
loop->ares_chan = NULL;
|
||||
loop->channel = NULL;
|
||||
RB_INIT(&loop->ares_handles);
|
||||
|
||||
loop->active_tcp_streams = 0;
|
||||
loop->active_udp_streams = 0;
|
||||
|
||||
@ -312,16 +312,6 @@ void uv_process_close(uv_loop_t* loop, uv_process_t* handle);
|
||||
void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle);
|
||||
|
||||
|
||||
/*
|
||||
* C-ares integration
|
||||
*/
|
||||
typedef struct uv_ares_action_s uv_ares_action_t;
|
||||
|
||||
void uv_process_ares_event_req(uv_loop_t* loop, uv_ares_action_t* handle,
|
||||
uv_req_t* req);
|
||||
void uv_process_ares_cleanup_req(uv_loop_t* loop, uv_ares_task_t* handle,
|
||||
uv_req_t* req);
|
||||
|
||||
/*
|
||||
* Getaddrinfo
|
||||
*/
|
||||
|
||||
@ -138,14 +138,6 @@ void uv_process_reqs(uv_loop_t* loop) {
|
||||
uv_process_poll_req(loop, (uv_poll_t*) req->data, req);
|
||||
break;
|
||||
|
||||
case UV_ARES_EVENT_REQ:
|
||||
uv_process_ares_event_req(loop, (uv_ares_action_t*) req->data, req);
|
||||
break;
|
||||
|
||||
case UV_ARES_CLEANUP_REQ:
|
||||
uv_process_ares_cleanup_req(loop, (uv_ares_task_t*) req->data, req);
|
||||
break;
|
||||
|
||||
case UV_GETADDRINFO:
|
||||
uv_process_getaddrinfo_req(loop, (uv_getaddrinfo_t*) req);
|
||||
break;
|
||||
|
||||
3
uv.gyp
3
uv.gyp
@ -49,6 +49,7 @@
|
||||
'include/uv.h',
|
||||
'include/uv-private/ngx-queue.h',
|
||||
'include/uv-private/tree.h',
|
||||
'src/cares.c',
|
||||
'src/uv-common.c',
|
||||
'src/uv-common.h',
|
||||
'src/ares/ares_cancel.c',
|
||||
@ -134,7 +135,6 @@
|
||||
'src/ares/ares_iphlpapi.h',
|
||||
'src/ares/ares_platform.c',
|
||||
'src/win/async.c',
|
||||
'src/win/cares.c',
|
||||
'src/win/core.c',
|
||||
'src/win/dl.c',
|
||||
'src/win/error.c',
|
||||
@ -182,7 +182,6 @@
|
||||
'include/uv-private/ev.h',
|
||||
'include/uv-private/uv-unix.h',
|
||||
'src/unix/async.c',
|
||||
'src/unix/cares.c',
|
||||
'src/unix/core.c',
|
||||
'src/unix/dl.c',
|
||||
'src/unix/eio/ecb.h',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user