diff --git a/deps/uv/src/win/async.c b/deps/uv/src/win/async.c new file mode 100644 index 00000000000..fb5ed903499 --- /dev/null +++ b/deps/uv/src/win/async.c @@ -0,0 +1,129 @@ +/* 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 + +#include "uv.h" +#include "internal.h" + + +/* Atomic set operation on char */ +#ifdef _MSC_VER /* MSVC */ + +/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less */ +/* efficient than InterlockedExchange, but InterlockedExchange8 does not */ +/* exist, and interlocked operations on larger targets might require the */ +/* target to be aligned. */ +#pragma intrinsic(_InterlockedOr8) + +static char __declspec(inline) uv_atomic_exchange_set(char volatile* target) { + return _InterlockedOr8(target, 1); +} + +#else /* GCC */ + +/* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ +static inline char uv_atomic_exchange_set(char volatile* target) { + const char one = 1; + char old_value; + __asm__ __volatile__ ("lock xchgb %0, %1\n\t" + : "=r"(old_value), "=m"(*target) + : "0"(one), "m"(*target) + : "memory"); + return old_value; +} + +#endif + + +void uv_async_endgame(uv_async_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING && + !handle->async_sent) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(); + } +} + + +int uv_async_init(uv_async_t* handle, uv_async_cb async_cb) { + uv_req_t* req; + + uv_counters()->handle_init++; + uv_counters()->async_init++; + + handle->type = UV_ASYNC; + handle->flags = 0; + handle->async_sent = 0; + handle->error = uv_ok_; + handle->async_cb = async_cb; + + req = &handle->async_req; + uv_req_init(req); + req->type = UV_WAKEUP; + req->data = handle; + + uv_ref(); + + return 0; +} + + +int uv_async_send(uv_async_t* handle) { + if (handle->type != UV_ASYNC) { + /* Can't set errno because that's not thread-safe. */ + return -1; + } + + /* The user should make sure never to call uv_async_send to a closing */ + /* or closed handle. */ + assert(!(handle->flags & UV_HANDLE_CLOSING)); + + if (!uv_atomic_exchange_set(&handle->async_sent)) { + if (!PostQueuedCompletionStatus(LOOP->iocp, + 0, + 0, + &handle->async_req.overlapped)) { + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); + } + } + + return 0; +} + + +void uv_process_async_wakeup_req(uv_async_t* handle, uv_req_t* req) { + assert(handle->type == UV_ASYNC); + assert(req->type == UV_WAKEUP); + + handle->async_sent = 0; + if (handle->async_cb) { + handle->async_cb((uv_async_t*) handle, 0); + } + if (handle->flags & UV_HANDLE_CLOSING) { + uv_want_endgame((uv_handle_t*)handle); + } +} diff --git a/deps/uv/src/win/cares.c b/deps/uv/src/win/cares.c new file mode 100644 index 00000000000..60e75eaf622 --- /dev/null +++ b/deps/uv/src/win/cares.c @@ -0,0 +1,304 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.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; +}; + + +/* memory used per ares_channel */ +typedef struct uv_ares_channel_s { + ares_channel channel; + int activesockets; + uv_timer_t pollingtimer; +} uv_ares_channel_t; + + +/* static data to hold single ares_channel */ +static uv_ares_channel_t uv_ares_data = { NULL, 0 }; + + +/* 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; + + assert(parameter != NULL); + + if (parameter != NULL) { + sockhandle = (uv_ares_task_t*)parameter; + + /* 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 unecessary + 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(uv_ares_req); + uv_ares_req->type = UV_ARES_EVENT_REQ; + uv_ares_req->data = selhandle; + + /* post ares needs to called */ + if (!PostQueuedCompletionStatus(LOOP->iocp, + 0, + 0, + &uv_ares_req->overlapped)) { + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); + } + } +} + + +/* periodically call ares to check for timeouts */ +static void uv_ares_poll(uv_timer_t* handle, int status) { + if (uv_ares_data.channel != NULL && uv_ares_data.activesockets > 0) { + ares_process_fd(uv_ares_data.channel, 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_ares_task_t* uv_handle_ares = uv_find_ares_handle(sock); + uv_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)data; + + int timeoutms = 0; + + 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 recieve 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); + + /* Post request to cleanup the Task */ + uv_ares_req = &uv_handle_ares->ares_req; + uv_req_init(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. */ + if (!PostQueuedCompletionStatus(LOOP->iocp, + 0, + 0, + &uv_ares_req->overlapped)) { + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); + } + } 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->data = uv_ares_data_ptr; + 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(uv_handle_ares); + uv_ref(); + + /* + * 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 (uv_ares_data_ptr->activesockets == 0) { + uv_timer_init(&uv_ares_data_ptr->pollingtimer); + uv_timer_start(&uv_ares_data_ptr->pollingtimer, uv_ares_poll, 1000L, 1000L); + } + uv_ares_data_ptr->activesockets++; + + /* 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_ares_action_t* handle, uv_req_t* req) { + uv_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)handle->data; + + ares_process_fd(uv_ares_data_ptr->channel, + 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_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_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)handle->data; + + uv_unref(); + + /* 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 (uv_ares_data_ptr->activesockets > 0) { + uv_ares_data_ptr->activesockets--; + if (uv_ares_data_ptr->activesockets == 0) { + uv_close((uv_handle_t*)&uv_ares_data_ptr->pollingtimer, NULL); + } + } + } else { + /* stil busy - repost and try again */ + if (!PostQueuedCompletionStatus(LOOP->iocp, + 0, + 0, + &req->overlapped)) { + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); + } + } +} + + +/* set ares SOCK_STATE callback to our handler */ +int uv_ares_init_options(ares_channel *channelptr, + struct ares_options *options, + int optmask) { + int rc; + + /* only allow single init at a time */ + if (uv_ares_data.channel != NULL) { + return UV_EALREADY; + } + + /* set our callback as an option */ + options->sock_state_cb = uv_ares_sockstate_cb; + options->sock_state_cb_data = &uv_ares_data; + 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) { + uv_ares_data.channel = *channelptr; + } + + return rc; +} + + +/* release memory */ +void uv_ares_destroy(ares_channel channel) { + /* only allow destroy if did init */ + if (uv_ares_data.channel != NULL) { + ares_destroy(channel); + uv_ares_data.channel = NULL; + } +} diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c new file mode 100644 index 00000000000..32009478fb5 --- /dev/null +++ b/deps/uv/src/win/core.c @@ -0,0 +1,154 @@ +/* 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 +#include +#include +#include +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* The only event loop we support right now */ +uv_loop_t uv_main_loop_; + + +static void uv_loop_init() { + /* Create an I/O completion port */ + LOOP->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); + if (LOOP->iocp == NULL) { + uv_fatal_error(GetLastError(), "CreateIoCompletionPort"); + } + + LOOP->refs = 0; + + uv_update_time(); + + LOOP->pending_reqs_tail = NULL; + + LOOP->endgame_handles = NULL; + + RB_INIT(&LOOP->timers); + + LOOP->check_handles = NULL; + LOOP->prepare_handles = NULL; + LOOP->idle_handles = NULL; + + LOOP->next_prepare_handle = NULL; + LOOP->next_check_handle = NULL; + LOOP->next_idle_handle = NULL; + + LOOP->last_error = uv_ok_; + + LOOP->err_str = NULL; +} + + +void uv_init() { + /* Initialize winsock */ + uv_winsock_startup(); + + /* Initialize timers */ + uv_timer_startup(); + + /* Intialize event loop */ + uv_loop_init(); +} + + +void uv_ref() { + LOOP->refs++; +} + + +void uv_unref() { + LOOP->refs--; +} + + +static void uv_poll() { + BOOL success; + DWORD bytes; + ULONG_PTR key; + OVERLAPPED* overlapped; + uv_req_t* req; + + success = GetQueuedCompletionStatus(LOOP->iocp, + &bytes, + &key, + &overlapped, + uv_get_poll_timeout()); + + uv_update_time(); + + if (overlapped) { + /* Package was dequeued */ + req = uv_overlapped_to_req(overlapped); + + if (!success) { + req->error = uv_new_sys_error(GetLastError()); + } + + uv_insert_pending_req(req); + + } else if (GetLastError() != WAIT_TIMEOUT) { + /* Serious error */ + uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); + } +} + + +int uv_run() { + while (1) { + uv_update_time(); + uv_process_timers(); + + /* Terrible: please fix me! */ + while (LOOP->refs > 0 && + (LOOP->idle_handles || LOOP->pending_reqs_tail || LOOP->endgame_handles)) { + /* Terrible: please fix me! */ + while (LOOP->pending_reqs_tail || LOOP->endgame_handles) { + uv_process_endgames(); + uv_process_reqs(); + } + + /* Call idle callbacks */ + uv_idle_invoke(); + } + + if (LOOP->refs <= 0) { + break; + } + + uv_prepare_invoke(); + + uv_poll(); + + uv_check_invoke(); + } + + assert(LOOP->refs == 0); + return 0; +} diff --git a/deps/uv/src/win/error.c b/deps/uv/src/win/error.c new file mode 100644 index 00000000000..d7cf9866c5b --- /dev/null +++ b/deps/uv/src/win/error.c @@ -0,0 +1,133 @@ +/* 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 +#include +#include +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +const uv_err_t uv_ok_ = { UV_OK, ERROR_SUCCESS }; + + +/* + * Display an error message and abort the event loop. + */ +void uv_fatal_error(const int errorno, const char* syscall) { + char* buf = NULL; + const char* errmsg; + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL); + + if (buf) { + errmsg = buf; + } else { + errmsg = "Unknown error"; + } + + /* FormatMessage messages include a newline character already, */ + /* so don't add another. */ + if (syscall) { + fprintf(stderr, "%s: (%d) %s", syscall, errorno, errmsg); + } else { + fprintf(stderr, "(%d) %s", errorno, errmsg); + } + + if (buf) { + LocalFree(buf); + } + + *((char*)NULL) = 0xff; /* Force debug break */ + abort(); +} + + +uv_err_t uv_last_error() { + return LOOP->last_error; +} + + +char* uv_strerror(uv_err_t err) { + if (LOOP->err_str != NULL) { + LocalFree(LOOP->err_str); + } + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err.sys_errno_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&LOOP->err_str, 0, NULL); + + if (LOOP->err_str) { + return LOOP->err_str; + } else { + return "Unknown error"; + } +} + + +uv_err_code uv_translate_sys_error(int sys_errno) { + switch (sys_errno) { + case ERROR_SUCCESS: return UV_OK; + case ERROR_NOACCESS: return UV_EACCESS; + case WSAEACCES: return UV_EACCESS; + case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; + case WSAEADDRINUSE: return UV_EADDRINUSE; + case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; + case WSAEWOULDBLOCK: return UV_EAGAIN; + case WSAEALREADY: return UV_EALREADY; + case ERROR_CONNECTION_REFUSED: return UV_ECONNREFUSED; + case WSAECONNREFUSED: return UV_ECONNREFUSED; + case WSAEFAULT: return UV_EFAULT; + case ERROR_INVALID_DATA: return UV_EINVAL; + case WSAEINVAL: return UV_EINVAL; + case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; + case WSAEMFILE: return UV_EMFILE; + case ERROR_OUTOFMEMORY: return UV_ENOMEM; + case ERROR_INSUFFICIENT_BUFFER: return UV_EINVAL; + case ERROR_INVALID_FLAGS: return UV_EBADF; + case ERROR_INVALID_PARAMETER: return UV_EINVAL; + case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET; + case ERROR_BROKEN_PIPE: return UV_EOF; + case ERROR_PIPE_BUSY: return UV_EBUSY; + case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT; + default: return UV_UNKNOWN; + } +} + + +uv_err_t uv_new_sys_error(int sys_errno) { + uv_err_t e; + e.code = uv_translate_sys_error(sys_errno); + e.sys_errno_ = sys_errno; + return e; +} + + +void uv_set_sys_error(int sys_errno) { + LOOP->last_error.code = uv_translate_sys_error(sys_errno); + LOOP->last_error.sys_errno_ = sys_errno; +} diff --git a/deps/uv/src/win/getaddrinfo.c b/deps/uv/src/win/getaddrinfo.c new file mode 100644 index 00000000000..7793fc4935b --- /dev/null +++ b/deps/uv/src/win/getaddrinfo.c @@ -0,0 +1,341 @@ +/* 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 +#include + +#include "uv.h" +#include "internal.h" + + +/* + * MinGW is missing this + */ +#ifndef _MSC_VER + typedef struct addrinfoW { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + wchar_t* ai_canonname; + struct sockaddr* ai_addr; + struct addrinfoW* ai_next; + } ADDRINFOW, *PADDRINFOW; + + DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const wchar_t* node, + const wchar_t* service, + const ADDRINFOW* hints, + PADDRINFOW* result); + + DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo); +#endif + + +/* adjust size value to be multiple of 4. Use to keep pointer aligned */ +/* Do we need different versions of this for different architectures? */ +#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2) + + +/* + * getaddrinfo error code mapping + * Falls back to uv_translate_sys_error if no match + */ +static uv_err_code uv_translate_eai_error(int eai_errno) { + switch (eai_errno) { + case ERROR_SUCCESS: return UV_OK; + case EAI_BADFLAGS: return UV_EBADF; + case EAI_FAIL: return UV_EFAULT; + case EAI_FAMILY: return UV_EAIFAMNOSUPPORT; + case EAI_MEMORY: return UV_ENOMEM; + case EAI_NONAME: return UV_EAINONAME; + case EAI_AGAIN: return UV_EAGAIN; + case EAI_SERVICE: return UV_EAISERVICE; + case EAI_SOCKTYPE: return UV_EAISOCKTYPE; + default: return uv_translate_sys_error(eai_errno); + } +} + + +/* getaddrinfo worker thread implementation */ +static DWORD WINAPI getaddrinfo_thread_proc(void* parameter) { + uv_getaddrinfo_t* handle = (uv_getaddrinfo_t*)parameter; + int ret; + + assert(handle != NULL); + + if (handle != NULL) { + /* call OS function on this thread */ + ret = GetAddrInfoW(handle->node, handle->service, handle->hints, &handle->res); + handle->retcode = ret; + + /* post getaddrinfo completed */ + if (!PostQueuedCompletionStatus(LOOP->iocp, + 0, + 0, + &handle->getadddrinfo_req.overlapped)) { + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); + } + } + + return 0; +} + + +/* + * Called from uv_run when complete. Call user specified callback + * then free returned addrinfo + * Returned addrinfo strings are converted from UTF-16 to UTF-8. + * + * To minimize allocation we calculate total size required, + * and copy all structs and referenced strings into the one block. + * Each size calculation is adjusted to avoid unaligned pointers. + */ +void uv_process_getaddrinfo_req(uv_getaddrinfo_t* handle, uv_req_t* req) { + int addrinfo_len = 0; + int name_len = 0; + size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo)); + struct addrinfoW* addrinfow_ptr; + struct addrinfo* addrinfo_ptr; + char* alloc_ptr = NULL; + char* cur_ptr = NULL; + uv_err_code uv_ret; + + /* release input parameter memory */ + if (handle->alloc != NULL) { + free(handle->alloc); + handle->alloc = NULL; + } + + uv_ret = uv_translate_eai_error(handle->retcode); + if (handle->retcode == 0) { + /* convert addrinfoW to addrinfo */ + /* first calculate required length */ + addrinfow_ptr = handle->res; + while (addrinfow_ptr != NULL) { + addrinfo_len += addrinfo_struct_len + ALIGNED_SIZE(addrinfow_ptr->ai_addrlen); + if (addrinfow_ptr->ai_canonname != NULL) { + name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); + if (name_len == 0) { + uv_ret = uv_translate_sys_error(GetLastError()); + goto complete; + } + addrinfo_len += ALIGNED_SIZE(name_len); + } + addrinfow_ptr = addrinfow_ptr->ai_next; + } + + /* allocate memory for addrinfo results */ + alloc_ptr = (char*)malloc(addrinfo_len); + + /* do conversions */ + if (alloc_ptr != NULL) { + cur_ptr = alloc_ptr; + addrinfow_ptr = handle->res; + + while (addrinfow_ptr != NULL) { + /* copy addrinfo struct data */ + assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len); + addrinfo_ptr = (struct addrinfo*)cur_ptr; + addrinfo_ptr->ai_family = addrinfow_ptr->ai_family; + addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype; + addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol; + addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags; + addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen; + addrinfo_ptr->ai_canonname = NULL; + addrinfo_ptr->ai_addr = NULL; + addrinfo_ptr->ai_next = NULL; + + cur_ptr += addrinfo_struct_len; + + /* copy sockaddr */ + if (addrinfo_ptr->ai_addrlen > 0) { + assert(cur_ptr + addrinfo_ptr->ai_addrlen <= alloc_ptr + addrinfo_len); + memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen); + addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr; + cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen); + } + + /* convert canonical name to UTF-8 */ + if (addrinfow_ptr->ai_canonname != NULL) { + name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); + assert(name_len > 0); + assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len); + name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, cur_ptr, name_len); + assert(name_len > 0); + addrinfo_ptr->ai_canonname = cur_ptr; + cur_ptr += ALIGNED_SIZE(name_len); + } + assert(cur_ptr <= alloc_ptr + addrinfo_len); + + /* set next ptr */ + addrinfow_ptr = addrinfow_ptr->ai_next; + if (addrinfow_ptr != NULL) { + addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr; + } + } + } else { + uv_ret = UV_ENOMEM; + } + + } + + /* return memory to system */ + if (handle->res != NULL) { + FreeAddrInfoW(handle->res); + handle->res = NULL; + } + +complete: + /* finally do callback with converted result */ + handle->getaddrinfo_cb(handle, uv_ret, (struct addrinfo*)alloc_ptr); + + /* release copied result memory */ + if (alloc_ptr != NULL) { + free(alloc_ptr); + } + + uv_unref(); +} + + +/* + * Entry point for getaddrinfo + * we convert the UTF-8 strings to UNICODE + * and save the UNICODE string pointers in the handle + * We also copy hints so that caller does not need to keep memory until the callback. + * return UV_OK if a callback will be made + * return error code if validation fails + * + * To minimize allocation we calculate total size required, + * and copy all structs and referenced strings into the one block. + * Each size calculation is adjusted to avoid unaligned pointers. + */ +int uv_getaddrinfo(uv_getaddrinfo_t* handle, + uv_getaddrinfo_cb getaddrinfo_cb, + const char* node, + const char* service, + const struct addrinfo* hints) { + int nodesize = 0; + int servicesize = 0; + int hintssize = 0; + char* alloc_ptr = NULL; + + if (handle == NULL || getaddrinfo_cb == NULL || + (node == NULL && service == NULL)) { + uv_set_sys_error(WSAEINVAL); + goto error; + } + + handle->getaddrinfo_cb = getaddrinfo_cb; + handle->res = NULL; + handle->type = UV_GETADDRINFO; + + /* calculate required memory size for all input values */ + if (node != NULL) { + nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(wchar_t)); + if (nodesize == 0) { + uv_set_sys_error(GetLastError()); + goto error; + } + } + + if (service != NULL) { + servicesize = ALIGNED_SIZE(uv_utf8_to_utf16(service, NULL, 0) * sizeof(wchar_t)); + if (servicesize == 0) { + uv_set_sys_error(GetLastError()); + goto error; + } + } + if (hints != NULL) { + hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW)); + } + + /* allocate memory for inputs, and partition it as needed */ + alloc_ptr = (char*)malloc(nodesize + servicesize + hintssize); + if (!alloc_ptr) { + uv_set_sys_error(WSAENOBUFS); + goto error; + } + + /* save alloc_ptr now so we can free if error */ + handle->alloc = (void*)alloc_ptr; + + /* convert node string to UTF16 into allocated memory and save pointer in handle */ + if (node != NULL) { + handle->node = (wchar_t*)alloc_ptr; + if (uv_utf8_to_utf16(node, (wchar_t*)alloc_ptr, nodesize / sizeof(wchar_t)) == 0) { + uv_set_sys_error(GetLastError()); + goto error; + } + alloc_ptr += nodesize; + } else { + handle->node = NULL; + } + + /* convert service string to UTF16 into allocated memory and save pointer in handle */ + if (service != NULL) { + handle->service = (wchar_t*)alloc_ptr; + if (uv_utf8_to_utf16(service, (wchar_t*)alloc_ptr, servicesize / sizeof(wchar_t)) == 0) { + uv_set_sys_error(GetLastError()); + goto error; + } + alloc_ptr += servicesize; + } else { + handle->service = NULL; + } + + /* copy hints to allocated memory and save pointer in handle */ + if (hints != NULL) { + handle->hints = (struct addrinfoW*)alloc_ptr; + handle->hints->ai_family = hints->ai_family; + handle->hints->ai_socktype = hints->ai_socktype; + handle->hints->ai_protocol = hints->ai_protocol; + handle->hints->ai_flags = hints->ai_flags; + handle->hints->ai_addrlen = 0; + handle->hints->ai_canonname = NULL; + handle->hints->ai_addr = NULL; + handle->hints->ai_next = NULL; + } else { + handle->hints = NULL; + } + + /* init request for Post handling */ + uv_req_init(&handle->getadddrinfo_req); + handle->getadddrinfo_req.data = handle; + handle->getadddrinfo_req.type = UV_GETADDRINFO_REQ; + + /* Ask thread to run. Treat this as a long operation */ + if (QueueUserWorkItem(&getaddrinfo_thread_proc, handle, WT_EXECUTELONGFUNCTION) == 0) { + uv_set_sys_error(GetLastError()); + goto error; + } + + uv_ref(); + + return 0; + +error: + if (handle != NULL && handle->alloc != NULL) { + free(handle->alloc); + } + return -1; +} diff --git a/deps/uv/src/win/handle.c b/deps/uv/src/win/handle.c new file mode 100644 index 00000000000..91dfba80f16 --- /dev/null +++ b/deps/uv/src/win/handle.c @@ -0,0 +1,167 @@ +/* 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 + +#include "uv.h" +#include "internal.h" + + +int uv_is_active(uv_handle_t* handle) { + switch (handle->type) { + case UV_TIMER: + case UV_IDLE: + case UV_PREPARE: + case UV_CHECK: + return (handle->flags & UV_HANDLE_ACTIVE) ? 1 : 0; + + default: + return 1; + } +} + + +/* TODO: integrate this with uv_close. */ +static int uv_close_error(uv_handle_t* handle, uv_err_t e) { + uv_tcp_t* tcp; + uv_pipe_t* pipe; + + if (handle->flags & UV_HANDLE_CLOSING) { + return 0; + } + + handle->error = e; + handle->flags |= UV_HANDLE_CLOSING; + + /* Handle-specific close actions */ + switch (handle->type) { + case UV_TCP: + tcp = (uv_tcp_t*)handle; + /* If we don't shutdown before calling closesocket, windows will */ + /* silently discard the kernel send buffer and reset the connection. */ + if (!(tcp->flags & UV_HANDLE_SHUT)) { + shutdown(tcp->socket, SD_SEND); + tcp->flags |= UV_HANDLE_SHUT; + } + tcp->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); + closesocket(tcp->socket); + if (tcp->reqs_pending == 0) { + uv_want_endgame(handle); + } + return 0; + + case UV_NAMED_PIPE: + pipe = (uv_pipe_t*)handle; + pipe->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); + close_pipe(pipe, NULL, NULL); + if (pipe->reqs_pending == 0) { + uv_want_endgame(handle); + } + return 0; + + case UV_TIMER: + uv_timer_stop((uv_timer_t*)handle); + uv_want_endgame(handle); + return 0; + + case UV_PREPARE: + uv_prepare_stop((uv_prepare_t*)handle); + uv_want_endgame(handle); + return 0; + + case UV_CHECK: + uv_check_stop((uv_check_t*)handle); + uv_want_endgame(handle); + return 0; + + case UV_IDLE: + uv_idle_stop((uv_idle_t*)handle); + uv_want_endgame(handle); + return 0; + + case UV_ASYNC: + if (!((uv_async_t*)handle)->async_sent) { + uv_want_endgame(handle); + } + return 0; + + default: + /* Not supported */ + assert(0); + return -1; + } +} + + +int uv_close(uv_handle_t* handle, uv_close_cb close_cb) { + handle->close_cb = close_cb; + return uv_close_error(handle, uv_ok_); +} + + +void uv_want_endgame(uv_handle_t* handle) { + if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) { + handle->flags |= UV_HANDLE_ENDGAME_QUEUED; + + handle->endgame_next = LOOP->endgame_handles; + LOOP->endgame_handles = handle; + } +} + + +void uv_process_endgames() { + uv_handle_t* handle; + + while (LOOP->endgame_handles) { + handle = LOOP->endgame_handles; + LOOP->endgame_handles = handle->endgame_next; + + handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED; + + switch (handle->type) { + case UV_TCP: + uv_tcp_endgame((uv_tcp_t*)handle); + break; + + case UV_NAMED_PIPE: + uv_pipe_endgame((uv_pipe_t*)handle); + break; + + case UV_TIMER: + uv_timer_endgame((uv_timer_t*)handle); + break; + + case UV_PREPARE: + case UV_CHECK: + case UV_IDLE: + uv_loop_watcher_endgame(handle); + break; + + case UV_ASYNC: + uv_async_endgame((uv_async_t*)handle); + break; + + default: + assert(0); + break; + } + } +} diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h new file mode 100644 index 00000000000..7c42a774e74 --- /dev/null +++ b/deps/uv/src/win/internal.h @@ -0,0 +1,223 @@ +/* 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. + */ + +#ifndef UV_WIN_INTERNAL_H_ +#define UV_WIN_INTERNAL_H_ + +#include "uv.h" +#include "../uv-common.h" +#include "tree.h" + + +/* + * Timers + */ + +RB_HEAD(uv_timer_tree_s, uv_timer_s); + +void uv_timer_startup(); + +void uv_timer_endgame(uv_timer_t* handle); + +DWORD uv_get_poll_timeout(); +void uv_process_timers(); + + +/* + * Core + */ + +/* Loop state struct. We don't support multiplicity right now, but this */ +/* should help when we get to that. */ +typedef struct uv_loop_s { + /* The loop's I/O completion port */ + HANDLE iocp; + /* Reference count that keeps the event loop alive */ + int refs; + /* The current time according to the event loop. in msecs. */ + int64_t time; + /* Tail of a single-linked circular queue of pending reqs. If the queue */ + /* is empty, tail_ is NULL. If there is only one item, */ + /* tail_->next_req == tail_ */ + uv_req_t* pending_reqs_tail; + /* Head of a single-linked list of closed handles */ + uv_handle_t* endgame_handles; + /* The head of the timers tree */ + struct uv_timer_tree_s timers; + /* Lists of active loop (prepare / check / idle) watchers */ + uv_prepare_t* prepare_handles; + uv_check_t* check_handles; + uv_idle_t* idle_handles; + /* This pointer will refer to the prepare/check/idle handle whose */ + /* callback is scheduled to be called next. This is needed to allow */ + /* safe removal from one of the lists above while that list being */ + /* iterated over. */ + uv_prepare_t* next_prepare_handle; + uv_check_t* next_check_handle; + uv_idle_t* next_idle_handle; + /* Last error code */ + uv_err_t last_error; + /* Error string most recently returned by uv_strerror() */ + char* err_str; +} uv_loop_t; + +extern uv_loop_t uv_main_loop_; + +#define LOOP (&uv_main_loop_) + + +/* + * Handles + */ + +/* Private uv_handle flags */ +#define UV_HANDLE_CLOSING 0x0001 +#define UV_HANDLE_CLOSED 0x0002 +#define UV_HANDLE_BOUND 0x0004 +#define UV_HANDLE_LISTENING 0x0008 +#define UV_HANDLE_CONNECTION 0x0010 +#define UV_HANDLE_CONNECTED 0x0020 +#define UV_HANDLE_READING 0x0040 +#define UV_HANDLE_ACTIVE 0x0040 +#define UV_HANDLE_EOF 0x0080 +#define UV_HANDLE_SHUTTING 0x0100 +#define UV_HANDLE_SHUT 0x0200 +#define UV_HANDLE_ENDGAME_QUEUED 0x0400 +#define UV_HANDLE_BIND_ERROR 0x1000 +#define UV_HANDLE_IPV6 0x2000 +#define UV_HANDLE_PIPESERVER 0x4000 +#define UV_HANDLE_READ_PENDING 0x8000 + +void uv_want_endgame(uv_handle_t* handle); +void uv_process_endgames(); + +#define DECREASE_PENDING_REQ_COUNT(handle) \ + do { \ + handle->reqs_pending--; \ + \ + if (handle->flags & UV_HANDLE_CLOSING && \ + handle->reqs_pending == 0) { \ + uv_want_endgame((uv_handle_t*)handle); \ + } \ + } while (0) + + +/* + * Requests + */ +void uv_req_init(uv_req_t* req); + +uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped); + +void uv_insert_pending_req(uv_req_t* req); +void uv_process_reqs(); + + +/* + * Streams + */ +void uv_stream_init(uv_stream_t* handle); +void uv_connection_init(uv_stream_t* handle); + +size_t uv_count_bufs(uv_buf_t bufs[], int count); + + +/* + * TCP + */ +void uv_winsock_startup(); + +void uv_tcp_endgame(uv_tcp_t* handle); + +int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client); +int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb); +int uv_tcp_write(uv_write_t* req, uv_tcp_t* handle, uv_buf_t bufs[], + int bufcnt, uv_write_cb cb); + +void uv_process_tcp_read_req(uv_tcp_t* handle, uv_req_t* req); +void uv_process_tcp_write_req(uv_tcp_t* handle, uv_write_t* req); +void uv_process_tcp_accept_req(uv_tcp_t* handle, uv_req_t* req); +void uv_process_tcp_connect_req(uv_tcp_t* handle, uv_connect_t* req); + + +/* + * Pipes + */ +void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err); +void uv_pipe_endgame(uv_pipe_t* handle); + +int uv_pipe_accept(uv_pipe_t* server, uv_pipe_t* client); +int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb); +int uv_pipe_write(uv_write_t* req, uv_pipe_t* handle, uv_buf_t bufs[], + int bufcnt, uv_write_cb cb); + +void uv_process_pipe_read_req(uv_pipe_t* handle, uv_req_t* req); +void uv_process_pipe_write_req(uv_pipe_t* handle, uv_write_t* req); +void uv_process_pipe_accept_req(uv_pipe_t* handle, uv_req_t* raw_req); +void uv_process_pipe_connect_req(uv_pipe_t* handle, uv_connect_t* req); + +/* + * Loop watchers + */ +void uv_loop_watcher_endgame(uv_handle_t* handle); + +void uv_prepare_invoke(); +void uv_check_invoke(); +void uv_idle_invoke(); + + +/* + * Async watcher + */ +void uv_async_endgame(uv_async_t* handle); + +void uv_process_async_wakeup_req(uv_async_t* handle, uv_req_t* req); + + +/* + * C-ares integration + */ +typedef struct uv_ares_action_s uv_ares_action_t; + +void uv_process_ares_event_req(uv_ares_action_t* handle, uv_req_t* req); +void uv_process_ares_cleanup_req(uv_ares_task_t* handle, uv_req_t* req); + +/* + * Getaddrinfo + */ +void uv_process_getaddrinfo_req(uv_getaddrinfo_t* handle, uv_req_t* req); + + +/* + * Error handling + */ +extern const uv_err_t uv_ok_; + +void uv_fatal_error(const int errorno, const char* syscall); + +uv_err_code uv_translate_sys_error(int sys_errno); +uv_err_t uv_new_sys_error(int sys_errno); +void uv_set_sys_error(int sys_errno); + + +#endif /* UV_WIN_INTERNAL_H_ */ diff --git a/deps/uv/src/win/loop-watcher.c b/deps/uv/src/win/loop-watcher.c new file mode 100644 index 00000000000..3038218c5a1 --- /dev/null +++ b/deps/uv/src/win/loop-watcher.c @@ -0,0 +1,128 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +void uv_loop_watcher_endgame(uv_handle_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb(handle); + } + + uv_unref(); + } +} + + +#define UV_LOOP_WATCHER_DEFINE(name, NAME) \ + int uv_##name##_init(uv_##name##_t* handle) { \ + handle->type = UV_##NAME; \ + handle->flags = 0; \ + handle->error = uv_ok_; \ + \ + uv_ref(); \ + \ + uv_counters()->handle_init++; \ + uv_counters()->prepare_init++; \ + \ + return 0; \ + } \ + \ + \ + int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ + uv_##name##_t* old_head; \ + \ + assert(handle->type == UV_##NAME); \ + \ + if (handle->flags & UV_HANDLE_ACTIVE) \ + return 0; \ + \ + old_head = LOOP->name##_handles; \ + \ + handle->name##_next = old_head; \ + handle->name##_prev = NULL; \ + \ + if (old_head) { \ + old_head->name##_prev = handle; \ + } \ + \ + LOOP->name##_handles = handle; \ + \ + handle->name##_cb = cb; \ + handle->flags |= UV_HANDLE_ACTIVE; \ + \ + return 0; \ + } \ + \ + \ + int uv_##name##_stop(uv_##name##_t* handle) { \ + assert(handle->type == UV_##NAME); \ + \ + if (!(handle->flags & UV_HANDLE_ACTIVE)) \ + return 0; \ + \ + /* Update loop head if needed */ \ + if (LOOP->name##_handles == handle) { \ + LOOP->name##_handles = handle->name##_next; \ + } \ + \ + /* Update the iterator-next pointer of needed */ \ + if (LOOP->next_##name##_handle == handle) { \ + LOOP->next_##name##_handle = handle->name##_next; \ + } \ + \ + if (handle->name##_prev) { \ + handle->name##_prev->name##_next = handle->name##_next; \ + } \ + if (handle->name##_next) { \ + handle->name##_next->name##_prev = handle->name##_prev; \ + } \ + \ + handle->flags &= ~UV_HANDLE_ACTIVE; \ + \ + return 0; \ + } \ + \ + \ + void uv_##name##_invoke() { \ + uv_##name##_t* handle; \ + \ + LOOP->next_##name##_handle = LOOP->name##_handles; \ + \ + while (LOOP->next_##name##_handle != NULL) { \ + handle = LOOP->next_##name##_handle; \ + LOOP->next_##name##_handle = handle->name##_next; \ + \ + handle->name##_cb(handle, 0); \ + } \ + } + +UV_LOOP_WATCHER_DEFINE(prepare, PREPARE) +UV_LOOP_WATCHER_DEFINE(check, CHECK) +UV_LOOP_WATCHER_DEFINE(idle, IDLE) diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c new file mode 100644 index 00000000000..f253d82a860 --- /dev/null +++ b/deps/uv/src/win/pipe.c @@ -0,0 +1,648 @@ +/* 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 +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* A zero-size buffer for use by uv_pipe_read */ +static char uv_zero_[] = ""; + + +int uv_pipe_init(uv_pipe_t* handle) { + uv_stream_init((uv_stream_t*)handle); + + handle->type = UV_NAMED_PIPE; + handle->reqs_pending = 0; + handle->pending_accepts = NULL; + handle->name = NULL; + + uv_counters()->pipe_init++; + + return 0; +} + + +static int uv_set_pipe_handle(uv_pipe_t* handle, HANDLE pipeHandle) { + DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + + if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) { + return -1; + } + + if (CreateIoCompletionPort(pipeHandle, + LOOP->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + return -1; + } + + return 0; +} + + +void uv_pipe_endgame(uv_pipe_t* handle) { + uv_err_t err; + int status; + + if (handle->flags & UV_HANDLE_SHUTTING && + !(handle->flags & UV_HANDLE_SHUT) && + handle->write_reqs_pending == 0) { + close_pipe(handle, &status, &err); + + if (handle->shutdown_req->cb) { + if (status == -1) { + LOOP->last_error = err; + } + handle->shutdown_req->cb(handle->shutdown_req, status); + } + handle->reqs_pending--; + } + + if (handle->flags & UV_HANDLE_CLOSING && + handle->reqs_pending == 0) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(); + } +} + + +/* Creates a pipe server. */ +/* TODO: make this work with UTF8 name */ +int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + int i; + uv_pipe_accept_t* req; + + if (!name) { + uv_set_sys_error(WSAEINVAL); + return -1; + } + + /* Make our own copy of the pipe name */ + handle->name = strdup(name); + if (!handle->name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + req = &handle->accept_reqs[i]; + uv_req_init((uv_req_t*) req); + req->type = UV_ACCEPT; + req->data = handle; + req->pipeHandle = INVALID_HANDLE_VALUE; + req->next_pending = NULL; + } + + handle->flags |= UV_HANDLE_PIPESERVER; + return 0; +} + + +static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { + HANDLE pipeHandle = INVALID_HANDLE_VALUE; + int errno; + uv_pipe_t* handle; + uv_connect_t* req; + + req = (uv_connect_t*)parameter; + assert(req); + handle = (uv_pipe_t*)req->handle; + assert(handle); + + /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait for the pipe to become available with WaitNamedPipe. */ + while (WaitNamedPipe(handle->name, 30000)) { + /* The pipe is now available, try to connect. */ + pipeHandle = CreateFile(handle->name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if (pipeHandle != INVALID_HANDLE_VALUE) { + break; + } + } + + if (pipeHandle != INVALID_HANDLE_VALUE && !uv_set_pipe_handle(handle, pipeHandle)) { + handle->handle = pipeHandle; + req->error = uv_ok_; + } else { + req->error = uv_new_sys_error(GetLastError()); + } + + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + /* Post completed */ + if (!PostQueuedCompletionStatus(LOOP->iocp, + 0, + 0, + &req->overlapped)) { + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); + } + + return 0; +} + + +/* TODO: make this work with UTF8 name */ +int uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, + const char* name, uv_connect_cb cb) { + int errno; + HANDLE pipeHandle; + + handle->handle = INVALID_HANDLE_VALUE; + + uv_req_init((uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + + pipeHandle = CreateFile(name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if (pipeHandle == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_PIPE_BUSY) { + /* Wait for the server to make a pipe instance available. */ + handle->name = strdup(name); + if (!handle->name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!QueueUserWorkItem(&pipe_connect_thread_proc, req, WT_EXECUTELONGFUNCTION)) { + errno = GetLastError(); + goto error; + } + + return 0; + } + + errno = GetLastError(); + goto error; + } + + if (uv_set_pipe_handle((uv_pipe_t*)req->handle, pipeHandle)) { + errno = GetLastError(); + goto error; + } + + handle->handle = pipeHandle; + + req->error = uv_ok_; + uv_insert_pending_req((uv_req_t*) req); + handle->reqs_pending++; + return 0; + +error: + if (pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); + } + uv_set_sys_error(errno); + return -1; +} + + +/* Cleans up uv_pipe_t (server or connection) and all resources associated with it */ +void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err) { + int i; + HANDLE pipeHandle; + + if (handle->name) { + free(handle->name); + handle->name; + } + + if (handle->flags & UV_HANDLE_PIPESERVER) { + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + pipeHandle = handle->accept_reqs[i].pipeHandle; + if (pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); + } + } + + } else if (handle->handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->handle); + } + + handle->flags |= UV_HANDLE_SHUT; +} + + +static void uv_pipe_queue_accept(uv_pipe_t* handle, uv_pipe_accept_t* req) { + HANDLE pipeHandle; + + assert(handle->flags & UV_HANDLE_LISTENING); + assert(req->pipeHandle == INVALID_HANDLE_VALUE); + + pipeHandle = CreateNamedPipe(handle->name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + 65536, + 65536, + 0, + NULL); + + if (pipeHandle == INVALID_HANDLE_VALUE) { + req->error = uv_new_sys_error(GetLastError()); + uv_insert_pending_req((uv_req_t*) req); + handle->reqs_pending++; + return; + } + + if (CreateIoCompletionPort(pipeHandle, + LOOP->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + req->error = uv_new_sys_error(GetLastError()); + uv_insert_pending_req((uv_req_t*) req); + handle->reqs_pending++; + return; + } + + /* Prepare the overlapped structure. */ + memset(&(req->overlapped), 0, sizeof(req->overlapped)); + + if (!ConnectNamedPipe(pipeHandle, &req->overlapped) && GetLastError() != ERROR_IO_PENDING) { + if (GetLastError() == ERROR_PIPE_CONNECTED) { + req->pipeHandle = pipeHandle; + req->error = uv_ok_; + } else { + /* Make this req pending reporting an error. */ + req->error = uv_new_sys_error(GetLastError()); + } + uv_insert_pending_req((uv_req_t*) req); + handle->reqs_pending++; + return; + } + + req->pipeHandle = pipeHandle; + handle->reqs_pending++; +} + + +int uv_pipe_accept(uv_pipe_t* server, uv_pipe_t* client) { + /* Find a connection instance that has been connected, but not yet accepted. */ + uv_pipe_accept_t* req = server->pending_accepts; + + if (!req) { + /* No valid connections found, so we error out. */ + uv_set_sys_error(WSAEWOULDBLOCK); + return -1; + } + + /* Initialize the client handle and copy the pipeHandle to the client */ + uv_pipe_init(client); + uv_connection_init((uv_stream_t*) client); + client->handle = req->pipeHandle; + + /* Prepare the req to pick up a new connection */ + server->pending_accepts = req->next_pending; + req->next_pending = NULL; + req->pipeHandle = INVALID_HANDLE_VALUE; + + if (!(server->flags & UV_HANDLE_CLOSING)) { + uv_pipe_queue_accept(server, req); + } + + return 0; +} + + +/* Starts listening for connections for the given pipe. */ +int uv_pipe_listen(uv_pipe_t* handle, uv_connection_cb cb) { + int i, errno; + + if (handle->flags & UV_HANDLE_LISTENING || + handle->flags & UV_HANDLE_READING) { + uv_set_sys_error(UV_EALREADY); + return -1; + } + + if (!(handle->flags & UV_HANDLE_PIPESERVER)) { + uv_set_sys_error(UV_ENOTSUP); + return -1; + } + + handle->flags |= UV_HANDLE_LISTENING; + handle->connection_cb = cb; + + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + uv_pipe_queue_accept(handle, &handle->accept_reqs[i]); + } + + return 0; +} + + +static void uv_pipe_queue_read(uv_pipe_t* handle) { + uv_req_t* req; + int result; + + assert(handle->flags & UV_HANDLE_READING); + assert(!(handle->flags & UV_HANDLE_READ_PENDING)); + + assert(handle->handle != INVALID_HANDLE_VALUE); + + req = &handle->read_req; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + /* Do 0-read */ + result = ReadFile(handle->handle, + &uv_zero_, + 0, + NULL, + &req->overlapped); + + if (!result && GetLastError() != ERROR_IO_PENDING) { + /* Make this req pending reporting an error. */ + req->error = uv_new_sys_error(WSAGetLastError()); + uv_insert_pending_req(req); + handle->reqs_pending++; + return; + } + + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; +} + + +int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(UV_EINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_READING) { + uv_set_sys_error(UV_EALREADY); + return -1; + } + + if (handle->flags & UV_HANDLE_EOF) { + uv_set_sys_error(UV_EOF); + return -1; + } + + handle->flags |= UV_HANDLE_READING; + handle->read_cb = read_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could stell be a */ + /* read request pending. */ + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + uv_pipe_queue_read(handle); + + return 0; +} + + +int uv_pipe_write(uv_write_t* req, uv_pipe_t* handle, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb) { + int result; + + if (bufcnt != 1) { + uv_set_sys_error(UV_ENOTSUP); + return -1; + } + + assert(handle->handle != INVALID_HANDLE_VALUE); + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(UV_EINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_sys_error(UV_EOF); + return -1; + } + + uv_req_init((uv_req_t*) req); + req->type = UV_WRITE; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + result = WriteFile(handle->handle, + bufs[0].base, + bufs[0].len, + NULL, + &req->overlapped); + + if (!result && GetLastError() != WSA_IO_PENDING) { + uv_set_sys_error(GetLastError()); + return -1; + } + + if (result) { + /* Request completed immediately. */ + req->queued_bytes = 0; + } else { + /* Request queued by the kernel. */ + req->queued_bytes = uv_count_bufs(bufs, bufcnt); + handle->write_queue_size += req->queued_bytes; + } + + handle->reqs_pending++; + handle->write_reqs_pending++; + + return 0; +} + + +void uv_process_pipe_read_req(uv_pipe_t* handle, uv_req_t* req) { + DWORD bytes, err, mode; + uv_buf_t buf; + + assert(handle->type == UV_NAMED_PIPE); + + handle->flags &= ~UV_HANDLE_READ_PENDING; + + if (req->error.code != UV_OK) { + /* An error occurred doing the 0-read. */ + if (handle->flags & UV_HANDLE_READING) { + /* Stop reading and report error. */ + handle->flags &= ~UV_HANDLE_READING; + LOOP->last_error = req->error; + buf.base = 0; + buf.len = 0; + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + } else { + /* + * Temporarily switch to non-blocking mode. + * This is so that ReadFile doesn't block if the read buffer is empty. + */ + mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT; + if (!SetNamedPipeHandleState(handle->handle, &mode, NULL, NULL)) { + /* We can't continue processing this read. */ + handle->flags &= ~UV_HANDLE_READING; + uv_set_sys_error(GetLastError()); + buf.base = 0; + buf.len = 0; + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + + /* Do non-blocking reads until the buffer is empty */ + while (handle->flags & UV_HANDLE_READING) { + buf = handle->alloc_cb((uv_stream_t*)handle, 65536); + assert(buf.len > 0); + + if (ReadFile(handle->handle, + buf.base, + buf.len, + &bytes, + NULL)) { + if (bytes > 0) { + /* Successful read */ + handle->read_cb((uv_stream_t*)handle, bytes, buf); + /* Read again only if bytes == buf.len */ + if (bytes < buf.len) { + break; + } + } else { + /* Connection closed */ + handle->flags &= ~UV_HANDLE_READING; + handle->flags |= UV_HANDLE_EOF; + LOOP->last_error.code = UV_EOF; + LOOP->last_error.sys_errno_ = ERROR_SUCCESS; + handle->read_cb((uv_stream_t*)handle, -1, buf); + break; + } + } else { + err = GetLastError(); + if (err == ERROR_NO_DATA) { + /* Read buffer was completely empty, report a 0-byte read. */ + uv_set_sys_error(WSAEWOULDBLOCK); + handle->read_cb((uv_stream_t*)handle, 0, buf); + } else { + /* Ouch! serious error. */ + uv_set_sys_error(err); + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + break; + } + } + + /* TODO: if the read callback stops reading we can't start reading again + because the pipe will still be in nowait mode. */ + if ((handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + /* Switch back to blocking mode so that we can use IOCP for 0-reads */ + mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + if (SetNamedPipeHandleState(handle->handle, &mode, NULL, NULL)) { + /* Post another 0-read */ + uv_pipe_queue_read(handle); + } else { + /* Report and continue. */ + /* We can't continue processing this read. */ + handle->flags &= ~UV_HANDLE_READING; + uv_set_sys_error(GetLastError()); + buf.base = 0; + buf.len = 0; + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_pipe_write_req(uv_pipe_t* handle, uv_write_t* req) { + assert(handle->type == UV_NAMED_PIPE); + + handle->write_queue_size -= req->queued_bytes; + + if (req->cb) { + LOOP->last_error = req->error; + ((uv_write_cb)req->cb)(req, LOOP->last_error.code == UV_OK ? 0 : -1); + } + + handle->write_reqs_pending--; + if (handle->write_reqs_pending == 0 && + handle->flags & UV_HANDLE_SHUTTING) { + uv_want_endgame((uv_handle_t*)handle); + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_pipe_accept_req(uv_pipe_t* handle, uv_req_t* raw_req) { + uv_pipe_accept_t* req = (uv_pipe_accept_t*) raw_req; + + assert(handle->type == UV_NAMED_PIPE); + + if (req->error.code == UV_OK) { + assert(req->pipeHandle != INVALID_HANDLE_VALUE); + + req->next_pending = handle->pending_accepts; + handle->pending_accepts = req; + + if (handle->connection_cb) { + handle->connection_cb((uv_handle_t*)handle, 0); + } + } else { + if (req->pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(req->pipeHandle); + req->pipeHandle = INVALID_HANDLE_VALUE; + } + if (!(handle->flags & UV_HANDLE_CLOSING)) { + uv_pipe_queue_accept(handle, req); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_pipe_connect_req(uv_pipe_t* handle, uv_connect_t* req) { + assert(handle->type == UV_NAMED_PIPE); + + if (req->cb) { + if (req->error.code == UV_OK) { + uv_connection_init((uv_stream_t*)handle); + ((uv_connect_cb)req->cb)(req, 0); + } else { + LOOP->last_error = req->error; + ((uv_connect_cb)req->cb)(req, -1); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} diff --git a/deps/uv/src/win/req.c b/deps/uv/src/win/req.c new file mode 100644 index 00000000000..627d7ec058f --- /dev/null +++ b/deps/uv/src/win/req.c @@ -0,0 +1,133 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +void uv_req_init(uv_req_t* req) { + uv_counters()->req_init++; + req->type = UV_UNKNOWN_REQ; + req->error = uv_ok_; +} + + +uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { + return CONTAINING_RECORD(overlapped, uv_req_t, overlapped); +} + + +void uv_insert_pending_req(uv_req_t* req) { + req->next_req = NULL; + if (LOOP->pending_reqs_tail) { + req->next_req = LOOP->pending_reqs_tail->next_req; + LOOP->pending_reqs_tail->next_req = req; + LOOP->pending_reqs_tail = req; + } else { + req->next_req = req; + LOOP->pending_reqs_tail = req; + } +} + + +static uv_req_t* uv_remove_pending_req() { + uv_req_t* req; + + if (LOOP->pending_reqs_tail) { + req = LOOP->pending_reqs_tail->next_req; + + if (req == LOOP->pending_reqs_tail) { + LOOP->pending_reqs_tail = NULL; + } else { + LOOP->pending_reqs_tail->next_req = req->next_req; + } + + return req; + + } else { + /* queue empty */ + return NULL; + } +} + + +#define DELEGATE_STREAM_REQ(req, method, handle_at) \ + do { \ + switch (((uv_handle_t*) (req)->handle_at)->type) { \ + case UV_TCP: \ + uv_process_tcp_##method##_req((uv_tcp_t*) ((req)->handle_at), req); \ + break; \ + \ + case UV_NAMED_PIPE: \ + uv_process_pipe_##method##_req((uv_pipe_t*) ((req)->handle_at), req); \ + break; \ + \ + default: \ + assert(0); \ + } \ + } while (0) + + +void uv_process_reqs() { + uv_req_t* req; + + while (req = uv_remove_pending_req()) { + switch (req->type) { + case UV_READ: + DELEGATE_STREAM_REQ(req, read, data); + break; + + case UV_WRITE: + DELEGATE_STREAM_REQ((uv_write_t*) req, write, handle); + break; + + case UV_ACCEPT: + DELEGATE_STREAM_REQ(req, accept, data); + break; + + case UV_CONNECT: + DELEGATE_STREAM_REQ((uv_connect_t*) req, connect, handle); + break; + + case UV_WAKEUP: + uv_process_async_wakeup_req((uv_async_t*) req->data, req); + break; + + case UV_ARES_EVENT_REQ: + uv_process_ares_event_req((uv_ares_action_t*) req->data, req); + break; + + case UV_ARES_CLEANUP_REQ: + uv_process_ares_cleanup_req((uv_ares_task_t*) req->data, req); + break; + + case UV_GETADDRINFO_REQ: + uv_process_getaddrinfo_req((uv_getaddrinfo_t*) req->data, req); + break; + + default: + assert(0); + } + } +} diff --git a/deps/uv/src/win/stream.c b/deps/uv/src/win/stream.c new file mode 100644 index 00000000000..acd67a0c58c --- /dev/null +++ b/deps/uv/src/win/stream.c @@ -0,0 +1,130 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +void uv_stream_init(uv_stream_t* handle) { + handle->write_queue_size = 0; + handle->flags = 0; + handle->error = uv_ok_; + + uv_counters()->handle_init++; + uv_counters()->stream_init++; + + uv_ref(); +} + + +void uv_connection_init(uv_stream_t* handle) { + handle->flags |= UV_HANDLE_CONNECTION; + handle->write_reqs_pending = 0; + + uv_req_init((uv_req_t*) &(handle->read_req)); + handle->read_req.type = UV_READ; + handle->read_req.data = handle; +} + + +int uv_accept(uv_handle_t* server, uv_stream_t* client) { + assert(client->type == server->type); + + if (server->type == UV_TCP) { + return uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client); + } else if (server->type == UV_NAMED_PIPE) { + return uv_pipe_accept((uv_pipe_t*)server, (uv_pipe_t*)client); + } + + return -1; +} + + +int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { + if (handle->type == UV_TCP) { + return uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); + } else if (handle->type == UV_NAMED_PIPE) { + return uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb); + } + + return -1; +} + + +int uv_read_stop(uv_stream_t* handle) { + handle->flags &= ~UV_HANDLE_READING; + + return 0; +} + + +int uv_write(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb) { + if (handle->type == UV_TCP) { + return uv_tcp_write(req, (uv_tcp_t*) handle, bufs, bufcnt, cb); + } else if (handle->type == UV_NAMED_PIPE) { + return uv_pipe_write(req, (uv_pipe_t*) handle, bufs, bufcnt, cb); + } + + uv_set_sys_error(WSAEINVAL); + return -1; +} + + +int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_sys_error(WSAESHUTDOWN); + return -1; + } + + uv_req_init((uv_req_t*) req); + req->type = UV_SHUTDOWN; + req->handle = handle; + req->cb = cb; + + handle->flags |= UV_HANDLE_SHUTTING; + handle->shutdown_req = req; + handle->reqs_pending++; + + uv_want_endgame((uv_handle_t*)handle); + + return 0; +} + + +size_t uv_count_bufs(uv_buf_t bufs[], int count) { + size_t bytes = 0; + int i; + + for (i = 0; i < count; i++) { + bytes += (size_t)bufs[i].len; + } + + return bytes; +} diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c new file mode 100644 index 00000000000..ace5353d20c --- /dev/null +++ b/deps/uv/src/win/tcp.c @@ -0,0 +1,895 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + +/* + * Guids and typedefs for winsock extension functions + * Mingw32 doesn't have these :-( + */ +#ifndef WSAID_ACCEPTEX +# define WSAID_ACCEPTEX \ + {0xb5367df1, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +# define WSAID_CONNECTEX \ + {0x25a207b9, 0xddf3, 0x4660, \ + {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}} + +# define WSAID_GETACCEPTEXSOCKADDRS \ + {0xb5367df2, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +# define WSAID_DISCONNECTEX \ + {0x7fda2e11, 0x8630, 0x436f, \ + {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} + +# define WSAID_TRANSMITFILE \ + {0xb5367df0, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + + typedef BOOL PASCAL (*LPFN_ACCEPTEX) + (SOCKET sListenSocket, + SOCKET sAcceptSocket, + PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPDWORD lpdwBytesReceived, + LPOVERLAPPED lpOverlapped); + + typedef BOOL PASCAL (*LPFN_CONNECTEX) + (SOCKET s, + const struct sockaddr* name, + int namelen, + PVOID lpSendBuffer, + DWORD dwSendDataLength, + LPDWORD lpdwBytesSent, + LPOVERLAPPED lpOverlapped); + + typedef void PASCAL (*LPFN_GETACCEPTEXSOCKADDRS) + (PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPSOCKADDR* LocalSockaddr, + LPINT LocalSockaddrLength, + LPSOCKADDR* RemoteSockaddr, + LPINT RemoteSockaddrLength); + + typedef BOOL PASCAL (*LPFN_DISCONNECTEX) + (SOCKET hSocket, + LPOVERLAPPED lpOverlapped, + DWORD dwFlags, + DWORD reserved); + + typedef BOOL PASCAL (*LPFN_TRANSMITFILE) + (SOCKET hSocket, + HANDLE hFile, + DWORD nNumberOfBytesToWrite, + DWORD nNumberOfBytesPerSend, + LPOVERLAPPED lpOverlapped, + LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, + DWORD dwFlags); +#endif + +/* + * MinGW is missing this too + */ +#ifndef SO_UPDATE_CONNECT_CONTEXT +# define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif + + +/* Pointers to winsock extension functions to be retrieved dynamically */ +static LPFN_CONNECTEX pConnectEx; +static LPFN_ACCEPTEX pAcceptEx; +static LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs; +static LPFN_DISCONNECTEX pDisconnectEx; +static LPFN_TRANSMITFILE pTransmitFile; + +/* IPv6 version of these extension functions */ +static LPFN_CONNECTEX pConnectEx6; +static LPFN_ACCEPTEX pAcceptEx6; +static LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs6; +static LPFN_DISCONNECTEX pDisconnectEx6; +static LPFN_TRANSMITFILE pTransmitFile6; + +/* Ip address used to bind to any port at any interface */ +static struct sockaddr_in uv_addr_ip4_any_; +static struct sockaddr_in6 uv_addr_ip6_any_; + +/* A zero-size buffer for use by uv_tcp_read */ +static char uv_zero_[] = ""; + +/* mark if IPv6 sockets are supported */ +static BOOL uv_allow_ipv6 = FALSE; + + +/* + * Retrieves the pointer to a winsock extension function. + */ +static BOOL uv_get_extension_function(SOCKET socket, GUID guid, + void **target) { + DWORD result, bytes; + + result = WSAIoctl(socket, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, + sizeof(guid), + (void*)target, + sizeof(*target), + &bytes, + NULL, + NULL); + + if (result == SOCKET_ERROR) { + *target = NULL; + return FALSE; + } else { + return TRUE; + } +} + + +/* + * Setup tcp subsystem + */ +void uv_winsock_startup() { + const GUID wsaid_connectex = WSAID_CONNECTEX; + const GUID wsaid_acceptex = WSAID_ACCEPTEX; + const GUID wsaid_getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; + const GUID wsaid_disconnectex = WSAID_DISCONNECTEX; + const GUID wsaid_transmitfile = WSAID_TRANSMITFILE; + + WSADATA wsa_data; + int errorno; + SOCKET dummy; + SOCKET dummy6; + + /* Initialize winsock */ + errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (errorno != 0) { + uv_fatal_error(errorno, "WSAStartup"); + } + + /* Set implicit binding address used by connectEx */ + uv_addr_ip4_any_ = uv_ip4_addr("0.0.0.0", 0); + uv_addr_ip6_any_ = uv_ip6_addr("::1", 0); + + /* Retrieve the needed winsock extension function pointers. */ + dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (dummy == INVALID_SOCKET) { + uv_fatal_error(WSAGetLastError(), "socket"); + } + + if (!uv_get_extension_function(dummy, + wsaid_connectex, + (void**)&pConnectEx) || + !uv_get_extension_function(dummy, + wsaid_acceptex, + (void**)&pAcceptEx) || + !uv_get_extension_function(dummy, + wsaid_getacceptexsockaddrs, + (void**)&pGetAcceptExSockAddrs) || + !uv_get_extension_function(dummy, + wsaid_disconnectex, + (void**)&pDisconnectEx) || + !uv_get_extension_function(dummy, + wsaid_transmitfile, + (void**)&pTransmitFile)) { + uv_fatal_error(WSAGetLastError(), + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)"); + } + + if (closesocket(dummy) == SOCKET_ERROR) { + uv_fatal_error(WSAGetLastError(), "closesocket"); + } + + /* optional IPv6 versions of winsock extension functions */ + dummy6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); + if (dummy6 != INVALID_SOCKET) { + uv_allow_ipv6 = TRUE; + + if (!uv_get_extension_function(dummy6, + wsaid_connectex, + (void**)&pConnectEx6) || + !uv_get_extension_function(dummy6, + wsaid_acceptex, + (void**)&pAcceptEx6) || + !uv_get_extension_function(dummy6, + wsaid_getacceptexsockaddrs, + (void**)&pGetAcceptExSockAddrs6) || + !uv_get_extension_function(dummy6, + wsaid_disconnectex, + (void**)&pDisconnectEx6) || + !uv_get_extension_function(dummy6, + wsaid_transmitfile, + (void**)&pTransmitFile6)) { + uv_allow_ipv6 = FALSE; + } + + if (closesocket(dummy6) == SOCKET_ERROR) { + uv_fatal_error(WSAGetLastError(), "closesocket"); + } + } +} + + +static int uv_tcp_set_socket(uv_tcp_t* handle, SOCKET socket) { + DWORD yes = 1; + + assert(handle->socket == INVALID_SOCKET); + + /* Set the socket to nonblocking mode */ + if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { + uv_set_sys_error(WSAGetLastError()); + return -1; + } + + /* Make the socket non-inheritable */ + if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { + uv_set_sys_error(GetLastError()); + return -1; + } + + /* Associate it with the I/O completion port. */ + /* Use uv_handle_t pointer as completion key. */ + if (CreateIoCompletionPort((HANDLE)socket, + LOOP->iocp, + (ULONG_PTR)socket, + 0) == NULL) { + uv_set_sys_error(GetLastError()); + return -1; + } + + handle->socket = socket; + + return 0; +} + + +int uv_tcp_init(uv_tcp_t* handle) { + uv_stream_init((uv_stream_t*)handle); + + handle->socket = INVALID_SOCKET; + handle->type = UV_TCP; + handle->reqs_pending = 0; + handle->accept_socket = INVALID_SOCKET; + + uv_counters()->tcp_init++; + + return 0; +} + + +void uv_tcp_endgame(uv_tcp_t* handle) { + uv_err_t err; + int status; + + if (handle->flags & UV_HANDLE_SHUTTING && + !(handle->flags & UV_HANDLE_SHUT) && + handle->write_reqs_pending == 0) { + + if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) { + status = 0; + handle->flags |= UV_HANDLE_SHUT; + } else { + status = -1; + err = uv_new_sys_error(WSAGetLastError()); + } + if (handle->shutdown_req->cb) { + if (status == -1) { + LOOP->last_error = err; + } + handle->shutdown_req->cb(handle->shutdown_req, status); + } + handle->reqs_pending--; + } + + if (handle->flags & UV_HANDLE_CLOSING && + handle->reqs_pending == 0) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(); + } +} + + +static int uv__bind(uv_tcp_t* handle, int domain, struct sockaddr* addr, int addrsize) { + DWORD err; + int r; + SOCKET sock; + + if (handle->socket == INVALID_SOCKET) { + sock = socket(domain, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + uv_set_sys_error(WSAGetLastError()); + return -1; + } + + if (uv_tcp_set_socket(handle, sock) == -1) { + closesocket(sock); + return -1; + } + } + + r = bind(handle->socket, addr, addrsize); + + if (r == SOCKET_ERROR) { + err = WSAGetLastError(); + if (err == WSAEADDRINUSE) { + /* Some errors are not to be reported until connect() or listen() */ + handle->error = uv_new_sys_error(err); + handle->flags |= UV_HANDLE_BIND_ERROR; + } else { + uv_set_sys_error(err); + return -1; + } + } + + handle->flags |= UV_HANDLE_BOUND; + + return 0; +} + + +int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { + if (addr.sin_family != AF_INET) { + uv_set_sys_error(WSAEFAULT); + return -1; + } + + return uv__bind(handle, AF_INET, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); +} + + +int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { + if (addr.sin6_family != AF_INET6) { + uv_set_sys_error(WSAEFAULT); + return -1; + } + if (uv_allow_ipv6) { + handle->flags |= UV_HANDLE_IPV6; + return uv__bind(handle, AF_INET6, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)); + } else { + uv_new_sys_error(UV_EAFNOSUPPORT); + return -1; + } +} + + +static void uv_tcp_queue_accept(uv_tcp_t* handle) { + uv_req_t* req; + BOOL success; + DWORD bytes; + SOCKET accept_socket; + short family; + LPFN_ACCEPTEX pAcceptExFamily; + + assert(handle->flags & UV_HANDLE_LISTENING); + assert(handle->accept_socket == INVALID_SOCKET); + + /* Prepare the uv_req structure. */ + req = &handle->accept_req; + + /* choose family and extension function */ + if ((handle->flags & UV_HANDLE_IPV6) != 0) { + family = AF_INET6; + pAcceptExFamily = pAcceptEx6; + } else { + family = AF_INET; + pAcceptExFamily = pAcceptEx; + } + + /* Open a socket for the accepted connection. */ + accept_socket = socket(family, SOCK_STREAM, 0); + if (accept_socket == INVALID_SOCKET) { + req->error = uv_new_sys_error(WSAGetLastError()); + uv_insert_pending_req(req); + handle->reqs_pending++; + return; + } + + /* Prepare the overlapped structure. */ + memset(&(req->overlapped), 0, sizeof(req->overlapped)); + + success = pAcceptExFamily(handle->socket, + accept_socket, + (void*)&handle->accept_buffer, + 0, + sizeof(struct sockaddr_storage), + sizeof(struct sockaddr_storage), + &bytes, + &req->overlapped); + + if (!success && WSAGetLastError() != ERROR_IO_PENDING) { + /* Make this req pending reporting an error. */ + req->error = uv_new_sys_error(WSAGetLastError()); + uv_insert_pending_req(req); + handle->reqs_pending++; + /* Destroy the preallocated client socket. */ + closesocket(accept_socket); + return; + } + + handle->accept_socket = accept_socket; + + handle->reqs_pending++; +} + + +static void uv_tcp_queue_read(uv_tcp_t* handle) { + uv_req_t* req; + uv_buf_t buf; + int result; + DWORD bytes, flags; + + assert(handle->flags & UV_HANDLE_READING); + assert(!(handle->flags & UV_HANDLE_READ_PENDING)); + + req = &handle->read_req; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + buf.base = (char*) &uv_zero_; + buf.len = 0; + + flags = 0; + result = WSARecv(handle->socket, + (WSABUF*)&buf, + 1, + &bytes, + &flags, + &req->overlapped, + NULL); + if (result != 0 && WSAGetLastError() != ERROR_IO_PENDING) { + /* Make this req pending reporting an error. */ + req->error = uv_new_sys_error(WSAGetLastError()); + uv_insert_pending_req(req); + handle->reqs_pending++; + return; + } + + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; +} + + +int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { + assert(backlog > 0); + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + LOOP->last_error = handle->error; + return -1; + } + + if (handle->flags & UV_HANDLE_LISTENING || + handle->flags & UV_HANDLE_READING) { + /* Already listening. */ + uv_set_sys_error(WSAEALREADY); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind(handle, uv_addr_ip4_any_) < 0) + return -1; + + if (listen(handle->socket, backlog) == SOCKET_ERROR) { + uv_set_sys_error(WSAGetLastError()); + return -1; + } + + handle->flags |= UV_HANDLE_LISTENING; + handle->connection_cb = cb; + + uv_req_init(&(handle->accept_req)); + handle->accept_req.type = UV_ACCEPT; + handle->accept_req.data = handle; + uv_tcp_queue_accept(handle); + + return 0; +} + + +int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { + int rv = 0; + + if (server->accept_socket == INVALID_SOCKET) { + uv_set_sys_error(WSAENOTCONN); + return -1; + } + + if (uv_tcp_set_socket(client, server->accept_socket) == -1) { + closesocket(server->accept_socket); + rv = -1; + } else { + uv_connection_init((uv_stream_t*)client); + } + + server->accept_socket = INVALID_SOCKET; + + if (!(server->flags & UV_HANDLE_CLOSING)) { + uv_tcp_queue_accept(server); + } + + return rv; +} + + +int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_READING) { + uv_set_sys_error(WSAEALREADY); + return -1; + } + + if (handle->flags & UV_HANDLE_EOF) { + uv_set_sys_error(WSAESHUTDOWN); + return -1; + } + + handle->flags |= UV_HANDLE_READING; + handle->read_cb = read_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could stell be a */ + /* read request pending. */ + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + uv_tcp_queue_read(handle); + + return 0; +} + + +int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in address, uv_connect_cb cb) { + int addrsize = sizeof(struct sockaddr_in); + BOOL success; + DWORD bytes; + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + LOOP->last_error = handle->error; + return -1; + } + + if (address.sin_family != AF_INET) { + uv_set_sys_error(WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind(handle, uv_addr_ip4_any_) < 0) + return -1; + + uv_req_init((uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + success = pConnectEx(handle->socket, + (struct sockaddr*) &address, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (!success && WSAGetLastError() != ERROR_IO_PENDING) { + uv_set_sys_error(WSAGetLastError()); + return -1; + } + + handle->reqs_pending++; + + return 0; +} + + +int uv_tcp_connect6(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in6 address, uv_connect_cb cb) { + int addrsize = sizeof(struct sockaddr_in6); + BOOL success; + DWORD bytes; + + if (!uv_allow_ipv6) { + uv_new_sys_error(UV_EAFNOSUPPORT); + return -1; + } + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + LOOP->last_error = handle->error; + return -1; + } + + if (address.sin6_family != AF_INET6) { + uv_set_sys_error(WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind6(handle, uv_addr_ip6_any_) < 0) + return -1; + + uv_req_init((uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + success = pConnectEx6(handle->socket, + (struct sockaddr*) &address, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (!success && WSAGetLastError() != ERROR_IO_PENDING) { + uv_set_sys_error(WSAGetLastError()); + return -1; + } + + handle->reqs_pending++; + + return 0; +} + + +int uv_getsockname(uv_tcp_t* handle, struct sockaddr* name, int* namelen) { + int result; + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_sys_error(WSAESHUTDOWN); + return -1; + } + + result = getsockname(handle->socket, name, namelen); + if (result != 0) { + uv_set_sys_error(WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_write(uv_write_t* req, uv_tcp_t* handle, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb) { + int result; + DWORD bytes, err; + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_sys_error(WSAESHUTDOWN); + return -1; + } + + uv_req_init((uv_req_t*) req); + req->type = UV_WRITE; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + result = WSASend(handle->socket, + (WSABUF*)bufs, + bufcnt, + &bytes, + 0, + &req->overlapped, + NULL); + if (result != 0) { + err = WSAGetLastError(); + if (err != WSA_IO_PENDING) { + /* Send failed due to an error. */ + uv_set_sys_error(WSAGetLastError()); + return -1; + } + } + + if (result == 0) { + /* Request completed immediately. */ + req->queued_bytes = 0; + } else { + /* Request queued by the kernel. */ + req->queued_bytes = uv_count_bufs(bufs, bufcnt); + handle->write_queue_size += req->queued_bytes; + } + + handle->reqs_pending++; + handle->write_reqs_pending++; + + return 0; +} + + +void uv_process_tcp_read_req(uv_tcp_t* handle, uv_req_t* req) { + DWORD bytes, flags, err; + uv_buf_t buf; + + assert(handle->type == UV_TCP); + + handle->flags &= ~UV_HANDLE_READ_PENDING; + + if (req->error.code != UV_OK) { + /* An error occurred doing the 0-read. */ + if ((handle->flags & UV_HANDLE_READING)) { + handle->flags &= ~UV_HANDLE_READING; + LOOP->last_error = req->error; + buf.base = 0; + buf.len = 0; + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + } else { + /* Do nonblocking reads until the buffer is empty */ + while (handle->flags & UV_HANDLE_READING) { + buf = handle->alloc_cb((uv_stream_t*)handle, 65536); + assert(buf.len > 0); + flags = 0; + if (WSARecv(handle->socket, + (WSABUF*)&buf, + 1, + &bytes, + &flags, + NULL, + NULL) != SOCKET_ERROR) { + if (bytes > 0) { + /* Successful read */ + handle->read_cb((uv_stream_t*)handle, bytes, buf); + /* Read again only if bytes == buf.len */ + if (bytes < buf.len) { + break; + } + } else { + /* Connection closed */ + handle->flags &= ~UV_HANDLE_READING; + handle->flags |= UV_HANDLE_EOF; + LOOP->last_error.code = UV_EOF; + LOOP->last_error.sys_errno_ = ERROR_SUCCESS; + handle->read_cb((uv_stream_t*)handle, -1, buf); + break; + } + } else { + err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) { + /* Read buffer was completely empty, report a 0-byte read. */ + uv_set_sys_error(WSAEWOULDBLOCK); + handle->read_cb((uv_stream_t*)handle, 0, buf); + } else { + /* Ouch! serious error. */ + uv_set_sys_error(err); + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + break; + } + } + + /* Post another 0-read if still reading and not closing. */ + if ((handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + uv_tcp_queue_read(handle); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_tcp_write_req(uv_tcp_t* handle, uv_write_t* req) { + assert(handle->type == UV_TCP); + + handle->write_queue_size -= req->queued_bytes; + + if (req->cb) { + LOOP->last_error = req->error; + ((uv_write_cb)req->cb)(req, LOOP->last_error.code == UV_OK ? 0 : -1); + } + + handle->write_reqs_pending--; + if (handle->flags & UV_HANDLE_SHUTTING && + handle->write_reqs_pending == 0) { + uv_want_endgame((uv_handle_t*)handle); + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_tcp_accept_req(uv_tcp_t* handle, uv_req_t* req) { + assert(handle->type == UV_TCP); + + /* If handle->accepted_socket is not a valid socket, then */ + /* uv_queue_accept must have failed. This is a serious error. We stop */ + /* accepting connections and report this error to the connection */ + /* callback. */ + if (handle->accept_socket == INVALID_SOCKET) { + if (handle->flags & UV_HANDLE_LISTENING) { + handle->flags &= ~UV_HANDLE_LISTENING; + if (handle->connection_cb) { + LOOP->last_error = req->error; + handle->connection_cb((uv_handle_t*)handle, -1); + } + } + } else if (req->error.code == UV_OK && + setsockopt(handle->accept_socket, + SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (char*)&handle->socket, + sizeof(handle->socket)) == 0) { + /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */ + if (handle->connection_cb) { + handle->connection_cb((uv_handle_t*)handle, 0); + } + } else { + /* Error related to accepted socket is ignored because the server */ + /* socket may still be healthy. If the server socket is broken + /* uv_queue_accept will detect it. */ + closesocket(handle->accept_socket); + if (handle->flags & UV_HANDLE_LISTENING) { + uv_tcp_queue_accept(handle); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_tcp_connect_req(uv_tcp_t* handle, uv_connect_t* req) { + assert(handle->type == UV_TCP); + + if (req->cb) { + if (req->error.code == UV_OK) { + if (setsockopt(handle->socket, + SOL_SOCKET, + SO_UPDATE_CONNECT_CONTEXT, + NULL, + 0) == 0) { + uv_connection_init((uv_stream_t*)handle); + ((uv_connect_cb)req->cb)(req, 0); + } else { + uv_set_sys_error(WSAGetLastError()); + ((uv_connect_cb)req->cb)(req, -1); + } + } else { + LOOP->last_error = req->error; + ((uv_connect_cb)req->cb)(req, -1); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} diff --git a/deps/uv/src/win/timer.c b/deps/uv/src/win/timer.c new file mode 100644 index 00000000000..6f74a2ca005 --- /dev/null +++ b/deps/uv/src/win/timer.c @@ -0,0 +1,232 @@ +/* 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 +#include + +#include "uv.h" +#include "internal.h" +#include "tree.h" + + +/* The resolution of the high-resolution clock. */ +static int64_t uv_ticks_per_msec_ = 0; + + + +void uv_update_time() { + LARGE_INTEGER counter; + + if (!QueryPerformanceCounter(&counter)) + uv_fatal_error(GetLastError(), "QueryPerformanceCounter"); + + LOOP->time = counter.QuadPart / uv_ticks_per_msec_; +} + + +int64_t uv_now() { + return LOOP->time; +} + + +uint64_t uv_hrtime(void) { + assert(0 && "implement me"); + return 0; +} + + +void uv_timer_startup() { + LARGE_INTEGER timer_frequency; + + /* Initialize the event loop time */ + if (!QueryPerformanceFrequency(&timer_frequency)) + uv_fatal_error(GetLastError(), "QueryPerformanceFrequency"); + uv_ticks_per_msec_ = timer_frequency.QuadPart / 1000; +} + + +static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) { + if (a->due < b->due) + return -1; + if (a->due > b->due) + return 1; + if ((intptr_t)a < (intptr_t)b) + return -1; + if ((intptr_t)a > (intptr_t)b) + return 1; + return 0; +} + + +RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare); + + +int uv_timer_init(uv_timer_t* handle) { + uv_counters()->handle_init++; + uv_counters()->timer_init++; + + handle->type = UV_TIMER; + handle->flags = 0; + handle->error = uv_ok_; + handle->timer_cb = NULL; + handle->repeat = 0; + + uv_ref(); + + return 0; +} + + +void uv_timer_endgame(uv_timer_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(); + } +} + + +int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, int64_t timeout, int64_t repeat) { + if (handle->flags & UV_HANDLE_ACTIVE) { + RB_REMOVE(uv_timer_tree_s, &LOOP->timers, handle); + } + + handle->timer_cb = timer_cb; + handle->due = LOOP->time + timeout; + handle->repeat = repeat; + handle->flags |= UV_HANDLE_ACTIVE; + + if (RB_INSERT(uv_timer_tree_s, &LOOP->timers, handle) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + + return 0; +} + + +int uv_timer_stop(uv_timer_t* handle) { + if (!(handle->flags & UV_HANDLE_ACTIVE)) + return 0; + + RB_REMOVE(uv_timer_tree_s, &LOOP->timers, handle); + + handle->flags &= ~UV_HANDLE_ACTIVE; + + return 0; +} + + +int uv_timer_again(uv_timer_t* handle) { + /* If timer_cb is NULL that means that the timer was never started. */ + if (!handle->timer_cb) { + uv_set_sys_error(ERROR_INVALID_DATA); + return -1; + } + + if (handle->flags & UV_HANDLE_ACTIVE) { + RB_REMOVE(uv_timer_tree_s, &LOOP->timers, handle); + handle->flags &= ~UV_HANDLE_ACTIVE; + } + + if (handle->repeat) { + handle->due = LOOP->time + handle->repeat; + + if (RB_INSERT(uv_timer_tree_s, &LOOP->timers, handle) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + + handle->flags |= UV_HANDLE_ACTIVE; + } + + return 0; +} + + +void uv_timer_set_repeat(uv_timer_t* handle, int64_t repeat) { + assert(handle->type == UV_TIMER); + handle->repeat = repeat; +} + + +int64_t uv_timer_get_repeat(uv_timer_t* handle) { + assert(handle->type == UV_TIMER); + return handle->repeat; +} + + +DWORD uv_get_poll_timeout() { + uv_timer_t* timer; + int64_t delta; + + /* Check if there are any running timers */ + timer = RB_MIN(uv_timer_tree_s, &LOOP->timers); + if (timer) { + uv_update_time(); + + delta = timer->due - LOOP->time; + if (delta >= UINT_MAX) { + /* Can't have a timeout greater than UINT_MAX, and a timeout value of */ + /* UINT_MAX means infinite, so that's no good either. */ + return UINT_MAX - 1; + } else if (delta < 0) { + /* Negative timeout values are not allowed */ + return 0; + } else { + return (DWORD)delta; + } + } else { + /* No timers */ + return INFINITE; + } +} + + +void uv_process_timers() { + uv_timer_t* timer; + + /* Call timer callbacks */ + for (timer = RB_MIN(uv_timer_tree_s, &LOOP->timers); + timer != NULL && timer->due <= LOOP->time; + timer = RB_MIN(uv_timer_tree_s, &LOOP->timers)) { + RB_REMOVE(uv_timer_tree_s, &LOOP->timers, timer); + + if (timer->repeat != 0) { + /* If it is a repeating timer, reschedule with repeat timeout. */ + timer->due += timer->repeat; + if (timer->due < LOOP->time) { + timer->due = LOOP->time; + } + if (RB_INSERT(uv_timer_tree_s, &LOOP->timers, timer) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + } else { + /* If non-repeating, mark the timer as inactive. */ + timer->flags &= ~UV_HANDLE_ACTIVE; + } + + timer->timer_cb((uv_timer_t*) timer, 0); + } +} diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c new file mode 100644 index 00000000000..3299e7a11d1 --- /dev/null +++ b/deps/uv/src/win/util.c @@ -0,0 +1,82 @@ +/* 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 +#include +#include + +#include "uv.h" +#include "internal.h" + + +int uv_utf16_to_utf8(wchar_t* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size) { + return WideCharToMultiByte(CP_UTF8, 0, utf16Buffer, utf16Size, utf8Buffer, utf8Size, NULL, NULL); +} + + +int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, size_t utf16Size) { + return MultiByteToWideChar(CP_UTF8, 0, utf8Buffer, -1, utf16Buffer, utf16Size); +} + + +int uv_exepath(char* buffer, size_t* size) { + int retVal; + size_t utf16Size; + wchar_t* utf16Buffer; + + if (!buffer || !size) { + return -1; + } + + utf16Buffer = (wchar_t*)malloc(sizeof(wchar_t) * *size); + if (!utf16Buffer) { + retVal = -1; + goto done; + } + + /* Get the path as UTF-16 */ + utf16Size = GetModuleFileNameW(NULL, utf16Buffer, *size - 1); + if (utf16Size <= 0) { + uv_set_sys_error(GetLastError()); + retVal = -1; + goto done; + } + + utf16Buffer[utf16Size] = L'\0'; + + /* Convert to UTF-8 */ + *size = uv_utf16_to_utf8(utf16Buffer, utf16Size, buffer, *size); + if (!*size) { + uv_set_sys_error(GetLastError()); + retVal = -1; + goto done; + } + + buffer[*size] = '\0'; + retVal = 0; + +done: + if (utf16Buffer) { + free(utf16Buffer); + } + + return retVal; +}