PageRenderTime 28ms CodeModel.GetById 10ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/win/cares.c

http://github.com/joyent/libuv
C | 289 lines | 170 code | 46 blank | 73 comment | 47 complexity | 18f01b9916145ed79bc652d9d4b4d085 MD5 | raw file
  1/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
  2 *
  3 * Permission is hereby granted, free of charge, to any person obtaining a copy
  4 * of this software and associated documentation files (the "Software"), to
  5 * deal in the Software without restriction, including without limitation the
  6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  7 * sell copies of the Software, and to permit persons to whom the Software is
  8 * furnished to do so, subject to the following conditions:
  9 *
 10 * The above copyright notice and this permission notice shall be included in
 11 * all copies or substantial portions of the Software.
 12 *
 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 19 * IN THE SOFTWARE.
 20 */
 21
 22#include <assert.h>
 23
 24#include "uv.h"
 25#include "../uv-common.h"
 26#include "internal.h"
 27
 28
 29/*
 30 * Subclass of uv_handle_t. Used for integration of c-ares.
 31 */
 32struct uv_ares_action_s {
 33  UV_HANDLE_FIELDS
 34  struct uv_req_s ares_req;
 35  SOCKET sock;
 36  int read;
 37  int write;
 38};
 39
 40
 41/* default timeout per socket request if ares does not specify value */
 42/* use 20 sec */
 43#define ARES_TIMEOUT_MS            20000
 44
 45
 46/* thread pool callback when socket is signalled */
 47static void CALLBACK uv_ares_socksignal_tp(void* parameter,
 48    BOOLEAN timerfired) {
 49  WSANETWORKEVENTS network_events;
 50  uv_ares_task_t* sockhandle;
 51  uv_ares_action_t* selhandle;
 52  uv_req_t* uv_ares_req;
 53  uv_loop_t* loop;
 54
 55  assert(parameter != NULL);
 56
 57  if (parameter != NULL) {
 58    sockhandle = (uv_ares_task_t*) parameter;
 59    loop = sockhandle->loop;
 60
 61    /* clear socket status for this event */
 62    /* do not fail if error, thread may run after socket close */
 63    /* The code assumes that c-ares will write all pending data in the */
 64    /* callback, unless the socket would block. We can clear the state here */
 65    /* to avoid unnecessary signals. */
 66    WSAEnumNetworkEvents(sockhandle->sock,
 67                         sockhandle->h_event,
 68                         &network_events);
 69
 70    /* setup new handle */
 71    selhandle = (uv_ares_action_t*)malloc(sizeof(uv_ares_action_t));
 72    if (selhandle == NULL) {
 73      uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
 74    }
 75    selhandle->type = UV_ARES_EVENT;
 76    selhandle->close_cb = NULL;
 77    selhandle->data = sockhandle->data;
 78    selhandle->sock = sockhandle->sock;
 79    selhandle->read =
 80        (network_events.lNetworkEvents & (FD_READ | FD_CONNECT)) ? 1 : 0;
 81    selhandle->write =
 82        (network_events.lNetworkEvents & (FD_WRITE | FD_CONNECT)) ? 1 : 0;
 83
 84    uv_ares_req = &selhandle->ares_req;
 85    uv_req_init(loop, uv_ares_req);
 86    uv_ares_req->type = UV_ARES_EVENT_REQ;
 87    uv_ares_req->data = selhandle;
 88
 89    /* post ares needs to called */
 90    POST_COMPLETION_FOR_REQ(loop, uv_ares_req);
 91  }
 92}
 93
 94
 95/* periodically call ares to check for timeouts */
 96static void uv_ares_poll(uv_timer_t* handle, int status) {
 97  uv_loop_t* loop = handle->loop;
 98  if (loop->ares_chan != NULL && loop->ares_active_sockets > 0) {
 99    ares_process_fd(loop->ares_chan, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
100  }
101}
102
103
104/* callback from ares when socket operation is started */
105static void uv_ares_sockstate_cb(void *data, ares_socket_t sock, int read,
106    int write) {
107  /* look to see if we have a handle for this socket in our list */
108  uv_loop_t* loop = (uv_loop_t*) data;
109  uv_ares_task_t* uv_handle_ares = uv_find_ares_handle(loop, sock);
110
111  int timeoutms = 0;
112
113  if (read == 0 && write == 0) {
114    /* if read and write are 0, cleanup existing data */
115    /* The code assumes that c-ares does a callback with read = 0 and */
116    /* write = 0 when the socket is closed. After we receive this we stop */
117    /* monitoring the socket. */
118    if (uv_handle_ares != NULL) {
119      uv_req_t* uv_ares_req;
120
121      uv_handle_ares->h_close_event = CreateEvent(NULL, FALSE, FALSE, NULL);
122      /* remove Wait */
123      if (uv_handle_ares->h_wait) {
124        UnregisterWaitEx(uv_handle_ares->h_wait,
125                         uv_handle_ares->h_close_event);
126        uv_handle_ares->h_wait = NULL;
127      }
128
129      /* detach socket from the event */
130      WSAEventSelect(sock, NULL, 0);
131      if (uv_handle_ares->h_event != WSA_INVALID_EVENT) {
132        WSACloseEvent(uv_handle_ares->h_event);
133        uv_handle_ares->h_event = WSA_INVALID_EVENT;
134      }
135      /* remove handle from list */
136      uv_remove_ares_handle(uv_handle_ares);
137
138      /* Post request to cleanup the Task */
139      uv_ares_req = &uv_handle_ares->ares_req;
140      uv_req_init(loop, uv_ares_req);
141      uv_ares_req->type = UV_ARES_CLEANUP_REQ;
142      uv_ares_req->data = uv_handle_ares;
143
144      /* post ares done with socket - finish cleanup when all threads done. */
145      POST_COMPLETION_FOR_REQ(loop, uv_ares_req);
146    } else {
147      assert(0);
148      uv_fatal_error(ERROR_INVALID_DATA, "ares_SockStateCB");
149    }
150  } else {
151    if (uv_handle_ares == NULL) {
152      /* setup new handle */
153      /* The code assumes that c-ares will call us when it has an open socket.
154        We need to call into c-ares when there is something to read,
155        or when it becomes writable. */
156      uv_handle_ares = (uv_ares_task_t*)malloc(sizeof(uv_ares_task_t));
157      if (uv_handle_ares == NULL) {
158        uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
159      }
160      uv_handle_ares->type = UV_ARES_TASK;
161      uv_handle_ares->close_cb = NULL;
162      uv_handle_ares->data = loop;
163      uv_handle_ares->sock = sock;
164      uv_handle_ares->h_wait = NULL;
165      uv_handle_ares->flags = 0;
166
167      /* create an event to wait on socket signal */
168      uv_handle_ares->h_event = WSACreateEvent();
169      if (uv_handle_ares->h_event == WSA_INVALID_EVENT) {
170        uv_fatal_error(WSAGetLastError(), "WSACreateEvent");
171      }
172
173      /* tie event to socket */
174      if (SOCKET_ERROR == WSAEventSelect(sock,
175                                         uv_handle_ares->h_event,
176                                         FD_READ | FD_WRITE | FD_CONNECT)) {
177        uv_fatal_error(WSAGetLastError(), "WSAEventSelect");
178      }
179
180      /* add handle to list */
181      uv_add_ares_handle(loop, uv_handle_ares);
182      uv_ref(loop);
183
184      /*
185       * we have a single polling timer for all ares sockets.
186       * This is preferred to using ares_timeout. See ares_timeout.c warning.
187       * if timer is not running start it, and keep socket count
188       */
189      if (loop->ares_active_sockets == 0) {
190        uv_timer_init(loop, &loop->ares_polling_timer);
191        uv_timer_start(&loop->ares_polling_timer, uv_ares_poll, 1000L, 1000L);
192      }
193      loop->ares_active_sockets++;
194
195      /* specify thread pool function to call when event is signaled */
196      if (RegisterWaitForSingleObject(&uv_handle_ares->h_wait,
197                                  uv_handle_ares->h_event,
198                                  uv_ares_socksignal_tp,
199                                  (void*)uv_handle_ares,
200                                  INFINITE,
201                                  WT_EXECUTEINWAITTHREAD) == 0) {
202        uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
203      }
204    } else {
205      /* found existing handle.  */
206      assert(uv_handle_ares->type == UV_ARES_TASK);
207      assert(uv_handle_ares->data != NULL);
208      assert(uv_handle_ares->h_event != WSA_INVALID_EVENT);
209    }
210  }
211}
212
213
214/* called via uv_poll when ares completion port signaled */
215void uv_process_ares_event_req(uv_loop_t* loop, uv_ares_action_t* handle,
216    uv_req_t* req) {
217  ares_process_fd(loop->ares_chan,
218                  handle->read ? handle->sock : INVALID_SOCKET,
219                  handle->write ?  handle->sock : INVALID_SOCKET);
220
221  /* release handle for select here  */
222  free(handle);
223}
224
225
226/* called via uv_poll when ares is finished with socket */
227void uv_process_ares_cleanup_req(uv_loop_t* loop, uv_ares_task_t* handle,
228    uv_req_t* req) {
229  /* check for event complete without waiting */
230  unsigned int signaled = WaitForSingleObject(handle->h_close_event, 0);
231
232  if (signaled != WAIT_TIMEOUT) {
233    uv_unref(loop);
234
235    /* close event handle and free uv handle memory */
236    CloseHandle(handle->h_close_event);
237    free(handle);
238
239    /* decrement active count. if it becomes 0 stop polling */
240    if (loop->ares_active_sockets > 0) {
241      loop->ares_active_sockets--;
242      if (loop->ares_active_sockets == 0) {
243        uv_close((uv_handle_t*) &loop->ares_polling_timer, NULL);
244      }
245    }
246  } else {
247    /* still busy - repost and try again */
248    POST_COMPLETION_FOR_REQ(loop, req);
249  }
250}
251
252
253/* set ares SOCK_STATE callback to our handler */
254int uv_ares_init_options(uv_loop_t* loop,
255                         ares_channel *channelptr,
256                         struct ares_options *options,
257                         int optmask) {
258  int rc;
259
260  /* only allow single init at a time */
261  if (loop->ares_chan != NULL) {
262    return UV_EALREADY;
263  }
264
265  /* set our callback as an option */
266  options->sock_state_cb = uv_ares_sockstate_cb;
267  options->sock_state_cb_data = loop;
268  optmask |= ARES_OPT_SOCK_STATE_CB;
269
270  /* We do the call to ares_init_option for caller. */
271  rc = ares_init_options(channelptr, options, optmask);
272
273  /* if success, save channel */
274  if (rc == ARES_SUCCESS) {
275    loop->ares_chan = *channelptr;
276  }
277
278  return rc;
279}
280
281
282/* release memory */
283void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) {
284  /* only allow destroy if did init */
285  if (loop->ares_chan != NULL) {
286    ares_destroy(channel);
287    loop->ares_chan = NULL;
288  }
289}