mirror of https://github.com/lukechilds/node.git
Ben Noordhuis
13 years ago
12 changed files with 448 additions and 210 deletions
@ -0,0 +1,225 @@ |
|||
/* 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 "tree.h" |
|||
#include "uv-common.h" |
|||
|
|||
#include <assert.h> |
|||
#include <errno.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
|
|||
|
|||
struct uv_ares_task_s { |
|||
UV_HANDLE_FIELDS |
|||
ares_socket_t sock; |
|||
uv_poll_t poll_watcher; |
|||
RB_ENTRY(uv_ares_task_s) node; |
|||
}; |
|||
|
|||
|
|||
static int cmp_ares_tasks(const uv_ares_task_t* a, const uv_ares_task_t* b) { |
|||
if (a->sock < b->sock) return -1; |
|||
if (a->sock > b->sock) return 1; |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
RB_GENERATE_STATIC(uv__ares_tasks, uv_ares_task_s, node, cmp_ares_tasks) |
|||
|
|||
|
|||
/* Add ares handle to list. */ |
|||
static void uv_add_ares_handle(uv_loop_t* loop, uv_ares_task_t* handle) { |
|||
assert(loop == handle->loop); |
|||
RB_INSERT(uv__ares_tasks, &loop->ares_handles, handle); |
|||
} |
|||
|
|||
|
|||
/* Find matching ares handle in list. */ |
|||
static 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->ares_handles, &handle); |
|||
} |
|||
|
|||
|
|||
/* Remove ares handle from list. */ |
|||
static void uv_remove_ares_handle(uv_ares_task_t* handle) { |
|||
RB_REMOVE(uv__ares_tasks, &handle->loop->ares_handles, handle); |
|||
} |
|||
|
|||
|
|||
/* Returns 1 if the ares_handles list is empty, 0 otherwise. */ |
|||
static int uv_ares_handles_empty(uv_loop_t* loop) { |
|||
return RB_EMPTY(&loop->ares_handles); |
|||
} |
|||
|
|||
|
|||
/* 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; |
|||
} |
|||
} |
Loading…
Reference in new issue