/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. #include <assert.h>
  22. #include "uv.h"
  23. #include "../uv-common.h"
  24. #include "internal.h"
  25. /*
  26. * Subclass of uv_handle_t. Used for integration of c-ares.
  27. */
  28. struct uv_ares_action_s {
  29. UV_HANDLE_FIELDS
  30. struct uv_req_s ares_req;
  31. SOCKET sock;
  32. int read;
  33. int write;
  34. };
  35. /* default timeout per socket request if ares does not specify value */
  36. /* use 20 sec */
  37. #define ARES_TIMEOUT_MS 20000
  38. /* thread pool callback when socket is signalled */
  39. static void CALLBACK uv_ares_socksignal_tp(void* parameter,
  40. BOOLEAN timerfired) {
  41. WSANETWORKEVENTS network_events;
  42. uv_ares_task_t* sockhandle;
  43. uv_ares_action_t* selhandle;
  44. uv_req_t* uv_ares_req;
  45. uv_loop_t* loop;
  46. assert(parameter != NULL);
  47. if (parameter != NULL) {
  48. sockhandle = (uv_ares_task_t*) parameter;
  49. loop = sockhandle->loop;
  50. /* clear socket status for this event */
  51. /* do not fail if error, thread may run after socket close */
  52. /* The code assumes that c-ares will write all pending data in the */
  53. /* callback, unless the socket would block. We can clear the state here */
  54. /* to avoid unnecessary signals. */
  55. WSAEnumNetworkEvents(sockhandle->sock,
  56. sockhandle->h_event,
  57. &network_events);
  58. /* setup new handle */
  59. selhandle = (uv_ares_action_t*)malloc(sizeof(uv_ares_action_t));
  60. if (selhandle == NULL) {
  61. uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
  62. }
  63. selhandle->type = UV_ARES_EVENT;
  64. selhandle->close_cb = NULL;
  65. selhandle->data = sockhandle->data;
  66. selhandle->sock = sockhandle->sock;
  67. selhandle->read =
  68. (network_events.lNetworkEvents & (FD_READ | FD_CONNECT)) ? 1 : 0;
  69. selhandle->write =
  70. (network_events.lNetworkEvents & (FD_WRITE | FD_CONNECT)) ? 1 : 0;
  71. uv_ares_req = &selhandle->ares_req;
  72. uv_req_init(loop, uv_ares_req);
  73. uv_ares_req->type = UV_ARES_EVENT_REQ;
  74. uv_ares_req->data = selhandle;
  75. /* post ares needs to called */
  76. POST_COMPLETION_FOR_REQ(loop, uv_ares_req);
  77. }
  78. }
  79. /* periodically call ares to check for timeouts */
  80. static void uv_ares_poll(uv_timer_t* handle, int status) {
  81. uv_loop_t* loop = handle->loop;
  82. if (loop->ares_chan != NULL && loop->ares_active_sockets > 0) {
  83. ares_process_fd(loop->ares_chan, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
  84. }
  85. }
  86. /* callback from ares when socket operation is started */
  87. static void uv_ares_sockstate_cb(void *data, ares_socket_t sock, int read,
  88. int write) {
  89. /* look to see if we have a handle for this socket in our list */
  90. uv_loop_t* loop = (uv_loop_t*) data;
  91. uv_ares_task_t* uv_handle_ares = uv_find_ares_handle(loop, sock);
  92. int timeoutms = 0;
  93. if (read == 0 && write == 0) {
  94. /* if read and write are 0, cleanup existing data */
  95. /* The code assumes that c-ares does a callback with read = 0 and */
  96. /* write = 0 when the socket is closed. After we receive this we stop */
  97. /* monitoring the socket. */
  98. if (uv_handle_ares != NULL) {
  99. uv_req_t* uv_ares_req;
  100. uv_handle_ares->h_close_event = CreateEvent(NULL, FALSE, FALSE, NULL);
  101. /* remove Wait */
  102. if (uv_handle_ares->h_wait) {
  103. UnregisterWaitEx(uv_handle_ares->h_wait,
  104. uv_handle_ares->h_close_event);
  105. uv_handle_ares->h_wait = NULL;
  106. }
  107. /* detach socket from the event */
  108. WSAEventSelect(sock, NULL, 0);
  109. if (uv_handle_ares->h_event != WSA_INVALID_EVENT) {
  110. WSACloseEvent(uv_handle_ares->h_event);
  111. uv_handle_ares->h_event = WSA_INVALID_EVENT;
  112. }
  113. /* remove handle from list */
  114. uv_remove_ares_handle(uv_handle_ares);
  115. /* Post request to cleanup the Task */
  116. uv_ares_req = &uv_handle_ares->ares_req;
  117. uv_req_init(loop, uv_ares_req);
  118. uv_ares_req->type = UV_ARES_CLEANUP_REQ;
  119. uv_ares_req->data = uv_handle_ares;
  120. /* post ares done with socket - finish cleanup when all threads done. */
  121. POST_COMPLETION_FOR_REQ(loop, uv_ares_req);
  122. } else {
  123. assert(0);
  124. uv_fatal_error(ERROR_INVALID_DATA, "ares_SockStateCB");
  125. }
  126. } else {
  127. if (uv_handle_ares == NULL) {
  128. /* setup new handle */
  129. /* The code assumes that c-ares will call us when it has an open socket.
  130. We need to call into c-ares when there is something to read,
  131. or when it becomes writable. */
  132. uv_handle_ares = (uv_ares_task_t*)malloc(sizeof(uv_ares_task_t));
  133. if (uv_handle_ares == NULL) {
  134. uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
  135. }
  136. uv_handle_ares->type = UV_ARES_TASK;
  137. uv_handle_ares->close_cb = NULL;
  138. uv_handle_ares->data = loop;
  139. uv_handle_ares->sock = sock;
  140. uv_handle_ares->h_wait = NULL;
  141. uv_handle_ares->flags = 0;
  142. /* create an event to wait on socket signal */
  143. uv_handle_ares->h_event = WSACreateEvent();
  144. if (uv_handle_ares->h_event == WSA_INVALID_EVENT) {
  145. uv_fatal_error(WSAGetLastError(), "WSACreateEvent");
  146. }
  147. /* tie event to socket */
  148. if (SOCKET_ERROR == WSAEventSelect(sock,
  149. uv_handle_ares->h_event,
  150. FD_READ | FD_WRITE | FD_CONNECT)) {
  151. uv_fatal_error(WSAGetLastError(), "WSAEventSelect");
  152. }
  153. /* add handle to list */
  154. uv_add_ares_handle(loop, uv_handle_ares);
  155. uv_ref(loop);
  156. /*
  157. * we have a single polling timer for all ares sockets.
  158. * This is preferred to using ares_timeout. See ares_timeout.c warning.
  159. * if timer is not running start it, and keep socket count
  160. */
  161. if (loop->ares_active_sockets == 0) {
  162. uv_timer_init(loop, &loop->ares_polling_timer);
  163. uv_timer_start(&loop->ares_polling_timer, uv_ares_poll, 1000L, 1000L);
  164. }
  165. loop->ares_active_sockets++;
  166. /* specify thread pool function to call when event is signaled */
  167. if (RegisterWaitForSingleObject(&uv_handle_ares->h_wait,
  168. uv_handle_ares->h_event,
  169. uv_ares_socksignal_tp,
  170. (void*)uv_handle_ares,
  171. INFINITE,
  172. WT_EXECUTEINWAITTHREAD) == 0) {
  173. uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
  174. }
  175. } else {
  176. /* found existing handle. */
  177. assert(uv_handle_ares->type == UV_ARES_TASK);
  178. assert(uv_handle_ares->data != NULL);
  179. assert(uv_handle_ares->h_event != WSA_INVALID_EVENT);
  180. }
  181. }
  182. }
  183. /* called via uv_poll when ares completion port signaled */
  184. void uv_process_ares_event_req(uv_loop_t* loop, uv_ares_action_t* handle,
  185. uv_req_t* req) {
  186. ares_process_fd(loop->ares_chan,
  187. handle->read ? handle->sock : INVALID_SOCKET,
  188. handle->write ? handle->sock : INVALID_SOCKET);
  189. /* release handle for select here */
  190. free(handle);
  191. }
  192. /* called via uv_poll when ares is finished with socket */
  193. void uv_process_ares_cleanup_req(uv_loop_t* loop, uv_ares_task_t* handle,
  194. uv_req_t* req) {
  195. /* check for event complete without waiting */
  196. unsigned int signaled = WaitForSingleObject(handle->h_close_event, 0);
  197. if (signaled != WAIT_TIMEOUT) {
  198. uv_unref(loop);
  199. /* close event handle and free uv handle memory */
  200. CloseHandle(handle->h_close_event);
  201. free(handle);
  202. /* decrement active count. if it becomes 0 stop polling */
  203. if (loop->ares_active_sockets > 0) {
  204. loop->ares_active_sockets--;
  205. if (loop->ares_active_sockets == 0) {
  206. uv_close((uv_handle_t*) &loop->ares_polling_timer, NULL);
  207. }
  208. }
  209. } else {
  210. /* still busy - repost and try again */
  211. POST_COMPLETION_FOR_REQ(loop, req);
  212. }
  213. }
  214. /* set ares SOCK_STATE callback to our handler */
  215. int uv_ares_init_options(uv_loop_t* loop,
  216. ares_channel *channelptr,
  217. struct ares_options *options,
  218. int optmask) {
  219. int rc;
  220. /* only allow single init at a time */
  221. if (loop->ares_chan != NULL) {
  222. return UV_EALREADY;
  223. }
  224. /* set our callback as an option */
  225. options->sock_state_cb = uv_ares_sockstate_cb;
  226. options->sock_state_cb_data = loop;
  227. optmask |= ARES_OPT_SOCK_STATE_CB;
  228. /* We do the call to ares_init_option for caller. */
  229. rc = ares_init_options(channelptr, options, optmask);
  230. /* if success, save channel */
  231. if (rc == ARES_SUCCESS) {
  232. loop->ares_chan = *channelptr;
  233. }
  234. return rc;
  235. }
  236. /* release memory */
  237. void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) {
  238. /* only allow destroy if did init */
  239. if (loop->ares_chan != NULL) {
  240. ares_destroy(channel);
  241. loop->ares_chan = NULL;
  242. }
  243. }