mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-20 09:38:21 +00:00
EQStream abstraction layer
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "atomicops-inl.h"
|
||||
#include "handle-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
|
||||
void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) {
|
||||
if (handle->flags & UV__HANDLE_CLOSING &&
|
||||
!handle->async_sent) {
|
||||
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||
uv__handle_close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
|
||||
uv_req_t* req;
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_ASYNC);
|
||||
handle->async_sent = 0;
|
||||
handle->async_cb = async_cb;
|
||||
|
||||
req = &handle->async_req;
|
||||
uv_req_init(loop, req);
|
||||
req->type = UV_WAKEUP;
|
||||
req->data = handle;
|
||||
|
||||
uv__handle_start(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_async_close(uv_loop_t* loop, uv_async_t* handle) {
|
||||
if (!((uv_async_t*)handle)->async_sent) {
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
}
|
||||
|
||||
uv__handle_closing(handle);
|
||||
}
|
||||
|
||||
|
||||
int uv_async_send(uv_async_t* handle) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
|
||||
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)) {
|
||||
POST_COMPLETION_FOR_REQ(loop, &handle->async_req);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
|
||||
uv_req_t* req) {
|
||||
assert(handle->type == UV_ASYNC);
|
||||
assert(req->type == UV_WAKEUP);
|
||||
|
||||
handle->async_sent = 0;
|
||||
|
||||
if (handle->flags & UV__HANDLE_CLOSING) {
|
||||
uv_want_endgame(loop, (uv_handle_t*)handle);
|
||||
} else if (handle->async_cb != NULL) {
|
||||
handle->async_cb(handle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* 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_ATOMICOPS_INL_H_
|
||||
#define UV_WIN_ATOMICOPS_INL_H_
|
||||
|
||||
#include "uv.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
|
||||
|
||||
#endif /* UV_WIN_ATOMICOPS_INL_H_ */
|
||||
@@ -0,0 +1,493 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
|
||||
static uv_loop_t default_loop_struct;
|
||||
static uv_loop_t* default_loop_ptr;
|
||||
|
||||
/* uv_once initialization guards */
|
||||
static uv_once_t uv_init_guard_ = UV_ONCE_INIT;
|
||||
|
||||
|
||||
#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR))
|
||||
/* Our crt debug report handler allows us to temporarily disable asserts
|
||||
* just for the current thread.
|
||||
*/
|
||||
|
||||
UV_THREAD_LOCAL int uv__crt_assert_enabled = TRUE;
|
||||
|
||||
static int uv__crt_dbg_report_handler(int report_type, char *message, int *ret_val) {
|
||||
if (uv__crt_assert_enabled || report_type != _CRT_ASSERT)
|
||||
return FALSE;
|
||||
|
||||
if (ret_val) {
|
||||
/* Set ret_val to 0 to continue with normal execution.
|
||||
* Set ret_val to 1 to trigger a breakpoint.
|
||||
*/
|
||||
|
||||
if(IsDebuggerPresent())
|
||||
*ret_val = 1;
|
||||
else
|
||||
*ret_val = 0;
|
||||
}
|
||||
|
||||
/* Don't call _CrtDbgReport. */
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
UV_THREAD_LOCAL int uv__crt_assert_enabled = FALSE;
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800
|
||||
static void uv__crt_invalid_parameter_handler(const wchar_t* expression,
|
||||
const wchar_t* function, const wchar_t * file, unsigned int line,
|
||||
uintptr_t reserved) {
|
||||
/* No-op. */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void uv_init(void) {
|
||||
/* Tell Windows that we will handle critical errors. */
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
|
||||
SEM_NOOPENFILEERRORBOX);
|
||||
|
||||
/* Tell the CRT to not exit the application when an invalid parameter is
|
||||
* passed. The main issue is that invalid FDs will trigger this behavior.
|
||||
*/
|
||||
#if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800
|
||||
_set_invalid_parameter_handler(uv__crt_invalid_parameter_handler);
|
||||
#endif
|
||||
|
||||
/* We also need to setup our debug report handler because some CRT
|
||||
* functions (eg _get_osfhandle) raise an assert when called with invalid
|
||||
* FDs even though they return the proper error code in the release build.
|
||||
*/
|
||||
#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR))
|
||||
_CrtSetReportHook(uv__crt_dbg_report_handler);
|
||||
#endif
|
||||
|
||||
/* Fetch winapi function pointers. This must be done first because other
|
||||
* initialization code might need these function pointers to be loaded.
|
||||
*/
|
||||
uv_winapi_init();
|
||||
|
||||
/* Initialize winsock */
|
||||
uv_winsock_init();
|
||||
|
||||
/* Initialize FS */
|
||||
uv_fs_init();
|
||||
|
||||
/* Initialize signal stuff */
|
||||
uv_signals_init();
|
||||
|
||||
/* Initialize console */
|
||||
uv_console_init();
|
||||
|
||||
/* Initialize utilities */
|
||||
uv__util_init();
|
||||
}
|
||||
|
||||
|
||||
int uv_loop_init(uv_loop_t* loop) {
|
||||
int err;
|
||||
|
||||
/* Initialize libuv itself first */
|
||||
uv__once_init();
|
||||
|
||||
/* Create an I/O completion port */
|
||||
loop->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
|
||||
if (loop->iocp == NULL)
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
|
||||
/* To prevent uninitialized memory access, loop->time must be initialized
|
||||
* to zero before calling uv_update_time for the first time.
|
||||
*/
|
||||
loop->time = 0;
|
||||
uv_update_time(loop);
|
||||
|
||||
QUEUE_INIT(&loop->wq);
|
||||
QUEUE_INIT(&loop->handle_queue);
|
||||
QUEUE_INIT(&loop->active_reqs);
|
||||
loop->active_handles = 0;
|
||||
|
||||
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;
|
||||
|
||||
memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets);
|
||||
|
||||
loop->active_tcp_streams = 0;
|
||||
loop->active_udp_streams = 0;
|
||||
|
||||
loop->timer_counter = 0;
|
||||
loop->stop_flag = 0;
|
||||
|
||||
err = uv_mutex_init(&loop->wq_mutex);
|
||||
if (err)
|
||||
goto fail_mutex_init;
|
||||
|
||||
err = uv_async_init(loop, &loop->wq_async, uv__work_done);
|
||||
if (err)
|
||||
goto fail_async_init;
|
||||
|
||||
uv__handle_unref(&loop->wq_async);
|
||||
loop->wq_async.flags |= UV__HANDLE_INTERNAL;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_async_init:
|
||||
uv_mutex_destroy(&loop->wq_mutex);
|
||||
|
||||
fail_mutex_init:
|
||||
CloseHandle(loop->iocp);
|
||||
loop->iocp = INVALID_HANDLE_VALUE;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void uv__once_init(void) {
|
||||
uv_once(&uv_init_guard_, uv_init);
|
||||
}
|
||||
|
||||
|
||||
void uv__loop_close(uv_loop_t* loop) {
|
||||
size_t i;
|
||||
|
||||
/* close the async handle without needing an extra loop iteration */
|
||||
assert(!loop->wq_async.async_sent);
|
||||
loop->wq_async.close_cb = NULL;
|
||||
uv__handle_closing(&loop->wq_async);
|
||||
uv__handle_close(&loop->wq_async);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(loop->poll_peer_sockets); i++) {
|
||||
SOCKET sock = loop->poll_peer_sockets[i];
|
||||
if (sock != 0 && sock != INVALID_SOCKET)
|
||||
closesocket(sock);
|
||||
}
|
||||
|
||||
uv_mutex_lock(&loop->wq_mutex);
|
||||
assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
|
||||
assert(!uv__has_active_reqs(loop));
|
||||
uv_mutex_unlock(&loop->wq_mutex);
|
||||
uv_mutex_destroy(&loop->wq_mutex);
|
||||
|
||||
CloseHandle(loop->iocp);
|
||||
}
|
||||
|
||||
|
||||
int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
|
||||
|
||||
int uv_backend_fd(const uv_loop_t* loop) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int uv_backend_timeout(const uv_loop_t* loop) {
|
||||
if (loop->stop_flag != 0)
|
||||
return 0;
|
||||
|
||||
if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop))
|
||||
return 0;
|
||||
|
||||
if (loop->pending_reqs_tail)
|
||||
return 0;
|
||||
|
||||
if (loop->endgame_handles)
|
||||
return 0;
|
||||
|
||||
if (loop->idle_handles)
|
||||
return 0;
|
||||
|
||||
return uv__next_timeout(loop);
|
||||
}
|
||||
|
||||
|
||||
static void uv_poll(uv_loop_t* loop, DWORD timeout) {
|
||||
DWORD bytes;
|
||||
ULONG_PTR key;
|
||||
OVERLAPPED* overlapped;
|
||||
uv_req_t* req;
|
||||
int repeat;
|
||||
uint64_t timeout_time;
|
||||
|
||||
timeout_time = loop->time + timeout;
|
||||
|
||||
for (repeat = 0; ; repeat++) {
|
||||
GetQueuedCompletionStatus(loop->iocp,
|
||||
&bytes,
|
||||
&key,
|
||||
&overlapped,
|
||||
timeout);
|
||||
|
||||
if (overlapped) {
|
||||
/* Package was dequeued */
|
||||
req = uv_overlapped_to_req(overlapped);
|
||||
uv_insert_pending_req(loop, req);
|
||||
|
||||
/* Some time might have passed waiting for I/O,
|
||||
* so update the loop time here.
|
||||
*/
|
||||
uv_update_time(loop);
|
||||
} else if (GetLastError() != WAIT_TIMEOUT) {
|
||||
/* Serious error */
|
||||
uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus");
|
||||
} else if (timeout > 0) {
|
||||
/* GetQueuedCompletionStatus can occasionally return a little early.
|
||||
* Make sure that the desired timeout target time is reached.
|
||||
*/
|
||||
uv_update_time(loop);
|
||||
if (timeout_time > loop->time) {
|
||||
timeout = (DWORD)(timeout_time - loop->time);
|
||||
/* The first call to GetQueuedCompletionStatus should return very
|
||||
* close to the target time and the second should reach it, but
|
||||
* this is not stated in the documentation. To make sure a busy
|
||||
* loop cannot happen, the timeout is increased exponentially
|
||||
* starting on the third round.
|
||||
*/
|
||||
timeout += repeat ? (1 << (repeat - 1)) : 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) {
|
||||
BOOL success;
|
||||
uv_req_t* req;
|
||||
OVERLAPPED_ENTRY overlappeds[128];
|
||||
ULONG count;
|
||||
ULONG i;
|
||||
int repeat;
|
||||
uint64_t timeout_time;
|
||||
|
||||
timeout_time = loop->time + timeout;
|
||||
|
||||
for (repeat = 0; ; repeat++) {
|
||||
success = pGetQueuedCompletionStatusEx(loop->iocp,
|
||||
overlappeds,
|
||||
ARRAY_SIZE(overlappeds),
|
||||
&count,
|
||||
timeout,
|
||||
FALSE);
|
||||
|
||||
if (success) {
|
||||
for (i = 0; i < count; i++) {
|
||||
/* Package was dequeued */
|
||||
req = uv_overlapped_to_req(overlappeds[i].lpOverlapped);
|
||||
uv_insert_pending_req(loop, req);
|
||||
}
|
||||
|
||||
/* Some time might have passed waiting for I/O,
|
||||
* so update the loop time here.
|
||||
*/
|
||||
uv_update_time(loop);
|
||||
} else if (GetLastError() != WAIT_TIMEOUT) {
|
||||
/* Serious error */
|
||||
uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx");
|
||||
} else if (timeout > 0) {
|
||||
/* GetQueuedCompletionStatus can occasionally return a little early.
|
||||
* Make sure that the desired timeout target time is reached.
|
||||
*/
|
||||
uv_update_time(loop);
|
||||
if (timeout_time > loop->time) {
|
||||
timeout = (DWORD)(timeout_time - loop->time);
|
||||
/* The first call to GetQueuedCompletionStatus should return very
|
||||
* close to the target time and the second should reach it, but
|
||||
* this is not stated in the documentation. To make sure a busy
|
||||
* loop cannot happen, the timeout is increased exponentially
|
||||
* starting on the third round.
|
||||
*/
|
||||
timeout += repeat ? (1 << (repeat - 1)) : 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__loop_alive(const uv_loop_t* loop) {
|
||||
return loop->active_handles > 0 ||
|
||||
!QUEUE_EMPTY(&loop->active_reqs) ||
|
||||
loop->endgame_handles != NULL;
|
||||
}
|
||||
|
||||
|
||||
int uv_loop_alive(const uv_loop_t* loop) {
|
||||
return uv__loop_alive(loop);
|
||||
}
|
||||
|
||||
|
||||
int uv_run(uv_loop_t *loop, uv_run_mode mode) {
|
||||
DWORD timeout;
|
||||
int r;
|
||||
int ran_pending;
|
||||
void (*poll)(uv_loop_t* loop, DWORD timeout);
|
||||
|
||||
if (pGetQueuedCompletionStatusEx)
|
||||
poll = &uv_poll_ex;
|
||||
else
|
||||
poll = &uv_poll;
|
||||
|
||||
r = uv__loop_alive(loop);
|
||||
if (!r)
|
||||
uv_update_time(loop);
|
||||
|
||||
while (r != 0 && loop->stop_flag == 0) {
|
||||
uv_update_time(loop);
|
||||
uv_process_timers(loop);
|
||||
|
||||
ran_pending = uv_process_reqs(loop);
|
||||
uv_idle_invoke(loop);
|
||||
uv_prepare_invoke(loop);
|
||||
|
||||
timeout = 0;
|
||||
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
|
||||
timeout = uv_backend_timeout(loop);
|
||||
|
||||
(*poll)(loop, timeout);
|
||||
|
||||
uv_check_invoke(loop);
|
||||
uv_process_endgames(loop);
|
||||
|
||||
if (mode == UV_RUN_ONCE) {
|
||||
/* UV_RUN_ONCE implies forward progress: at least one callback must have
|
||||
* been invoked when it returns. uv__io_poll() can return without doing
|
||||
* I/O (meaning: no callbacks) when its timeout expires - which means we
|
||||
* have pending timers that satisfy the forward progress constraint.
|
||||
*
|
||||
* UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
|
||||
* the check.
|
||||
*/
|
||||
uv_process_timers(loop);
|
||||
}
|
||||
|
||||
r = uv__loop_alive(loop);
|
||||
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
|
||||
break;
|
||||
}
|
||||
|
||||
/* The if statement lets the compiler compile it to a conditional store.
|
||||
* Avoids dirtying a cache line.
|
||||
*/
|
||||
if (loop->stop_flag != 0)
|
||||
loop->stop_flag = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) {
|
||||
uv_os_fd_t fd_out;
|
||||
|
||||
switch (handle->type) {
|
||||
case UV_TCP:
|
||||
fd_out = (uv_os_fd_t)((uv_tcp_t*) handle)->socket;
|
||||
break;
|
||||
|
||||
case UV_NAMED_PIPE:
|
||||
fd_out = ((uv_pipe_t*) handle)->handle;
|
||||
break;
|
||||
|
||||
case UV_TTY:
|
||||
fd_out = ((uv_tty_t*) handle)->handle;
|
||||
break;
|
||||
|
||||
case UV_UDP:
|
||||
fd_out = (uv_os_fd_t)((uv_udp_t*) handle)->socket;
|
||||
break;
|
||||
|
||||
case UV_POLL:
|
||||
fd_out = (uv_os_fd_t)((uv_poll_t*) handle)->socket;
|
||||
break;
|
||||
|
||||
default:
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (uv_is_closing(handle) || fd_out == INVALID_HANDLE_VALUE)
|
||||
return UV_EBADF;
|
||||
|
||||
*fd = fd_out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) {
|
||||
int r;
|
||||
int len;
|
||||
SOCKET socket;
|
||||
|
||||
if (handle == NULL || value == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (handle->type == UV_TCP)
|
||||
socket = ((uv_tcp_t*) handle)->socket;
|
||||
else if (handle->type == UV_UDP)
|
||||
socket = ((uv_udp_t*) handle)->socket;
|
||||
else
|
||||
return UV_ENOTSUP;
|
||||
|
||||
len = sizeof(*value);
|
||||
|
||||
if (*value == 0)
|
||||
r = getsockopt(socket, SOL_SOCKET, optname, (char*) value, &len);
|
||||
else
|
||||
r = setsockopt(socket, SOL_SOCKET, optname, (const char*) value, len);
|
||||
|
||||
if (r == SOCKET_ERROR)
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
static int uv__dlerror(uv_lib_t* lib, int errorno);
|
||||
|
||||
|
||||
int uv_dlopen(const char* filename, uv_lib_t* lib) {
|
||||
WCHAR filename_w[32768];
|
||||
|
||||
lib->handle = NULL;
|
||||
lib->errmsg = NULL;
|
||||
|
||||
if (!MultiByteToWideChar(CP_UTF8,
|
||||
0,
|
||||
filename,
|
||||
-1,
|
||||
filename_w,
|
||||
ARRAY_SIZE(filename_w))) {
|
||||
return uv__dlerror(lib, GetLastError());
|
||||
}
|
||||
|
||||
lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
if (lib->handle == NULL) {
|
||||
return uv__dlerror(lib, GetLastError());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_dlclose(uv_lib_t* lib) {
|
||||
if (lib->errmsg) {
|
||||
LocalFree((void*)lib->errmsg);
|
||||
lib->errmsg = NULL;
|
||||
}
|
||||
|
||||
if (lib->handle) {
|
||||
/* Ignore errors. No good way to signal them without leaking memory. */
|
||||
FreeLibrary(lib->handle);
|
||||
lib->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) {
|
||||
*ptr = (void*) GetProcAddress(lib->handle, name);
|
||||
return uv__dlerror(lib, *ptr ? 0 : GetLastError());
|
||||
}
|
||||
|
||||
|
||||
const char* uv_dlerror(const uv_lib_t* lib) {
|
||||
return lib->errmsg ? lib->errmsg : "no error";
|
||||
}
|
||||
|
||||
|
||||
static void uv__format_fallback_error(uv_lib_t* lib, int errorno){
|
||||
DWORD_PTR args[1] = { (DWORD_PTR) errorno };
|
||||
LPSTR fallback_error = "error: %1!d!";
|
||||
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_STRING |
|
||||
FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||||
fallback_error, 0, 0,
|
||||
(LPSTR) &lib->errmsg,
|
||||
0, (va_list*) args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int uv__dlerror(uv_lib_t* lib, int errorno) {
|
||||
DWORD res;
|
||||
|
||||
if (lib->errmsg) {
|
||||
LocalFree((void*)lib->errmsg);
|
||||
lib->errmsg = NULL;
|
||||
}
|
||||
|
||||
if (errorno) {
|
||||
res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
|
||||
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
||||
(LPSTR) &lib->errmsg, 0, NULL);
|
||||
if (!res && GetLastError() == ERROR_MUI_FILE_NOT_FOUND) {
|
||||
res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
|
||||
0, (LPSTR) &lib->errmsg, 0, NULL);
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
uv__format_fallback_error(lib, errorno);
|
||||
}
|
||||
}
|
||||
|
||||
return errorno ? -1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
int uv_translate_sys_error(int sys_errno) {
|
||||
if (sys_errno <= 0) {
|
||||
return sys_errno; /* If < 0 then it's already a libuv error. */
|
||||
}
|
||||
|
||||
switch (sys_errno) {
|
||||
case ERROR_NOACCESS: return UV_EACCES;
|
||||
case WSAEACCES: return UV_EACCES;
|
||||
case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE;
|
||||
case WSAEADDRINUSE: return UV_EADDRINUSE;
|
||||
case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL;
|
||||
case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT;
|
||||
case WSAEWOULDBLOCK: return UV_EAGAIN;
|
||||
case WSAEALREADY: return UV_EALREADY;
|
||||
case ERROR_INVALID_FLAGS: return UV_EBADF;
|
||||
case ERROR_INVALID_HANDLE: return UV_EBADF;
|
||||
case ERROR_LOCK_VIOLATION: return UV_EBUSY;
|
||||
case ERROR_PIPE_BUSY: return UV_EBUSY;
|
||||
case ERROR_SHARING_VIOLATION: return UV_EBUSY;
|
||||
case ERROR_OPERATION_ABORTED: return UV_ECANCELED;
|
||||
case WSAEINTR: return UV_ECANCELED;
|
||||
case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET;
|
||||
case ERROR_CONNECTION_ABORTED: return UV_ECONNABORTED;
|
||||
case WSAECONNABORTED: return UV_ECONNABORTED;
|
||||
case ERROR_CONNECTION_REFUSED: return UV_ECONNREFUSED;
|
||||
case WSAECONNREFUSED: return UV_ECONNREFUSED;
|
||||
case ERROR_NETNAME_DELETED: return UV_ECONNRESET;
|
||||
case WSAECONNRESET: return UV_ECONNRESET;
|
||||
case ERROR_ALREADY_EXISTS: return UV_EEXIST;
|
||||
case ERROR_FILE_EXISTS: return UV_EEXIST;
|
||||
case ERROR_BUFFER_OVERFLOW: return UV_EFAULT;
|
||||
case WSAEFAULT: return UV_EFAULT;
|
||||
case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH;
|
||||
case WSAEHOSTUNREACH: return UV_EHOSTUNREACH;
|
||||
case ERROR_INSUFFICIENT_BUFFER: return UV_EINVAL;
|
||||
case ERROR_INVALID_DATA: return UV_EINVAL;
|
||||
case ERROR_INVALID_PARAMETER: return UV_EINVAL;
|
||||
case ERROR_SYMLINK_NOT_SUPPORTED: return UV_EINVAL;
|
||||
case WSAEINVAL: return UV_EINVAL;
|
||||
case WSAEPFNOSUPPORT: return UV_EINVAL;
|
||||
case WSAESOCKTNOSUPPORT: return UV_EINVAL;
|
||||
case ERROR_BEGINNING_OF_MEDIA: return UV_EIO;
|
||||
case ERROR_BUS_RESET: return UV_EIO;
|
||||
case ERROR_CRC: return UV_EIO;
|
||||
case ERROR_DEVICE_DOOR_OPEN: return UV_EIO;
|
||||
case ERROR_DEVICE_REQUIRES_CLEANING: return UV_EIO;
|
||||
case ERROR_DISK_CORRUPT: return UV_EIO;
|
||||
case ERROR_EOM_OVERFLOW: return UV_EIO;
|
||||
case ERROR_FILEMARK_DETECTED: return UV_EIO;
|
||||
case ERROR_GEN_FAILURE: return UV_EIO;
|
||||
case ERROR_INVALID_BLOCK_LENGTH: return UV_EIO;
|
||||
case ERROR_IO_DEVICE: return UV_EIO;
|
||||
case ERROR_NO_DATA_DETECTED: return UV_EIO;
|
||||
case ERROR_NO_SIGNAL_SENT: return UV_EIO;
|
||||
case ERROR_OPEN_FAILED: return UV_EIO;
|
||||
case ERROR_SETMARK_DETECTED: return UV_EIO;
|
||||
case ERROR_SIGNAL_REFUSED: return UV_EIO;
|
||||
case WSAEISCONN: return UV_EISCONN;
|
||||
case ERROR_CANT_RESOLVE_FILENAME: return UV_ELOOP;
|
||||
case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE;
|
||||
case WSAEMFILE: return UV_EMFILE;
|
||||
case WSAEMSGSIZE: return UV_EMSGSIZE;
|
||||
case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG;
|
||||
case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH;
|
||||
case WSAENETUNREACH: return UV_ENETUNREACH;
|
||||
case WSAENOBUFS: return UV_ENOBUFS;
|
||||
case ERROR_BAD_PATHNAME: return UV_ENOENT;
|
||||
case ERROR_DIRECTORY: return UV_ENOENT;
|
||||
case ERROR_FILE_NOT_FOUND: return UV_ENOENT;
|
||||
case ERROR_INVALID_NAME: return UV_ENOENT;
|
||||
case ERROR_INVALID_DRIVE: return UV_ENOENT;
|
||||
case ERROR_INVALID_REPARSE_DATA: return UV_ENOENT;
|
||||
case ERROR_MOD_NOT_FOUND: return UV_ENOENT;
|
||||
case ERROR_PATH_NOT_FOUND: return UV_ENOENT;
|
||||
case WSAHOST_NOT_FOUND: return UV_ENOENT;
|
||||
case WSANO_DATA: return UV_ENOENT;
|
||||
case ERROR_NOT_ENOUGH_MEMORY: return UV_ENOMEM;
|
||||
case ERROR_OUTOFMEMORY: return UV_ENOMEM;
|
||||
case ERROR_CANNOT_MAKE: return UV_ENOSPC;
|
||||
case ERROR_DISK_FULL: return UV_ENOSPC;
|
||||
case ERROR_EA_TABLE_FULL: return UV_ENOSPC;
|
||||
case ERROR_END_OF_MEDIA: return UV_ENOSPC;
|
||||
case ERROR_HANDLE_DISK_FULL: return UV_ENOSPC;
|
||||
case ERROR_NOT_CONNECTED: return UV_ENOTCONN;
|
||||
case WSAENOTCONN: return UV_ENOTCONN;
|
||||
case ERROR_DIR_NOT_EMPTY: return UV_ENOTEMPTY;
|
||||
case WSAENOTSOCK: return UV_ENOTSOCK;
|
||||
case ERROR_NOT_SUPPORTED: return UV_ENOTSUP;
|
||||
case ERROR_BROKEN_PIPE: return UV_EOF;
|
||||
case ERROR_ACCESS_DENIED: return UV_EPERM;
|
||||
case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM;
|
||||
case ERROR_BAD_PIPE: return UV_EPIPE;
|
||||
case ERROR_NO_DATA: return UV_EPIPE;
|
||||
case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE;
|
||||
case WSAESHUTDOWN: return UV_EPIPE;
|
||||
case WSAEPROTONOSUPPORT: return UV_EPROTONOSUPPORT;
|
||||
case ERROR_WRITE_PROTECT: return UV_EROFS;
|
||||
case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT;
|
||||
case WSAETIMEDOUT: return UV_ETIMEDOUT;
|
||||
case ERROR_NOT_SAME_DEVICE: return UV_EXDEV;
|
||||
case ERROR_INVALID_FUNCTION: return UV_EISDIR;
|
||||
case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG;
|
||||
default: return UV_UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,527 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
|
||||
const unsigned int uv_directory_watcher_buffer_size = 4096;
|
||||
|
||||
|
||||
static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
|
||||
uv_fs_event_t* handle) {
|
||||
assert(handle->dir_handle != INVALID_HANDLE_VALUE);
|
||||
assert(!handle->req_pending);
|
||||
|
||||
memset(&(handle->req.u.io.overlapped), 0,
|
||||
sizeof(handle->req.u.io.overlapped));
|
||||
if (!ReadDirectoryChangesW(handle->dir_handle,
|
||||
handle->buffer,
|
||||
uv_directory_watcher_buffer_size,
|
||||
(handle->flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
FILE_NOTIFY_CHANGE_SIZE |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
FILE_NOTIFY_CHANGE_LAST_ACCESS |
|
||||
FILE_NOTIFY_CHANGE_CREATION |
|
||||
FILE_NOTIFY_CHANGE_SECURITY,
|
||||
NULL,
|
||||
&handle->req.u.io.overlapped,
|
||||
NULL)) {
|
||||
/* Make this req pending reporting an error. */
|
||||
SET_REQ_ERROR(&handle->req, GetLastError());
|
||||
uv_insert_pending_req(loop, (uv_req_t*)&handle->req);
|
||||
}
|
||||
|
||||
handle->req_pending = 1;
|
||||
}
|
||||
|
||||
static void uv_relative_path(const WCHAR* filename,
|
||||
const WCHAR* dir,
|
||||
WCHAR** relpath) {
|
||||
size_t relpathlen;
|
||||
size_t filenamelen = wcslen(filename);
|
||||
size_t dirlen = wcslen(dir);
|
||||
if (dirlen > 0 && dir[dirlen - 1] == '\\')
|
||||
dirlen--;
|
||||
relpathlen = filenamelen - dirlen - 1;
|
||||
*relpath = uv__malloc((relpathlen + 1) * sizeof(WCHAR));
|
||||
if (!*relpath)
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
wcsncpy(*relpath, filename + dirlen + 1, relpathlen);
|
||||
(*relpath)[relpathlen] = L'\0';
|
||||
}
|
||||
|
||||
static int uv_split_path(const WCHAR* filename, WCHAR** dir,
|
||||
WCHAR** file) {
|
||||
int len = wcslen(filename);
|
||||
int i = len;
|
||||
while (i > 0 && filename[--i] != '\\' && filename[i] != '/');
|
||||
|
||||
if (i == 0) {
|
||||
if (dir) {
|
||||
*dir = (WCHAR*)uv__malloc((MAX_PATH + 1) * sizeof(WCHAR));
|
||||
if (!*dir) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
}
|
||||
|
||||
if (!GetCurrentDirectoryW(MAX_PATH, *dir)) {
|
||||
uv__free(*dir);
|
||||
*dir = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*file = wcsdup(filename);
|
||||
} else {
|
||||
if (dir) {
|
||||
*dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));
|
||||
if (!*dir) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
}
|
||||
wcsncpy(*dir, filename, i + 1);
|
||||
(*dir)[i + 1] = L'\0';
|
||||
}
|
||||
|
||||
*file = (WCHAR*)uv__malloc((len - i) * sizeof(WCHAR));
|
||||
if (!*file) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
}
|
||||
wcsncpy(*file, filename + i + 1, len - i - 1);
|
||||
(*file)[len - i - 1] = L'\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);
|
||||
handle->dir_handle = INVALID_HANDLE_VALUE;
|
||||
handle->buffer = NULL;
|
||||
handle->req_pending = 0;
|
||||
handle->filew = NULL;
|
||||
handle->short_filew = NULL;
|
||||
handle->dirw = NULL;
|
||||
|
||||
uv_req_init(loop, (uv_req_t*)&handle->req);
|
||||
handle->req.type = UV_FS_EVENT_REQ;
|
||||
handle->req.data = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_start(uv_fs_event_t* handle,
|
||||
uv_fs_event_cb cb,
|
||||
const char* path,
|
||||
unsigned int flags) {
|
||||
int name_size, is_path_dir;
|
||||
DWORD attr, last_error;
|
||||
WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
|
||||
WCHAR short_path[MAX_PATH];
|
||||
|
||||
if (uv__is_active(handle))
|
||||
return UV_EINVAL;
|
||||
|
||||
handle->cb = cb;
|
||||
handle->path = uv__strdup(path);
|
||||
if (!handle->path) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
}
|
||||
|
||||
uv__handle_start(handle);
|
||||
|
||||
/* Convert name to UTF16. */
|
||||
|
||||
name_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) *
|
||||
sizeof(WCHAR);
|
||||
pathw = (WCHAR*)uv__malloc(name_size);
|
||||
if (!pathw) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
}
|
||||
|
||||
if (!MultiByteToWideChar(CP_UTF8,
|
||||
0,
|
||||
path,
|
||||
-1,
|
||||
pathw,
|
||||
name_size / sizeof(WCHAR))) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
/* Determine whether path is a file or a directory. */
|
||||
attr = GetFileAttributesW(pathw);
|
||||
if (attr == INVALID_FILE_ATTRIBUTES) {
|
||||
last_error = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
|
||||
|
||||
if (is_path_dir) {
|
||||
/* path is a directory, so that's the directory that we will watch. */
|
||||
handle->dirw = pathw;
|
||||
dir_to_watch = pathw;
|
||||
} else {
|
||||
/*
|
||||
* path is a file. So we split path into dir & file parts, and
|
||||
* watch the dir directory.
|
||||
*/
|
||||
|
||||
/* Convert to short path. */
|
||||
if (!GetShortPathNameW(pathw, short_path, ARRAY_SIZE(short_path))) {
|
||||
last_error = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (uv_split_path(pathw, &dir, &handle->filew) != 0) {
|
||||
last_error = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) {
|
||||
last_error = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
dir_to_watch = dir;
|
||||
uv__free(pathw);
|
||||
pathw = NULL;
|
||||
}
|
||||
|
||||
handle->dir_handle = CreateFileW(dir_to_watch,
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_DELETE |
|
||||
FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS |
|
||||
FILE_FLAG_OVERLAPPED,
|
||||
NULL);
|
||||
|
||||
if (dir) {
|
||||
uv__free(dir);
|
||||
dir = NULL;
|
||||
}
|
||||
|
||||
if (handle->dir_handle == INVALID_HANDLE_VALUE) {
|
||||
last_error = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (CreateIoCompletionPort(handle->dir_handle,
|
||||
handle->loop->iocp,
|
||||
(ULONG_PTR)handle,
|
||||
0) == NULL) {
|
||||
last_error = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!handle->buffer) {
|
||||
handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);
|
||||
}
|
||||
if (!handle->buffer) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
}
|
||||
|
||||
memset(&(handle->req.u.io.overlapped), 0,
|
||||
sizeof(handle->req.u.io.overlapped));
|
||||
|
||||
if (!ReadDirectoryChangesW(handle->dir_handle,
|
||||
handle->buffer,
|
||||
uv_directory_watcher_buffer_size,
|
||||
(flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
FILE_NOTIFY_CHANGE_SIZE |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
FILE_NOTIFY_CHANGE_LAST_ACCESS |
|
||||
FILE_NOTIFY_CHANGE_CREATION |
|
||||
FILE_NOTIFY_CHANGE_SECURITY,
|
||||
NULL,
|
||||
&handle->req.u.io.overlapped,
|
||||
NULL)) {
|
||||
last_error = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
handle->req_pending = 1;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (handle->path) {
|
||||
uv__free(handle->path);
|
||||
handle->path = NULL;
|
||||
}
|
||||
|
||||
if (handle->filew) {
|
||||
uv__free(handle->filew);
|
||||
handle->filew = NULL;
|
||||
}
|
||||
|
||||
if (handle->short_filew) {
|
||||
uv__free(handle->short_filew);
|
||||
handle->short_filew = NULL;
|
||||
}
|
||||
|
||||
uv__free(pathw);
|
||||
|
||||
if (handle->dir_handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle->dir_handle);
|
||||
handle->dir_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (handle->buffer) {
|
||||
uv__free(handle->buffer);
|
||||
handle->buffer = NULL;
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(last_error);
|
||||
}
|
||||
|
||||
|
||||
int uv_fs_event_stop(uv_fs_event_t* handle) {
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
if (handle->dir_handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle->dir_handle);
|
||||
handle->dir_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
uv__handle_stop(handle);
|
||||
|
||||
if (handle->filew) {
|
||||
uv__free(handle->filew);
|
||||
handle->filew = NULL;
|
||||
}
|
||||
|
||||
if (handle->short_filew) {
|
||||
uv__free(handle->short_filew);
|
||||
handle->short_filew = NULL;
|
||||
}
|
||||
|
||||
if (handle->path) {
|
||||
uv__free(handle->path);
|
||||
handle->path = NULL;
|
||||
}
|
||||
|
||||
if (handle->dirw) {
|
||||
uv__free(handle->dirw);
|
||||
handle->dirw = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
|
||||
uv_fs_event_t* handle) {
|
||||
FILE_NOTIFY_INFORMATION* file_info;
|
||||
int err, sizew, size;
|
||||
char* filename = NULL;
|
||||
WCHAR* filenamew = NULL;
|
||||
WCHAR* long_filenamew = NULL;
|
||||
DWORD offset = 0;
|
||||
|
||||
assert(req->type == UV_FS_EVENT_REQ);
|
||||
assert(handle->req_pending);
|
||||
handle->req_pending = 0;
|
||||
|
||||
/* Don't report any callbacks if:
|
||||
* - We're closing, just push the handle onto the endgame queue
|
||||
* - We are not active, just ignore the callback
|
||||
*/
|
||||
if (!uv__is_active(handle)) {
|
||||
if (handle->flags & UV__HANDLE_CLOSING) {
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
|
||||
|
||||
if (REQ_SUCCESS(req)) {
|
||||
if (req->u.io.overlapped.InternalHigh > 0) {
|
||||
do {
|
||||
file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
|
||||
assert(!filename);
|
||||
assert(!filenamew);
|
||||
assert(!long_filenamew);
|
||||
|
||||
/*
|
||||
* Fire the event only if we were asked to watch a directory,
|
||||
* or if the filename filter matches.
|
||||
*/
|
||||
if (handle->dirw ||
|
||||
_wcsnicmp(handle->filew, file_info->FileName,
|
||||
file_info->FileNameLength / sizeof(WCHAR)) == 0 ||
|
||||
_wcsnicmp(handle->short_filew, file_info->FileName,
|
||||
file_info->FileNameLength / sizeof(WCHAR)) == 0) {
|
||||
|
||||
if (handle->dirw) {
|
||||
/*
|
||||
* We attempt to resolve the long form of the file name explicitly.
|
||||
* We only do this for file names that might still exist on disk.
|
||||
* If this fails, we use the name given by ReadDirectoryChangesW.
|
||||
* This may be the long form or the 8.3 short name in some cases.
|
||||
*/
|
||||
if (file_info->Action != FILE_ACTION_REMOVED &&
|
||||
file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
|
||||
/* Construct a full path to the file. */
|
||||
size = wcslen(handle->dirw) +
|
||||
file_info->FileNameLength / sizeof(WCHAR) + 2;
|
||||
|
||||
filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
|
||||
if (!filenamew) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
}
|
||||
|
||||
_snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,
|
||||
file_info->FileNameLength / sizeof(WCHAR),
|
||||
file_info->FileName);
|
||||
|
||||
filenamew[size - 1] = L'\0';
|
||||
|
||||
/* Convert to long name. */
|
||||
size = GetLongPathNameW(filenamew, NULL, 0);
|
||||
|
||||
if (size) {
|
||||
long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
|
||||
if (!long_filenamew) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
}
|
||||
|
||||
size = GetLongPathNameW(filenamew, long_filenamew, size);
|
||||
if (size) {
|
||||
long_filenamew[size] = '\0';
|
||||
} else {
|
||||
uv__free(long_filenamew);
|
||||
long_filenamew = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
uv__free(filenamew);
|
||||
|
||||
if (long_filenamew) {
|
||||
/* Get the file name out of the long path. */
|
||||
uv_relative_path(long_filenamew,
|
||||
handle->dirw,
|
||||
&filenamew);
|
||||
uv__free(long_filenamew);
|
||||
long_filenamew = filenamew;
|
||||
sizew = -1;
|
||||
} else {
|
||||
/* We couldn't get the long filename, use the one reported. */
|
||||
filenamew = file_info->FileName;
|
||||
sizew = file_info->FileNameLength / sizeof(WCHAR);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Removed or renamed events cannot be resolved to the long form.
|
||||
* We therefore use the name given by ReadDirectoryChangesW.
|
||||
* This may be the long form or the 8.3 short name in some cases.
|
||||
*/
|
||||
filenamew = file_info->FileName;
|
||||
sizew = file_info->FileNameLength / sizeof(WCHAR);
|
||||
}
|
||||
} else {
|
||||
/* We already have the long name of the file, so just use it. */
|
||||
filenamew = handle->filew;
|
||||
sizew = -1;
|
||||
}
|
||||
|
||||
/* Convert the filename to utf8. */
|
||||
uv__convert_utf16_to_utf8(filenamew, sizew, &filename);
|
||||
|
||||
switch (file_info->Action) {
|
||||
case FILE_ACTION_ADDED:
|
||||
case FILE_ACTION_REMOVED:
|
||||
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||
case FILE_ACTION_RENAMED_NEW_NAME:
|
||||
handle->cb(handle, filename, UV_RENAME, 0);
|
||||
break;
|
||||
|
||||
case FILE_ACTION_MODIFIED:
|
||||
handle->cb(handle, filename, UV_CHANGE, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
uv__free(filename);
|
||||
filename = NULL;
|
||||
uv__free(long_filenamew);
|
||||
long_filenamew = NULL;
|
||||
filenamew = NULL;
|
||||
}
|
||||
|
||||
offset = file_info->NextEntryOffset;
|
||||
} while (offset && !(handle->flags & UV__HANDLE_CLOSING));
|
||||
} else {
|
||||
handle->cb(handle, NULL, UV_CHANGE, 0);
|
||||
}
|
||||
} else {
|
||||
err = GET_REQ_ERROR(req);
|
||||
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
|
||||
}
|
||||
|
||||
if (!(handle->flags & UV__HANDLE_CLOSING)) {
|
||||
uv_fs_event_queue_readdirchanges(loop, handle);
|
||||
} else {
|
||||
uv_want_endgame(loop, (uv_handle_t*)handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
|
||||
uv_fs_event_stop(handle);
|
||||
|
||||
uv__handle_closing(handle);
|
||||
|
||||
if (!handle->req_pending) {
|
||||
uv_want_endgame(loop, (uv_handle_t*)handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
|
||||
if ((handle->flags & UV__HANDLE_CLOSING) && !handle->req_pending) {
|
||||
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||
|
||||
if (handle->buffer) {
|
||||
uv__free(handle->buffer);
|
||||
handle->buffer = NULL;
|
||||
}
|
||||
|
||||
uv__handle_close(handle);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,385 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
/* EAI_* constants. */
|
||||
#include <winsock2.h>
|
||||
|
||||
|
||||
int uv__getaddrinfo_translate_error(int sys_err) {
|
||||
switch (sys_err) {
|
||||
case 0: return 0;
|
||||
case WSATRY_AGAIN: return UV_EAI_AGAIN;
|
||||
case WSAEINVAL: return UV_EAI_BADFLAGS;
|
||||
case WSANO_RECOVERY: return UV_EAI_FAIL;
|
||||
case WSAEAFNOSUPPORT: return UV_EAI_FAMILY;
|
||||
case WSA_NOT_ENOUGH_MEMORY: return UV_EAI_MEMORY;
|
||||
case WSAHOST_NOT_FOUND: return UV_EAI_NONAME;
|
||||
case WSATYPE_NOT_FOUND: return UV_EAI_SERVICE;
|
||||
case WSAESOCKTNOSUPPORT: return UV_EAI_SOCKTYPE;
|
||||
default: return uv_translate_sys_error(sys_err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MinGW is missing this
|
||||
*/
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct addrinfoW {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
size_t ai_addrlen;
|
||||
WCHAR* ai_canonname;
|
||||
struct sockaddr* ai_addr;
|
||||
struct addrinfoW* ai_next;
|
||||
} ADDRINFOW, *PADDRINFOW;
|
||||
|
||||
DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node,
|
||||
const WCHAR* 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)
|
||||
|
||||
|
||||
static void uv__getaddrinfo_work(struct uv__work* w) {
|
||||
uv_getaddrinfo_t* req;
|
||||
struct addrinfoW* hints;
|
||||
int err;
|
||||
|
||||
req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
hints = req->addrinfow;
|
||||
req->addrinfow = NULL;
|
||||
err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);
|
||||
req->retcode = uv__getaddrinfo_translate_error(err);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
uv_getaddrinfo_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;
|
||||
|
||||
req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
|
||||
/* release input parameter memory */
|
||||
uv__free(req->alloc);
|
||||
req->alloc = NULL;
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
assert(req->retcode == 0);
|
||||
req->retcode = UV_EAI_CANCELED;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (req->retcode == 0) {
|
||||
/* convert addrinfoW to addrinfo */
|
||||
/* first calculate required length */
|
||||
addrinfow_ptr = req->addrinfow;
|
||||
while (addrinfow_ptr != NULL) {
|
||||
addrinfo_len += addrinfo_struct_len +
|
||||
ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
|
||||
if (addrinfow_ptr->ai_canonname != NULL) {
|
||||
name_len = WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
addrinfow_ptr->ai_canonname,
|
||||
-1,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL);
|
||||
if (name_len == 0) {
|
||||
req->retcode = 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*)uv__malloc(addrinfo_len);
|
||||
|
||||
/* do conversions */
|
||||
if (alloc_ptr != NULL) {
|
||||
cur_ptr = alloc_ptr;
|
||||
addrinfow_ptr = req->addrinfow;
|
||||
|
||||
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 = WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
addrinfow_ptr->ai_canonname,
|
||||
-1,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL);
|
||||
assert(name_len > 0);
|
||||
assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len);
|
||||
name_len = WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
addrinfow_ptr->ai_canonname,
|
||||
-1,
|
||||
cur_ptr,
|
||||
name_len,
|
||||
NULL,
|
||||
NULL);
|
||||
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;
|
||||
}
|
||||
}
|
||||
req->addrinfo = (struct addrinfo*)alloc_ptr;
|
||||
} else {
|
||||
req->retcode = UV_EAI_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
/* return memory to system */
|
||||
if (req->addrinfow != NULL) {
|
||||
FreeAddrInfoW(req->addrinfow);
|
||||
req->addrinfow = NULL;
|
||||
}
|
||||
|
||||
complete:
|
||||
uv__req_unregister(req->loop, req);
|
||||
|
||||
/* finally do callback with converted result */
|
||||
if (req->getaddrinfo_cb)
|
||||
req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
|
||||
}
|
||||
|
||||
|
||||
void uv_freeaddrinfo(struct addrinfo* ai) {
|
||||
char* alloc_ptr = (char*)ai;
|
||||
|
||||
/* release copied result memory */
|
||||
uv__free(alloc_ptr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Entry point for getaddrinfo
|
||||
* we convert the UTF-8 strings to UNICODE
|
||||
* and save the UNICODE string pointers in the req
|
||||
* We also copy hints so that caller does not need to keep memory until the
|
||||
* callback.
|
||||
* return 0 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_loop_t* loop,
|
||||
uv_getaddrinfo_t* req,
|
||||
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;
|
||||
int err;
|
||||
|
||||
if (req == NULL || (node == NULL && service == NULL)) {
|
||||
err = WSAEINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
uv_req_init(loop, (uv_req_t*)req);
|
||||
|
||||
req->getaddrinfo_cb = getaddrinfo_cb;
|
||||
req->addrinfo = NULL;
|
||||
req->type = UV_GETADDRINFO;
|
||||
req->loop = loop;
|
||||
req->retcode = 0;
|
||||
|
||||
/* calculate required memory size for all input values */
|
||||
if (node != NULL) {
|
||||
nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, node, -1, NULL, 0) *
|
||||
sizeof(WCHAR));
|
||||
if (nodesize == 0) {
|
||||
err = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (service != NULL) {
|
||||
servicesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8,
|
||||
0,
|
||||
service,
|
||||
-1,
|
||||
NULL,
|
||||
0) *
|
||||
sizeof(WCHAR));
|
||||
if (servicesize == 0) {
|
||||
err = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (hints != NULL) {
|
||||
hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
|
||||
}
|
||||
|
||||
/* allocate memory for inputs, and partition it as needed */
|
||||
alloc_ptr = (char*)uv__malloc(nodesize + servicesize + hintssize);
|
||||
if (!alloc_ptr) {
|
||||
err = WSAENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* save alloc_ptr now so we can free if error */
|
||||
req->alloc = (void*)alloc_ptr;
|
||||
|
||||
/* convert node string to UTF16 into allocated memory and save pointer in */
|
||||
/* the request. */
|
||||
if (node != NULL) {
|
||||
req->node = (WCHAR*)alloc_ptr;
|
||||
if (MultiByteToWideChar(CP_UTF8,
|
||||
0,
|
||||
node,
|
||||
-1,
|
||||
(WCHAR*) alloc_ptr,
|
||||
nodesize / sizeof(WCHAR)) == 0) {
|
||||
err = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
alloc_ptr += nodesize;
|
||||
} else {
|
||||
req->node = NULL;
|
||||
}
|
||||
|
||||
/* convert service string to UTF16 into allocated memory and save pointer */
|
||||
/* in the req. */
|
||||
if (service != NULL) {
|
||||
req->service = (WCHAR*)alloc_ptr;
|
||||
if (MultiByteToWideChar(CP_UTF8,
|
||||
0,
|
||||
service,
|
||||
-1,
|
||||
(WCHAR*) alloc_ptr,
|
||||
servicesize / sizeof(WCHAR)) == 0) {
|
||||
err = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
alloc_ptr += servicesize;
|
||||
} else {
|
||||
req->service = NULL;
|
||||
}
|
||||
|
||||
/* copy hints to allocated memory and save pointer in req */
|
||||
if (hints != NULL) {
|
||||
req->addrinfow = (struct addrinfoW*)alloc_ptr;
|
||||
req->addrinfow->ai_family = hints->ai_family;
|
||||
req->addrinfow->ai_socktype = hints->ai_socktype;
|
||||
req->addrinfow->ai_protocol = hints->ai_protocol;
|
||||
req->addrinfow->ai_flags = hints->ai_flags;
|
||||
req->addrinfow->ai_addrlen = 0;
|
||||
req->addrinfow->ai_canonname = NULL;
|
||||
req->addrinfow->ai_addr = NULL;
|
||||
req->addrinfow->ai_next = NULL;
|
||||
} else {
|
||||
req->addrinfow = NULL;
|
||||
}
|
||||
|
||||
uv__req_register(loop, req);
|
||||
|
||||
if (getaddrinfo_cb) {
|
||||
uv__work_submit(loop,
|
||||
&req->work_req,
|
||||
uv__getaddrinfo_work,
|
||||
uv__getaddrinfo_done);
|
||||
return 0;
|
||||
} else {
|
||||
uv__getaddrinfo_work(&req->work_req);
|
||||
uv__getaddrinfo_done(&req->work_req, 0);
|
||||
return req->retcode;
|
||||
}
|
||||
|
||||
error:
|
||||
if (req != NULL) {
|
||||
uv__free(req->alloc);
|
||||
req->alloc = NULL;
|
||||
}
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
#ifndef GetNameInfo
|
||||
int WSAAPI GetNameInfoW(
|
||||
const SOCKADDR *pSockaddr,
|
||||
socklen_t SockaddrLength,
|
||||
PWCHAR pNodeBuffer,
|
||||
DWORD NodeBufferSize,
|
||||
PWCHAR pServiceBuffer,
|
||||
DWORD ServiceBufferSize,
|
||||
INT Flags
|
||||
);
|
||||
#endif
|
||||
|
||||
static void uv__getnameinfo_work(struct uv__work* w) {
|
||||
uv_getnameinfo_t* req;
|
||||
WCHAR host[NI_MAXHOST];
|
||||
WCHAR service[NI_MAXSERV];
|
||||
int ret = 0;
|
||||
|
||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||
if (GetNameInfoW((struct sockaddr*)&req->storage,
|
||||
sizeof(req->storage),
|
||||
host,
|
||||
ARRAY_SIZE(host),
|
||||
service,
|
||||
ARRAY_SIZE(service),
|
||||
req->flags)) {
|
||||
ret = WSAGetLastError();
|
||||
}
|
||||
req->retcode = uv__getaddrinfo_translate_error(ret);
|
||||
|
||||
/* convert results to UTF-8 */
|
||||
WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
host,
|
||||
-1,
|
||||
req->host,
|
||||
sizeof(req->host),
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
service,
|
||||
-1,
|
||||
req->service,
|
||||
sizeof(req->service),
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called from uv_run when complete.
|
||||
*/
|
||||
static void uv__getnameinfo_done(struct uv__work* w, int status) {
|
||||
uv_getnameinfo_t* req;
|
||||
char* host;
|
||||
char* service;
|
||||
|
||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
host = service = NULL;
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
assert(req->retcode == 0);
|
||||
req->retcode = UV_EAI_CANCELED;
|
||||
} else if (req->retcode == 0) {
|
||||
host = req->host;
|
||||
service = req->service;
|
||||
}
|
||||
|
||||
if (req->getnameinfo_cb)
|
||||
req->getnameinfo_cb(req, req->retcode, host, service);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Entry point for getnameinfo
|
||||
* return 0 if a callback will be made
|
||||
* return error code if validation fails
|
||||
*/
|
||||
int uv_getnameinfo(uv_loop_t* loop,
|
||||
uv_getnameinfo_t* req,
|
||||
uv_getnameinfo_cb getnameinfo_cb,
|
||||
const struct sockaddr* addr,
|
||||
int flags) {
|
||||
if (req == NULL || addr == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr->sa_family == AF_INET) {
|
||||
memcpy(&req->storage,
|
||||
addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
} else if (addr->sa_family == AF_INET6) {
|
||||
memcpy(&req->storage,
|
||||
addr,
|
||||
sizeof(struct sockaddr_in6));
|
||||
} else {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
uv_req_init(loop, (uv_req_t*)req);
|
||||
uv__req_register(loop, req);
|
||||
|
||||
req->getnameinfo_cb = getnameinfo_cb;
|
||||
req->flags = flags;
|
||||
req->type = UV_GETNAMEINFO;
|
||||
req->loop = loop;
|
||||
req->retcode = 0;
|
||||
|
||||
if (getnameinfo_cb) {
|
||||
uv__work_submit(loop,
|
||||
&req->work_req,
|
||||
uv__getnameinfo_work,
|
||||
uv__getnameinfo_done);
|
||||
return 0;
|
||||
} else {
|
||||
uv__getnameinfo_work(&req->work_req);
|
||||
uv__getnameinfo_done(&req->work_req, 0);
|
||||
return req->retcode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef UV_WIN_HANDLE_INL_H_
|
||||
#define UV_WIN_HANDLE_INL_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <io.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
#define DECREASE_ACTIVE_COUNT(loop, handle) \
|
||||
do { \
|
||||
if (--(handle)->activecnt == 0 && \
|
||||
!((handle)->flags & UV__HANDLE_CLOSING)) { \
|
||||
uv__handle_stop((handle)); \
|
||||
} \
|
||||
assert((handle)->activecnt >= 0); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define INCREASE_ACTIVE_COUNT(loop, handle) \
|
||||
do { \
|
||||
if ((handle)->activecnt++ == 0) { \
|
||||
uv__handle_start((handle)); \
|
||||
} \
|
||||
assert((handle)->activecnt > 0); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define DECREASE_PENDING_REQ_COUNT(handle) \
|
||||
do { \
|
||||
assert(handle->reqs_pending > 0); \
|
||||
handle->reqs_pending--; \
|
||||
\
|
||||
if (handle->flags & UV__HANDLE_CLOSING && \
|
||||
handle->reqs_pending == 0) { \
|
||||
uv_want_endgame(loop, (uv_handle_t*)handle); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define uv__handle_closing(handle) \
|
||||
do { \
|
||||
assert(!((handle)->flags & UV__HANDLE_CLOSING)); \
|
||||
\
|
||||
if (!(((handle)->flags & UV__HANDLE_ACTIVE) && \
|
||||
((handle)->flags & UV__HANDLE_REF))) \
|
||||
uv__active_handle_add((uv_handle_t*) (handle)); \
|
||||
\
|
||||
(handle)->flags |= UV__HANDLE_CLOSING; \
|
||||
(handle)->flags &= ~UV__HANDLE_ACTIVE; \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define uv__handle_close(handle) \
|
||||
do { \
|
||||
QUEUE_REMOVE(&(handle)->handle_queue); \
|
||||
uv__active_handle_rm((uv_handle_t*) (handle)); \
|
||||
\
|
||||
(handle)->flags |= UV_HANDLE_CLOSED; \
|
||||
\
|
||||
if ((handle)->close_cb) \
|
||||
(handle)->close_cb((uv_handle_t*) (handle)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
INLINE static void uv_want_endgame(uv_loop_t* loop, 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
INLINE static void uv_process_endgames(uv_loop_t* loop) {
|
||||
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(loop, (uv_tcp_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_NAMED_PIPE:
|
||||
uv_pipe_endgame(loop, (uv_pipe_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_TTY:
|
||||
uv_tty_endgame(loop, (uv_tty_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_UDP:
|
||||
uv_udp_endgame(loop, (uv_udp_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_POLL:
|
||||
uv_poll_endgame(loop, (uv_poll_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_TIMER:
|
||||
uv_timer_endgame(loop, (uv_timer_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_PREPARE:
|
||||
case UV_CHECK:
|
||||
case UV_IDLE:
|
||||
uv_loop_watcher_endgame(loop, handle);
|
||||
break;
|
||||
|
||||
case UV_ASYNC:
|
||||
uv_async_endgame(loop, (uv_async_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_SIGNAL:
|
||||
uv_signal_endgame(loop, (uv_signal_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_PROCESS:
|
||||
uv_process_endgame(loop, (uv_process_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_FS_EVENT:
|
||||
uv_fs_event_endgame(loop, (uv_fs_event_t*) handle);
|
||||
break;
|
||||
|
||||
case UV_FS_POLL:
|
||||
uv__fs_poll_endgame(loop, (uv_fs_poll_t*) handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INLINE static HANDLE uv__get_osfhandle(int fd)
|
||||
{
|
||||
/* _get_osfhandle() raises an assert in debug builds if the FD is invalid. */
|
||||
/* But it also correctly checks the FD and returns INVALID_HANDLE_VALUE */
|
||||
/* for invalid FDs in release builds (or if you let the assert continue). */
|
||||
/* So this wrapper function disables asserts when calling _get_osfhandle. */
|
||||
|
||||
HANDLE handle;
|
||||
UV_BEGIN_DISABLE_CRT_ASSERT();
|
||||
handle = (HANDLE) _get_osfhandle(fd);
|
||||
UV_END_DISABLE_CRT_ASSERT();
|
||||
return handle;
|
||||
}
|
||||
|
||||
#endif /* UV_WIN_HANDLE_INL_H_ */
|
||||
@@ -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 <assert.h>
|
||||
#include <io.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
|
||||
|
||||
uv_handle_type uv_guess_handle(uv_file file) {
|
||||
HANDLE handle;
|
||||
DWORD mode;
|
||||
|
||||
if (file < 0) {
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
}
|
||||
|
||||
handle = uv__get_osfhandle(file);
|
||||
|
||||
switch (GetFileType(handle)) {
|
||||
case FILE_TYPE_CHAR:
|
||||
if (GetConsoleMode(handle, &mode)) {
|
||||
return UV_TTY;
|
||||
} else {
|
||||
return UV_FILE;
|
||||
}
|
||||
|
||||
case FILE_TYPE_PIPE:
|
||||
return UV_NAMED_PIPE;
|
||||
|
||||
case FILE_TYPE_DISK:
|
||||
return UV_FILE;
|
||||
|
||||
default:
|
||||
return UV_UNKNOWN_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_is_active(const uv_handle_t* handle) {
|
||||
return (handle->flags & UV__HANDLE_ACTIVE) &&
|
||||
!(handle->flags & UV__HANDLE_CLOSING);
|
||||
}
|
||||
|
||||
|
||||
void uv_close(uv_handle_t* handle, uv_close_cb cb) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
|
||||
if (handle->flags & UV__HANDLE_CLOSING) {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
handle->close_cb = cb;
|
||||
|
||||
/* Handle-specific close actions */
|
||||
switch (handle->type) {
|
||||
case UV_TCP:
|
||||
uv_tcp_close(loop, (uv_tcp_t*)handle);
|
||||
return;
|
||||
|
||||
case UV_NAMED_PIPE:
|
||||
uv_pipe_close(loop, (uv_pipe_t*) handle);
|
||||
return;
|
||||
|
||||
case UV_TTY:
|
||||
uv_tty_close((uv_tty_t*) handle);
|
||||
return;
|
||||
|
||||
case UV_UDP:
|
||||
uv_udp_close(loop, (uv_udp_t*) handle);
|
||||
return;
|
||||
|
||||
case UV_POLL:
|
||||
uv_poll_close(loop, (uv_poll_t*) handle);
|
||||
return;
|
||||
|
||||
case UV_TIMER:
|
||||
uv_timer_stop((uv_timer_t*)handle);
|
||||
uv__handle_closing(handle);
|
||||
uv_want_endgame(loop, handle);
|
||||
return;
|
||||
|
||||
case UV_PREPARE:
|
||||
uv_prepare_stop((uv_prepare_t*)handle);
|
||||
uv__handle_closing(handle);
|
||||
uv_want_endgame(loop, handle);
|
||||
return;
|
||||
|
||||
case UV_CHECK:
|
||||
uv_check_stop((uv_check_t*)handle);
|
||||
uv__handle_closing(handle);
|
||||
uv_want_endgame(loop, handle);
|
||||
return;
|
||||
|
||||
case UV_IDLE:
|
||||
uv_idle_stop((uv_idle_t*)handle);
|
||||
uv__handle_closing(handle);
|
||||
uv_want_endgame(loop, handle);
|
||||
return;
|
||||
|
||||
case UV_ASYNC:
|
||||
uv_async_close(loop, (uv_async_t*) handle);
|
||||
return;
|
||||
|
||||
case UV_SIGNAL:
|
||||
uv_signal_close(loop, (uv_signal_t*) handle);
|
||||
return;
|
||||
|
||||
case UV_PROCESS:
|
||||
uv_process_close(loop, (uv_process_t*) handle);
|
||||
return;
|
||||
|
||||
case UV_FS_EVENT:
|
||||
uv_fs_event_close(loop, (uv_fs_event_t*) handle);
|
||||
return;
|
||||
|
||||
case UV_FS_POLL:
|
||||
uv__fs_poll_close((uv_fs_poll_t*) handle);
|
||||
uv__handle_closing(handle);
|
||||
uv_want_endgame(loop, handle);
|
||||
return;
|
||||
|
||||
default:
|
||||
/* Not supported */
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_is_closing(const uv_handle_t* handle) {
|
||||
return !!(handle->flags & (UV__HANDLE_CLOSING | UV_HANDLE_CLOSED));
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
/* 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"
|
||||
#include "winapi.h"
|
||||
#include "winsock.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define INLINE __inline
|
||||
# define UV_THREAD_LOCAL __declspec( thread )
|
||||
#else
|
||||
# define INLINE inline
|
||||
# define UV_THREAD_LOCAL __thread
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
extern UV_THREAD_LOCAL int uv__crt_assert_enabled;
|
||||
|
||||
#define UV_BEGIN_DISABLE_CRT_ASSERT() \
|
||||
{ \
|
||||
int uv__saved_crt_assert_enabled = uv__crt_assert_enabled; \
|
||||
uv__crt_assert_enabled = FALSE;
|
||||
|
||||
|
||||
#define UV_END_DISABLE_CRT_ASSERT() \
|
||||
uv__crt_assert_enabled = uv__saved_crt_assert_enabled; \
|
||||
}
|
||||
|
||||
#else
|
||||
#define UV_BEGIN_DISABLE_CRT_ASSERT()
|
||||
#define UV_END_DISABLE_CRT_ASSERT()
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handles
|
||||
* (also see handle-inl.h)
|
||||
*/
|
||||
|
||||
/* Used by all handles. */
|
||||
#define UV_HANDLE_CLOSED 0x00000002
|
||||
#define UV_HANDLE_ENDGAME_QUEUED 0x00000008
|
||||
|
||||
/* uv-common.h: #define UV__HANDLE_CLOSING 0x00000001 */
|
||||
/* uv-common.h: #define UV__HANDLE_ACTIVE 0x00000040 */
|
||||
/* uv-common.h: #define UV__HANDLE_REF 0x00000020 */
|
||||
/* uv-common.h: #define UV_HANDLE_INTERNAL 0x00000080 */
|
||||
|
||||
/* Used by streams and UDP handles. */
|
||||
#define UV_HANDLE_READING 0x00000100
|
||||
#define UV_HANDLE_BOUND 0x00000200
|
||||
#define UV_HANDLE_LISTENING 0x00000800
|
||||
#define UV_HANDLE_CONNECTION 0x00001000
|
||||
#define UV_HANDLE_READABLE 0x00008000
|
||||
#define UV_HANDLE_WRITABLE 0x00010000
|
||||
#define UV_HANDLE_READ_PENDING 0x00020000
|
||||
#define UV_HANDLE_SYNC_BYPASS_IOCP 0x00040000
|
||||
#define UV_HANDLE_ZERO_READ 0x00080000
|
||||
#define UV_HANDLE_EMULATE_IOCP 0x00100000
|
||||
#define UV_HANDLE_BLOCKING_WRITES 0x00200000
|
||||
#define UV_HANDLE_CANCELLATION_PENDING 0x00400000
|
||||
|
||||
/* Used by uv_tcp_t and uv_udp_t handles */
|
||||
#define UV_HANDLE_IPV6 0x01000000
|
||||
|
||||
/* Only used by uv_tcp_t handles. */
|
||||
#define UV_HANDLE_TCP_NODELAY 0x02000000
|
||||
#define UV_HANDLE_TCP_KEEPALIVE 0x04000000
|
||||
#define UV_HANDLE_TCP_SINGLE_ACCEPT 0x08000000
|
||||
#define UV_HANDLE_TCP_ACCEPT_STATE_CHANGING 0x10000000
|
||||
#define UV_HANDLE_TCP_SOCKET_CLOSED 0x20000000
|
||||
#define UV_HANDLE_SHARED_TCP_SOCKET 0x40000000
|
||||
|
||||
/* Only used by uv_pipe_t handles. */
|
||||
#define UV_HANDLE_NON_OVERLAPPED_PIPE 0x01000000
|
||||
#define UV_HANDLE_PIPESERVER 0x02000000
|
||||
#define UV_HANDLE_PIPE_READ_CANCELABLE 0x04000000
|
||||
|
||||
/* Only used by uv_tty_t handles. */
|
||||
#define UV_HANDLE_TTY_READABLE 0x01000000
|
||||
#define UV_HANDLE_TTY_RAW 0x02000000
|
||||
#define UV_HANDLE_TTY_SAVED_POSITION 0x04000000
|
||||
#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x08000000
|
||||
|
||||
/* Only used by uv_poll_t handles. */
|
||||
#define UV_HANDLE_POLL_SLOW 0x02000000
|
||||
|
||||
|
||||
/*
|
||||
* Requests: see req-inl.h
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Streams: see stream-inl.h
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* TCP
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
WSAPROTOCOL_INFOW socket_info;
|
||||
int delayed_error;
|
||||
} uv__ipc_socket_info_ex;
|
||||
|
||||
int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb);
|
||||
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_loop_t* loop, uv_write_t* req, uv_tcp_t* handle,
|
||||
const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
|
||||
int uv__tcp_try_write(uv_tcp_t* handle, const uv_buf_t bufs[],
|
||||
unsigned int nbufs);
|
||||
|
||||
void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req);
|
||||
void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
|
||||
uv_write_t* req);
|
||||
void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
|
||||
uv_req_t* req);
|
||||
void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
|
||||
uv_connect_t* req);
|
||||
|
||||
void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp);
|
||||
void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle);
|
||||
|
||||
int uv_tcp_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex,
|
||||
int tcp_connection);
|
||||
|
||||
int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid,
|
||||
LPWSAPROTOCOL_INFOW protocol_info);
|
||||
|
||||
|
||||
/*
|
||||
* UDP
|
||||
*/
|
||||
void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req);
|
||||
void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
|
||||
uv_udp_send_t* req);
|
||||
|
||||
void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle);
|
||||
void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle);
|
||||
|
||||
|
||||
/*
|
||||
* Pipes
|
||||
*/
|
||||
int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access,
|
||||
char* name, size_t nameSize);
|
||||
|
||||
int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
|
||||
int uv_pipe_accept(uv_pipe_t* server, uv_stream_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_loop_t* loop, uv_write_t* req, uv_pipe_t* handle,
|
||||
const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
|
||||
int uv_pipe_write2(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle,
|
||||
const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle,
|
||||
uv_write_cb cb);
|
||||
void uv__pipe_pause_read(uv_pipe_t* handle);
|
||||
void uv__pipe_unpause_read(uv_pipe_t* handle);
|
||||
void uv__pipe_stop_read(uv_pipe_t* handle);
|
||||
|
||||
void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
uv_req_t* req);
|
||||
void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
uv_write_t* req);
|
||||
void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
uv_req_t* raw_req);
|
||||
void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
uv_connect_t* req);
|
||||
void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
uv_shutdown_t* req);
|
||||
|
||||
void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle);
|
||||
void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle);
|
||||
void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle);
|
||||
|
||||
|
||||
/*
|
||||
* TTY
|
||||
*/
|
||||
void uv_console_init();
|
||||
|
||||
int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
|
||||
uv_read_cb read_cb);
|
||||
int uv_tty_read_stop(uv_tty_t* handle);
|
||||
int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle,
|
||||
const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
|
||||
int uv__tty_try_write(uv_tty_t* handle, const uv_buf_t bufs[],
|
||||
unsigned int nbufs);
|
||||
void uv_tty_close(uv_tty_t* handle);
|
||||
|
||||
void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
|
||||
uv_req_t* req);
|
||||
void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
|
||||
uv_write_t* req);
|
||||
/* TODO: remove me */
|
||||
void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
|
||||
uv_req_t* raw_req);
|
||||
/* TODO: remove me */
|
||||
void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
|
||||
uv_connect_t* req);
|
||||
|
||||
void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle);
|
||||
|
||||
|
||||
/*
|
||||
* Poll watchers
|
||||
*/
|
||||
void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
|
||||
uv_req_t* req);
|
||||
|
||||
int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle);
|
||||
void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle);
|
||||
|
||||
|
||||
/*
|
||||
* Timers
|
||||
*/
|
||||
void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle);
|
||||
|
||||
DWORD uv__next_timeout(const uv_loop_t* loop);
|
||||
void uv_process_timers(uv_loop_t* loop);
|
||||
|
||||
|
||||
/*
|
||||
* Loop watchers
|
||||
*/
|
||||
void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle);
|
||||
|
||||
void uv_prepare_invoke(uv_loop_t* loop);
|
||||
void uv_check_invoke(uv_loop_t* loop);
|
||||
void uv_idle_invoke(uv_loop_t* loop);
|
||||
|
||||
void uv__once_init();
|
||||
|
||||
|
||||
/*
|
||||
* Async watcher
|
||||
*/
|
||||
void uv_async_close(uv_loop_t* loop, uv_async_t* handle);
|
||||
void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle);
|
||||
|
||||
void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
|
||||
uv_req_t* req);
|
||||
|
||||
|
||||
/*
|
||||
* Signal watcher
|
||||
*/
|
||||
void uv_signals_init();
|
||||
int uv__signal_dispatch(int signum);
|
||||
|
||||
void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle);
|
||||
void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle);
|
||||
|
||||
void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
|
||||
uv_req_t* req);
|
||||
|
||||
|
||||
/*
|
||||
* Spawn
|
||||
*/
|
||||
void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle);
|
||||
void uv_process_close(uv_loop_t* loop, uv_process_t* handle);
|
||||
void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle);
|
||||
|
||||
|
||||
/*
|
||||
* Error
|
||||
*/
|
||||
int uv_translate_sys_error(int sys_errno);
|
||||
|
||||
|
||||
/*
|
||||
* FS
|
||||
*/
|
||||
void uv_fs_init();
|
||||
|
||||
|
||||
/*
|
||||
* FS Event
|
||||
*/
|
||||
void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
|
||||
uv_fs_event_t* handle);
|
||||
void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle);
|
||||
void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle);
|
||||
|
||||
|
||||
/*
|
||||
* Stat poller.
|
||||
*/
|
||||
void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle);
|
||||
|
||||
|
||||
/*
|
||||
* Utilities.
|
||||
*/
|
||||
void uv__util_init();
|
||||
|
||||
uint64_t uv__hrtime(double scale);
|
||||
int uv_parent_pid();
|
||||
int uv_current_pid();
|
||||
__declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall);
|
||||
int uv__getpwuid_r(uv_passwd_t* pwd);
|
||||
int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8);
|
||||
|
||||
|
||||
/*
|
||||
* Process stdio handles.
|
||||
*/
|
||||
int uv__stdio_create(uv_loop_t* loop,
|
||||
const uv_process_options_t* options,
|
||||
BYTE** buffer_ptr);
|
||||
void uv__stdio_destroy(BYTE* buffer);
|
||||
void uv__stdio_noinherit(BYTE* buffer);
|
||||
int uv__stdio_verify(BYTE* buffer, WORD size);
|
||||
WORD uv__stdio_size(BYTE* buffer);
|
||||
HANDLE uv__stdio_handle(BYTE* buffer, int fd);
|
||||
|
||||
|
||||
/*
|
||||
* Winapi and ntapi utility functions
|
||||
*/
|
||||
void uv_winapi_init();
|
||||
|
||||
|
||||
/*
|
||||
* Winsock utility functions
|
||||
*/
|
||||
void uv_winsock_init();
|
||||
|
||||
int uv_ntstatus_to_winsock_error(NTSTATUS status);
|
||||
|
||||
BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target);
|
||||
BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target);
|
||||
|
||||
int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
|
||||
DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
|
||||
int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
|
||||
DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
|
||||
int* addr_len, WSAOVERLAPPED *overlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
|
||||
|
||||
int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
|
||||
AFD_POLL_INFO* info_out, OVERLAPPED* overlapped);
|
||||
|
||||
/* Whether there are any non-IFS LSPs stacked on TCP */
|
||||
extern int uv_tcp_non_ifs_lsp_ipv4;
|
||||
extern int uv_tcp_non_ifs_lsp_ipv6;
|
||||
|
||||
/* Ip address used to bind to any port at any interface */
|
||||
extern struct sockaddr_in uv_addr_ip4_any_;
|
||||
extern struct sockaddr_in6 uv_addr_ip6_any_;
|
||||
|
||||
#endif /* UV_WIN_INTERNAL_H_ */
|
||||
@@ -0,0 +1,122 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
|
||||
|
||||
void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) {
|
||||
if (handle->flags & UV__HANDLE_CLOSING) {
|
||||
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||
handle->flags |= UV_HANDLE_CLOSED;
|
||||
uv__handle_close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define UV_LOOP_WATCHER_DEFINE(name, NAME) \
|
||||
int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_##NAME); \
|
||||
\
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \
|
||||
uv_loop_t* loop = handle->loop; \
|
||||
uv_##name##_t* old_head; \
|
||||
\
|
||||
assert(handle->type == UV_##NAME); \
|
||||
\
|
||||
if (uv__is_active(handle)) \
|
||||
return 0; \
|
||||
\
|
||||
if (cb == NULL) \
|
||||
return UV_EINVAL; \
|
||||
\
|
||||
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; \
|
||||
uv__handle_start(handle); \
|
||||
\
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
int uv_##name##_stop(uv_##name##_t* handle) { \
|
||||
uv_loop_t* loop = handle->loop; \
|
||||
\
|
||||
assert(handle->type == UV_##NAME); \
|
||||
\
|
||||
if (!uv__is_active(handle)) \
|
||||
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; \
|
||||
} \
|
||||
\
|
||||
uv__handle_stop(handle); \
|
||||
\
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
void uv_##name##_invoke(uv_loop_t* loop) { \
|
||||
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); \
|
||||
} \
|
||||
}
|
||||
|
||||
UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
|
||||
UV_LOOP_WATCHER_DEFINE(check, CHECK)
|
||||
UV_LOOP_WATCHER_DEFINE(idle, IDLE)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,646 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <io.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
|
||||
static const GUID uv_msafd_provider_ids[UV_MSAFD_PROVIDER_COUNT] = {
|
||||
{0xe70f1aa0, 0xab8b, 0x11cf,
|
||||
{0x8c, 0xa3, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}},
|
||||
{0xf9eab0c0, 0x26d4, 0x11d0,
|
||||
{0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}},
|
||||
{0x9fc48064, 0x7298, 0x43e4,
|
||||
{0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}}
|
||||
};
|
||||
|
||||
typedef struct uv_single_fd_set_s {
|
||||
unsigned int fd_count;
|
||||
SOCKET fd_array[1];
|
||||
} uv_single_fd_set_t;
|
||||
|
||||
|
||||
static OVERLAPPED overlapped_dummy_;
|
||||
static uv_once_t overlapped_dummy_init_guard_ = UV_ONCE_INIT;
|
||||
|
||||
static AFD_POLL_INFO afd_poll_info_dummy_;
|
||||
|
||||
|
||||
static void uv__init_overlapped_dummy(void) {
|
||||
HANDLE event;
|
||||
|
||||
event = CreateEvent(NULL, TRUE, TRUE, NULL);
|
||||
if (event == NULL)
|
||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
||||
|
||||
memset(&overlapped_dummy_, 0, sizeof overlapped_dummy_);
|
||||
overlapped_dummy_.hEvent = (HANDLE) ((uintptr_t) event | 1);
|
||||
}
|
||||
|
||||
|
||||
static OVERLAPPED* uv__get_overlapped_dummy() {
|
||||
uv_once(&overlapped_dummy_init_guard_, uv__init_overlapped_dummy);
|
||||
return &overlapped_dummy_;
|
||||
}
|
||||
|
||||
|
||||
static AFD_POLL_INFO* uv__get_afd_poll_info_dummy() {
|
||||
return &afd_poll_info_dummy_;
|
||||
}
|
||||
|
||||
|
||||
static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
|
||||
uv_req_t* req;
|
||||
AFD_POLL_INFO* afd_poll_info;
|
||||
DWORD result;
|
||||
|
||||
/* Find a yet unsubmitted req to submit. */
|
||||
if (handle->submitted_events_1 == 0) {
|
||||
req = &handle->poll_req_1;
|
||||
afd_poll_info = &handle->afd_poll_info_1;
|
||||
handle->submitted_events_1 = handle->events;
|
||||
handle->mask_events_1 = 0;
|
||||
handle->mask_events_2 = handle->events;
|
||||
} else if (handle->submitted_events_2 == 0) {
|
||||
req = &handle->poll_req_2;
|
||||
afd_poll_info = &handle->afd_poll_info_2;
|
||||
handle->submitted_events_2 = handle->events;
|
||||
handle->mask_events_1 = handle->events;
|
||||
handle->mask_events_2 = 0;
|
||||
} else {
|
||||
/* Just wait until there's an unsubmitted req. */
|
||||
/* This will happen almost immediately as one of the 2 outstanding */
|
||||
/* requests is about to return. When this happens, */
|
||||
/* uv__fast_poll_process_poll_req will be called, and the pending */
|
||||
/* events, if needed, will be processed in a subsequent request. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Setting Exclusive to TRUE makes the other poll request return if there */
|
||||
/* is any. */
|
||||
afd_poll_info->Exclusive = TRUE;
|
||||
afd_poll_info->NumberOfHandles = 1;
|
||||
afd_poll_info->Timeout.QuadPart = INT64_MAX;
|
||||
afd_poll_info->Handles[0].Handle = (HANDLE) handle->socket;
|
||||
afd_poll_info->Handles[0].Status = 0;
|
||||
afd_poll_info->Handles[0].Events = 0;
|
||||
|
||||
if (handle->events & UV_READABLE) {
|
||||
afd_poll_info->Handles[0].Events |= AFD_POLL_RECEIVE |
|
||||
AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT;
|
||||
} else {
|
||||
if (handle->events & UV_DISCONNECT) {
|
||||
afd_poll_info->Handles[0].Events |= AFD_POLL_DISCONNECT;
|
||||
}
|
||||
}
|
||||
if (handle->events & UV_WRITABLE) {
|
||||
afd_poll_info->Handles[0].Events |= AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL;
|
||||
}
|
||||
|
||||
memset(&req->u.io.overlapped, 0, sizeof req->u.io.overlapped);
|
||||
|
||||
result = uv_msafd_poll((SOCKET) handle->peer_socket,
|
||||
afd_poll_info,
|
||||
afd_poll_info,
|
||||
&req->u.io.overlapped);
|
||||
if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) {
|
||||
/* Queue this req, reporting an error. */
|
||||
SET_REQ_ERROR(req, WSAGetLastError());
|
||||
uv_insert_pending_req(loop, req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
|
||||
AFD_POLL_INFO afd_poll_info;
|
||||
DWORD result;
|
||||
|
||||
afd_poll_info.Exclusive = TRUE;
|
||||
afd_poll_info.NumberOfHandles = 1;
|
||||
afd_poll_info.Timeout.QuadPart = INT64_MAX;
|
||||
afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket;
|
||||
afd_poll_info.Handles[0].Status = 0;
|
||||
afd_poll_info.Handles[0].Events = AFD_POLL_ALL;
|
||||
|
||||
result = uv_msafd_poll(handle->socket,
|
||||
&afd_poll_info,
|
||||
uv__get_afd_poll_info_dummy(),
|
||||
uv__get_overlapped_dummy());
|
||||
|
||||
if (result == SOCKET_ERROR) {
|
||||
DWORD error = WSAGetLastError();
|
||||
if (error != WSA_IO_PENDING)
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
|
||||
uv_req_t* req) {
|
||||
unsigned char mask_events;
|
||||
AFD_POLL_INFO* afd_poll_info;
|
||||
|
||||
if (req == &handle->poll_req_1) {
|
||||
afd_poll_info = &handle->afd_poll_info_1;
|
||||
handle->submitted_events_1 = 0;
|
||||
mask_events = handle->mask_events_1;
|
||||
} else if (req == &handle->poll_req_2) {
|
||||
afd_poll_info = &handle->afd_poll_info_2;
|
||||
handle->submitted_events_2 = 0;
|
||||
mask_events = handle->mask_events_2;
|
||||
} else {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Report an error unless the select was just interrupted. */
|
||||
if (!REQ_SUCCESS(req)) {
|
||||
DWORD error = GET_REQ_SOCK_ERROR(req);
|
||||
if (error != WSAEINTR && handle->events != 0) {
|
||||
handle->events = 0; /* Stop the watcher */
|
||||
handle->poll_cb(handle, uv_translate_sys_error(error), 0);
|
||||
}
|
||||
|
||||
} else if (afd_poll_info->NumberOfHandles >= 1) {
|
||||
unsigned char events = 0;
|
||||
|
||||
if ((afd_poll_info->Handles[0].Events & (AFD_POLL_RECEIVE |
|
||||
AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT)) != 0) {
|
||||
events |= UV_READABLE;
|
||||
if ((afd_poll_info->Handles[0].Events & AFD_POLL_DISCONNECT) != 0) {
|
||||
events |= UV_DISCONNECT;
|
||||
}
|
||||
}
|
||||
if ((afd_poll_info->Handles[0].Events & (AFD_POLL_SEND |
|
||||
AFD_POLL_CONNECT_FAIL)) != 0) {
|
||||
events |= UV_WRITABLE;
|
||||
}
|
||||
|
||||
events &= handle->events & ~mask_events;
|
||||
|
||||
if (afd_poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) {
|
||||
/* Stop polling. */
|
||||
handle->events = 0;
|
||||
if (uv__is_active(handle))
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
|
||||
if (events != 0) {
|
||||
handle->poll_cb(handle, 0, events);
|
||||
}
|
||||
}
|
||||
|
||||
if ((handle->events & ~(handle->submitted_events_1 |
|
||||
handle->submitted_events_2)) != 0) {
|
||||
uv__fast_poll_submit_poll_req(loop, handle);
|
||||
} else if ((handle->flags & UV__HANDLE_CLOSING) &&
|
||||
handle->submitted_events_1 == 0 &&
|
||||
handle->submitted_events_2 == 0) {
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__fast_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) {
|
||||
assert(handle->type == UV_POLL);
|
||||
assert(!(handle->flags & UV__HANDLE_CLOSING));
|
||||
assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0);
|
||||
|
||||
handle->events = events;
|
||||
|
||||
if (handle->events != 0) {
|
||||
uv__handle_start(handle);
|
||||
} else {
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
|
||||
if ((handle->events & ~(handle->submitted_events_1 |
|
||||
handle->submitted_events_2)) != 0) {
|
||||
uv__fast_poll_submit_poll_req(handle->loop, handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__fast_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
|
||||
handle->events = 0;
|
||||
uv__handle_closing(handle);
|
||||
|
||||
if (handle->submitted_events_1 == 0 &&
|
||||
handle->submitted_events_2 == 0) {
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
return 0;
|
||||
} else {
|
||||
/* Cancel outstanding poll requests by executing another, unique poll */
|
||||
/* request that forces the outstanding ones to return. */
|
||||
return uv__fast_poll_cancel_poll_req(loop, handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static SOCKET uv__fast_poll_create_peer_socket(HANDLE iocp,
|
||||
WSAPROTOCOL_INFOW* protocol_info) {
|
||||
SOCKET sock = 0;
|
||||
|
||||
sock = WSASocketW(protocol_info->iAddressFamily,
|
||||
protocol_info->iSocketType,
|
||||
protocol_info->iProtocol,
|
||||
protocol_info,
|
||||
0,
|
||||
WSA_FLAG_OVERLAPPED);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)) {
|
||||
goto error;
|
||||
};
|
||||
|
||||
if (CreateIoCompletionPort((HANDLE) sock,
|
||||
iocp,
|
||||
(ULONG_PTR) sock,
|
||||
0) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return sock;
|
||||
|
||||
error:
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
|
||||
static SOCKET uv__fast_poll_get_peer_socket(uv_loop_t* loop,
|
||||
WSAPROTOCOL_INFOW* protocol_info) {
|
||||
int index, i;
|
||||
SOCKET peer_socket;
|
||||
|
||||
index = -1;
|
||||
for (i = 0; (size_t) i < ARRAY_SIZE(uv_msafd_provider_ids); i++) {
|
||||
if (memcmp((void*) &protocol_info->ProviderId,
|
||||
(void*) &uv_msafd_provider_ids[i],
|
||||
sizeof protocol_info->ProviderId) == 0) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the protocol uses an msafd socket. */
|
||||
if (index < 0) {
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* If we didn't (try) to create a peer socket yet, try to make one. Don't */
|
||||
/* try again if the peer socket creation failed earlier for the same */
|
||||
/* protocol. */
|
||||
peer_socket = loop->poll_peer_sockets[index];
|
||||
if (peer_socket == 0) {
|
||||
peer_socket = uv__fast_poll_create_peer_socket(loop->iocp, protocol_info);
|
||||
loop->poll_peer_sockets[index] = peer_socket;
|
||||
}
|
||||
|
||||
return peer_socket;
|
||||
}
|
||||
|
||||
|
||||
static DWORD WINAPI uv__slow_poll_thread_proc(void* arg) {
|
||||
uv_req_t* req = (uv_req_t*) arg;
|
||||
uv_poll_t* handle = (uv_poll_t*) req->data;
|
||||
unsigned char reported_events;
|
||||
int r;
|
||||
uv_single_fd_set_t rfds, wfds, efds;
|
||||
struct timeval timeout;
|
||||
|
||||
assert(handle->type == UV_POLL);
|
||||
assert(req->type == UV_POLL_REQ);
|
||||
|
||||
if (handle->events & UV_READABLE) {
|
||||
rfds.fd_count = 1;
|
||||
rfds.fd_array[0] = handle->socket;
|
||||
} else {
|
||||
rfds.fd_count = 0;
|
||||
}
|
||||
|
||||
if (handle->events & UV_WRITABLE) {
|
||||
wfds.fd_count = 1;
|
||||
wfds.fd_array[0] = handle->socket;
|
||||
efds.fd_count = 1;
|
||||
efds.fd_array[0] = handle->socket;
|
||||
} else {
|
||||
wfds.fd_count = 0;
|
||||
efds.fd_count = 0;
|
||||
}
|
||||
|
||||
/* Make the select() time out after 3 minutes. If select() hangs because */
|
||||
/* the user closed the socket, we will at least not hang indefinitely. */
|
||||
timeout.tv_sec = 3 * 60;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
r = select(1, (fd_set*) &rfds, (fd_set*) &wfds, (fd_set*) &efds, &timeout);
|
||||
if (r == SOCKET_ERROR) {
|
||||
/* Queue this req, reporting an error. */
|
||||
SET_REQ_ERROR(&handle->poll_req_1, WSAGetLastError());
|
||||
POST_COMPLETION_FOR_REQ(handle->loop, req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
reported_events = 0;
|
||||
|
||||
if (r > 0) {
|
||||
if (rfds.fd_count > 0) {
|
||||
assert(rfds.fd_count == 1);
|
||||
assert(rfds.fd_array[0] == handle->socket);
|
||||
reported_events |= UV_READABLE;
|
||||
}
|
||||
|
||||
if (wfds.fd_count > 0) {
|
||||
assert(wfds.fd_count == 1);
|
||||
assert(wfds.fd_array[0] == handle->socket);
|
||||
reported_events |= UV_WRITABLE;
|
||||
} else if (efds.fd_count > 0) {
|
||||
assert(efds.fd_count == 1);
|
||||
assert(efds.fd_array[0] == handle->socket);
|
||||
reported_events |= UV_WRITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
SET_REQ_SUCCESS(req);
|
||||
req->u.io.overlapped.InternalHigh = (DWORD) reported_events;
|
||||
POST_COMPLETION_FOR_REQ(handle->loop, req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__slow_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
|
||||
uv_req_t* req;
|
||||
|
||||
/* Find a yet unsubmitted req to submit. */
|
||||
if (handle->submitted_events_1 == 0) {
|
||||
req = &handle->poll_req_1;
|
||||
handle->submitted_events_1 = handle->events;
|
||||
handle->mask_events_1 = 0;
|
||||
handle->mask_events_2 = handle->events;
|
||||
} else if (handle->submitted_events_2 == 0) {
|
||||
req = &handle->poll_req_2;
|
||||
handle->submitted_events_2 = handle->events;
|
||||
handle->mask_events_1 = handle->events;
|
||||
handle->mask_events_2 = 0;
|
||||
} else {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QueueUserWorkItem(uv__slow_poll_thread_proc,
|
||||
(void*) req,
|
||||
WT_EXECUTELONGFUNCTION)) {
|
||||
/* Make this req pending, reporting an error. */
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
uv_insert_pending_req(loop, req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
|
||||
uv_req_t* req) {
|
||||
unsigned char mask_events;
|
||||
int err;
|
||||
|
||||
if (req == &handle->poll_req_1) {
|
||||
handle->submitted_events_1 = 0;
|
||||
mask_events = handle->mask_events_1;
|
||||
} else if (req == &handle->poll_req_2) {
|
||||
handle->submitted_events_2 = 0;
|
||||
mask_events = handle->mask_events_2;
|
||||
} else {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!REQ_SUCCESS(req)) {
|
||||
/* Error. */
|
||||
if (handle->events != 0) {
|
||||
err = GET_REQ_ERROR(req);
|
||||
handle->events = 0; /* Stop the watcher */
|
||||
handle->poll_cb(handle, uv_translate_sys_error(err), 0);
|
||||
}
|
||||
} else {
|
||||
/* Got some events. */
|
||||
int events = req->u.io.overlapped.InternalHigh & handle->events & ~mask_events;
|
||||
if (events != 0) {
|
||||
handle->poll_cb(handle, 0, events);
|
||||
}
|
||||
}
|
||||
|
||||
if ((handle->events & ~(handle->submitted_events_1 |
|
||||
handle->submitted_events_2)) != 0) {
|
||||
uv__slow_poll_submit_poll_req(loop, handle);
|
||||
} else if ((handle->flags & UV__HANDLE_CLOSING) &&
|
||||
handle->submitted_events_1 == 0 &&
|
||||
handle->submitted_events_2 == 0) {
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__slow_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) {
|
||||
assert(handle->type == UV_POLL);
|
||||
assert(!(handle->flags & UV__HANDLE_CLOSING));
|
||||
assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0);
|
||||
|
||||
handle->events = events;
|
||||
|
||||
if (handle->events != 0) {
|
||||
uv__handle_start(handle);
|
||||
} else {
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
|
||||
if ((handle->events &
|
||||
~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) {
|
||||
uv__slow_poll_submit_poll_req(handle->loop, handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__slow_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
|
||||
handle->events = 0;
|
||||
uv__handle_closing(handle);
|
||||
|
||||
if (handle->submitted_events_1 == 0 &&
|
||||
handle->submitted_events_2 == 0) {
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
|
||||
return uv_poll_init_socket(loop, handle, (SOCKET) uv__get_osfhandle(fd));
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
|
||||
uv_os_sock_t socket) {
|
||||
WSAPROTOCOL_INFOW protocol_info;
|
||||
int len;
|
||||
SOCKET peer_socket, base_socket;
|
||||
DWORD bytes;
|
||||
DWORD yes = 1;
|
||||
|
||||
/* Set the socket to nonblocking mode */
|
||||
if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR)
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
|
||||
/* Try to obtain a base handle for the socket. This increases this chances */
|
||||
/* that we find an AFD handle and are able to use the fast poll mechanism. */
|
||||
/* This will always fail on windows XP/2k3, since they don't support the */
|
||||
/* SIO_BASE_HANDLE ioctl. */
|
||||
#ifndef NDEBUG
|
||||
base_socket = INVALID_SOCKET;
|
||||
#endif
|
||||
|
||||
if (WSAIoctl(socket,
|
||||
SIO_BASE_HANDLE,
|
||||
NULL,
|
||||
0,
|
||||
&base_socket,
|
||||
sizeof base_socket,
|
||||
&bytes,
|
||||
NULL,
|
||||
NULL) == 0) {
|
||||
assert(base_socket != 0 && base_socket != INVALID_SOCKET);
|
||||
socket = base_socket;
|
||||
}
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
|
||||
handle->socket = socket;
|
||||
handle->events = 0;
|
||||
|
||||
/* Obtain protocol information about the socket. */
|
||||
len = sizeof protocol_info;
|
||||
if (getsockopt(socket,
|
||||
SOL_SOCKET,
|
||||
SO_PROTOCOL_INFOW,
|
||||
(char*) &protocol_info,
|
||||
&len) != 0) {
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
}
|
||||
|
||||
/* Get the peer socket that is needed to enable fast poll. If the returned */
|
||||
/* value is NULL, the protocol is not implemented by MSAFD and we'll have */
|
||||
/* to use slow mode. */
|
||||
peer_socket = uv__fast_poll_get_peer_socket(loop, &protocol_info);
|
||||
|
||||
if (peer_socket != INVALID_SOCKET) {
|
||||
/* Initialize fast poll specific fields. */
|
||||
handle->peer_socket = peer_socket;
|
||||
} else {
|
||||
/* Initialize slow poll specific fields. */
|
||||
handle->flags |= UV_HANDLE_POLL_SLOW;
|
||||
}
|
||||
|
||||
/* Initialize 2 poll reqs. */
|
||||
handle->submitted_events_1 = 0;
|
||||
uv_req_init(loop, (uv_req_t*) &(handle->poll_req_1));
|
||||
handle->poll_req_1.type = UV_POLL_REQ;
|
||||
handle->poll_req_1.data = handle;
|
||||
|
||||
handle->submitted_events_2 = 0;
|
||||
uv_req_init(loop, (uv_req_t*) &(handle->poll_req_2));
|
||||
handle->poll_req_2.type = UV_POLL_REQ;
|
||||
handle->poll_req_2.data = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) {
|
||||
int err;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
|
||||
err = uv__fast_poll_set(handle->loop, handle, events);
|
||||
} else {
|
||||
err = uv__slow_poll_set(handle->loop, handle, events);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
handle->poll_cb = cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_stop(uv_poll_t* handle) {
|
||||
int err;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
|
||||
err = uv__fast_poll_set(handle->loop, handle, 0);
|
||||
} else {
|
||||
err = uv__slow_poll_set(handle->loop, handle, 0);
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) {
|
||||
if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
|
||||
uv__fast_poll_process_poll_req(loop, handle, req);
|
||||
} else {
|
||||
uv__slow_poll_process_poll_req(loop, handle, req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
|
||||
if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
|
||||
return uv__fast_poll_close(loop, handle);
|
||||
} else {
|
||||
return uv__slow_poll_close(loop, handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) {
|
||||
assert(handle->flags & UV__HANDLE_CLOSING);
|
||||
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||
|
||||
assert(handle->submitted_events_1 == 0);
|
||||
assert(handle->submitted_events_2 == 0);
|
||||
|
||||
uv__handle_close(handle);
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
|
||||
|
||||
/*
|
||||
* The `child_stdio_buffer` buffer has the following layout:
|
||||
* int number_of_fds
|
||||
* unsigned char crt_flags[number_of_fds]
|
||||
* HANDLE os_handle[number_of_fds]
|
||||
*/
|
||||
#define CHILD_STDIO_SIZE(count) \
|
||||
(sizeof(int) + \
|
||||
sizeof(unsigned char) * (count) + \
|
||||
sizeof(uintptr_t) * (count))
|
||||
|
||||
#define CHILD_STDIO_COUNT(buffer) \
|
||||
*((unsigned int*) (buffer))
|
||||
|
||||
#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \
|
||||
*((unsigned char*) (buffer) + sizeof(int) + fd)
|
||||
|
||||
#define CHILD_STDIO_HANDLE(buffer, fd) \
|
||||
*((HANDLE*) ((unsigned char*) (buffer) + \
|
||||
sizeof(int) + \
|
||||
sizeof(unsigned char) * \
|
||||
CHILD_STDIO_COUNT((buffer)) + \
|
||||
sizeof(HANDLE) * (fd)))
|
||||
|
||||
|
||||
/* CRT file descriptor mode flags */
|
||||
#define FOPEN 0x01
|
||||
#define FEOFLAG 0x02
|
||||
#define FCRLF 0x04
|
||||
#define FPIPE 0x08
|
||||
#define FNOINHERIT 0x10
|
||||
#define FAPPEND 0x20
|
||||
#define FDEV 0x40
|
||||
#define FTEXT 0x80
|
||||
|
||||
|
||||
/*
|
||||
* Clear the HANDLE_FLAG_INHERIT flag from all HANDLEs that were inherited
|
||||
* the parent process. Don't check for errors - the stdio handles may not be
|
||||
* valid, or may be closed already. There is no guarantee that this function
|
||||
* does a perfect job.
|
||||
*/
|
||||
void uv_disable_stdio_inheritance(void) {
|
||||
HANDLE handle;
|
||||
STARTUPINFOW si;
|
||||
|
||||
/* Make the windows stdio handles non-inheritable. */
|
||||
handle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
||||
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
||||
|
||||
handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
||||
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
||||
|
||||
handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
||||
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
||||
|
||||
/* Make inherited CRT FDs non-inheritable. */
|
||||
GetStartupInfoW(&si);
|
||||
if (uv__stdio_verify(si.lpReserved2, si.cbReserved2))
|
||||
uv__stdio_noinherit(si.lpReserved2);
|
||||
}
|
||||
|
||||
|
||||
static int uv__create_stdio_pipe_pair(uv_loop_t* loop,
|
||||
uv_pipe_t* server_pipe, HANDLE* child_pipe_ptr, unsigned int flags) {
|
||||
char pipe_name[64];
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
DWORD server_access = 0;
|
||||
DWORD client_access = 0;
|
||||
HANDLE child_pipe = INVALID_HANDLE_VALUE;
|
||||
int err;
|
||||
|
||||
if (flags & UV_READABLE_PIPE) {
|
||||
/* The server needs inbound access too, otherwise CreateNamedPipe() */
|
||||
/* won't give us the FILE_READ_ATTRIBUTES permission. We need that to */
|
||||
/* probe the state of the write buffer when we're trying to shutdown */
|
||||
/* the pipe. */
|
||||
server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND;
|
||||
client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
|
||||
}
|
||||
if (flags & UV_WRITABLE_PIPE) {
|
||||
server_access |= PIPE_ACCESS_INBOUND;
|
||||
client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||||
}
|
||||
|
||||
/* Create server pipe handle. */
|
||||
err = uv_stdio_pipe_server(loop,
|
||||
server_pipe,
|
||||
server_access,
|
||||
pipe_name,
|
||||
sizeof(pipe_name));
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Create child pipe handle. */
|
||||
sa.nLength = sizeof sa;
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
child_pipe = CreateFileA(pipe_name,
|
||||
client_access,
|
||||
0,
|
||||
&sa,
|
||||
OPEN_EXISTING,
|
||||
server_pipe->ipc ? FILE_FLAG_OVERLAPPED : 0,
|
||||
NULL);
|
||||
if (child_pipe == INVALID_HANDLE_VALUE) {
|
||||
err = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* Validate that the pipe was opened in the right mode. */
|
||||
{
|
||||
DWORD mode;
|
||||
BOOL r = GetNamedPipeHandleState(child_pipe,
|
||||
&mode,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
assert(r == TRUE);
|
||||
assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Do a blocking ConnectNamedPipe. This should not block because we have */
|
||||
/* both ends of the pipe created. */
|
||||
if (!ConnectNamedPipe(server_pipe->handle, NULL)) {
|
||||
if (GetLastError() != ERROR_PIPE_CONNECTED) {
|
||||
err = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* The server end is now readable and/or writable. */
|
||||
if (flags & UV_READABLE_PIPE)
|
||||
server_pipe->flags |= UV_HANDLE_WRITABLE;
|
||||
if (flags & UV_WRITABLE_PIPE)
|
||||
server_pipe->flags |= UV_HANDLE_READABLE;
|
||||
|
||||
*child_pipe_ptr = child_pipe;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (server_pipe->handle != INVALID_HANDLE_VALUE) {
|
||||
uv_pipe_cleanup(loop, server_pipe);
|
||||
}
|
||||
|
||||
if (child_pipe != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(child_pipe);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) {
|
||||
HANDLE current_process;
|
||||
|
||||
|
||||
/* _get_osfhandle will sometimes return -2 in case of an error. This seems */
|
||||
/* to happen when fd <= 2 and the process' corresponding stdio handle is */
|
||||
/* set to NULL. Unfortunately DuplicateHandle will happily duplicate */
|
||||
/* (HANDLE) -2, so this situation goes unnoticed until someone tries to */
|
||||
/* use the duplicate. Therefore we filter out known-invalid handles here. */
|
||||
if (handle == INVALID_HANDLE_VALUE ||
|
||||
handle == NULL ||
|
||||
handle == (HANDLE) -2) {
|
||||
*dup = INVALID_HANDLE_VALUE;
|
||||
return ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
current_process = GetCurrentProcess();
|
||||
|
||||
if (!DuplicateHandle(current_process,
|
||||
handle,
|
||||
current_process,
|
||||
dup,
|
||||
0,
|
||||
TRUE,
|
||||
DUPLICATE_SAME_ACCESS)) {
|
||||
*dup = INVALID_HANDLE_VALUE;
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) {
|
||||
HANDLE handle;
|
||||
|
||||
if (fd == -1) {
|
||||
*dup = INVALID_HANDLE_VALUE;
|
||||
return ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
handle = uv__get_osfhandle(fd);
|
||||
return uv__duplicate_handle(loop, handle, dup);
|
||||
}
|
||||
|
||||
|
||||
int uv__create_nul_handle(HANDLE* handle_ptr,
|
||||
DWORD access) {
|
||||
HANDLE handle;
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
|
||||
sa.nLength = sizeof sa;
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
handle = CreateFileW(L"NUL",
|
||||
access,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
&sa,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
*handle_ptr = handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__stdio_create(uv_loop_t* loop,
|
||||
const uv_process_options_t* options,
|
||||
BYTE** buffer_ptr) {
|
||||
BYTE* buffer;
|
||||
int count, i;
|
||||
int err;
|
||||
|
||||
count = options->stdio_count;
|
||||
|
||||
if (count < 0 || count > 255) {
|
||||
/* Only support FDs 0-255 */
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
} else if (count < 3) {
|
||||
/* There should always be at least 3 stdio handles. */
|
||||
count = 3;
|
||||
}
|
||||
|
||||
/* Allocate the child stdio buffer */
|
||||
buffer = (BYTE*) uv__malloc(CHILD_STDIO_SIZE(count));
|
||||
if (buffer == NULL) {
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
/* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */
|
||||
/* clean up on failure. */
|
||||
CHILD_STDIO_COUNT(buffer) = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
||||
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv_stdio_container_t fdopt;
|
||||
if (i < options->stdio_count) {
|
||||
fdopt = options->stdio[i];
|
||||
} else {
|
||||
fdopt.flags = UV_IGNORE;
|
||||
}
|
||||
|
||||
switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
|
||||
UV_INHERIT_STREAM)) {
|
||||
case UV_IGNORE:
|
||||
/* Starting a process with no stdin/stout/stderr can confuse it. */
|
||||
/* So no matter what the user specified, we make sure the first */
|
||||
/* three FDs are always open in their typical modes, e.g. stdin */
|
||||
/* be readable and stdout/err should be writable. For FDs > 2, don't */
|
||||
/* do anything - all handles in the stdio buffer are initialized with */
|
||||
/* INVALID_HANDLE_VALUE, which should be okay. */
|
||||
if (i <= 2) {
|
||||
DWORD access = (i == 0) ? FILE_GENERIC_READ :
|
||||
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||||
|
||||
err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
|
||||
access);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
||||
}
|
||||
break;
|
||||
|
||||
case UV_CREATE_PIPE: {
|
||||
/* Create a pair of two connected pipe ends; one end is turned into */
|
||||
/* an uv_pipe_t for use by the parent. The other one is given to */
|
||||
/* the child. */
|
||||
uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream;
|
||||
HANDLE child_pipe = INVALID_HANDLE_VALUE;
|
||||
|
||||
/* Create a new, connected pipe pair. stdio[i].stream should point */
|
||||
/* to an uninitialized, but not connected pipe handle. */
|
||||
assert(fdopt.data.stream->type == UV_NAMED_PIPE);
|
||||
assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION));
|
||||
assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER));
|
||||
|
||||
err = uv__create_stdio_pipe_pair(loop,
|
||||
parent_pipe,
|
||||
&child_pipe,
|
||||
fdopt.flags);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
|
||||
break;
|
||||
}
|
||||
|
||||
case UV_INHERIT_FD: {
|
||||
/* Inherit a raw FD. */
|
||||
HANDLE child_handle;
|
||||
|
||||
/* Make an inheritable duplicate of the handle. */
|
||||
err = uv__duplicate_fd(loop, fdopt.data.fd, &child_handle);
|
||||
if (err) {
|
||||
/* If fdopt.data.fd is not valid and fd fd <= 2, then ignore the */
|
||||
/* error. */
|
||||
if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
||||
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
|
||||
break;
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Figure out what the type is. */
|
||||
switch (GetFileType(child_handle)) {
|
||||
case FILE_TYPE_DISK:
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN;
|
||||
break;
|
||||
|
||||
case FILE_TYPE_PIPE:
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
|
||||
|
||||
case FILE_TYPE_CHAR:
|
||||
case FILE_TYPE_REMOTE:
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
||||
break;
|
||||
|
||||
case FILE_TYPE_UNKNOWN:
|
||||
if (GetLastError() != 0) {
|
||||
err = GetLastError();
|
||||
CloseHandle(child_handle);
|
||||
goto error;
|
||||
}
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
|
||||
break;
|
||||
}
|
||||
|
||||
case UV_INHERIT_STREAM: {
|
||||
/* Use an existing stream as the stdio handle for the child. */
|
||||
HANDLE stream_handle, child_handle;
|
||||
unsigned char crt_flags;
|
||||
uv_stream_t* stream = fdopt.data.stream;
|
||||
|
||||
/* Leech the handle out of the stream. */
|
||||
if (stream->type == UV_TTY) {
|
||||
stream_handle = ((uv_tty_t*) stream)->handle;
|
||||
crt_flags = FOPEN | FDEV;
|
||||
} else if (stream->type == UV_NAMED_PIPE &&
|
||||
stream->flags & UV_HANDLE_CONNECTION) {
|
||||
stream_handle = ((uv_pipe_t*) stream)->handle;
|
||||
crt_flags = FOPEN | FPIPE;
|
||||
} else {
|
||||
stream_handle = INVALID_HANDLE_VALUE;
|
||||
crt_flags = 0;
|
||||
}
|
||||
|
||||
if (stream_handle == NULL ||
|
||||
stream_handle == INVALID_HANDLE_VALUE) {
|
||||
/* The handle is already closed, or not yet created, or the */
|
||||
/* stream type is not supported. */
|
||||
err = ERROR_NOT_SUPPORTED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Make an inheritable copy of the handle. */
|
||||
err = uv__duplicate_handle(loop, stream_handle, &child_handle);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*buffer_ptr = buffer;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uv__stdio_destroy(buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void uv__stdio_destroy(BYTE* buffer) {
|
||||
int i, count;
|
||||
|
||||
count = CHILD_STDIO_COUNT(buffer);
|
||||
for (i = 0; i < count; i++) {
|
||||
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
uv__free(buffer);
|
||||
}
|
||||
|
||||
|
||||
void uv__stdio_noinherit(BYTE* buffer) {
|
||||
int i, count;
|
||||
|
||||
count = CHILD_STDIO_COUNT(buffer);
|
||||
for (i = 0; i < count; i++) {
|
||||
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv__stdio_verify(BYTE* buffer, WORD size) {
|
||||
unsigned int count;
|
||||
|
||||
/* Check the buffer pointer. */
|
||||
if (buffer == NULL)
|
||||
return 0;
|
||||
|
||||
/* Verify that the buffer is at least big enough to hold the count. */
|
||||
if (size < CHILD_STDIO_SIZE(0))
|
||||
return 0;
|
||||
|
||||
/* Verify if the count is within range. */
|
||||
count = CHILD_STDIO_COUNT(buffer);
|
||||
if (count > 256)
|
||||
return 0;
|
||||
|
||||
/* Verify that the buffer size is big enough to hold info for N FDs. */
|
||||
if (size < CHILD_STDIO_SIZE(count))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
WORD uv__stdio_size(BYTE* buffer) {
|
||||
return (WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)));
|
||||
}
|
||||
|
||||
|
||||
HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
|
||||
return CHILD_STDIO_HANDLE(buffer, fd);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,224 @@
|
||||
/* 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_REQ_INL_H_
|
||||
#define UV_WIN_REQ_INL_H_
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
#define SET_REQ_STATUS(req, status) \
|
||||
(req)->u.io.overlapped.Internal = (ULONG_PTR) (status)
|
||||
|
||||
#define SET_REQ_ERROR(req, error) \
|
||||
SET_REQ_STATUS((req), NTSTATUS_FROM_WIN32((error)))
|
||||
|
||||
#define SET_REQ_SUCCESS(req) \
|
||||
SET_REQ_STATUS((req), STATUS_SUCCESS)
|
||||
|
||||
#define GET_REQ_STATUS(req) \
|
||||
((NTSTATUS) (req)->u.io.overlapped.Internal)
|
||||
|
||||
#define REQ_SUCCESS(req) \
|
||||
(NT_SUCCESS(GET_REQ_STATUS((req))))
|
||||
|
||||
#define GET_REQ_ERROR(req) \
|
||||
(pRtlNtStatusToDosError(GET_REQ_STATUS((req))))
|
||||
|
||||
#define GET_REQ_SOCK_ERROR(req) \
|
||||
(uv_ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
|
||||
|
||||
|
||||
#define REGISTER_HANDLE_REQ(loop, handle, req) \
|
||||
do { \
|
||||
INCREASE_ACTIVE_COUNT((loop), (handle)); \
|
||||
uv__req_register((loop), (req)); \
|
||||
} while (0)
|
||||
|
||||
#define UNREGISTER_HANDLE_REQ(loop, handle, req) \
|
||||
do { \
|
||||
DECREASE_ACTIVE_COUNT((loop), (handle)); \
|
||||
uv__req_unregister((loop), (req)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define UV_SUCCEEDED_WITHOUT_IOCP(result) \
|
||||
((result) && (handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP))
|
||||
|
||||
#define UV_SUCCEEDED_WITH_IOCP(result) \
|
||||
((result) || (GetLastError() == ERROR_IO_PENDING))
|
||||
|
||||
|
||||
#define POST_COMPLETION_FOR_REQ(loop, req) \
|
||||
if (!PostQueuedCompletionStatus((loop)->iocp, \
|
||||
0, \
|
||||
0, \
|
||||
&((req)->u.io.overlapped))) { \
|
||||
uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); \
|
||||
}
|
||||
|
||||
|
||||
INLINE static void uv_req_init(uv_loop_t* loop, uv_req_t* req) {
|
||||
req->type = UV_UNKNOWN_REQ;
|
||||
SET_REQ_SUCCESS(req);
|
||||
}
|
||||
|
||||
|
||||
INLINE static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) {
|
||||
return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped);
|
||||
}
|
||||
|
||||
|
||||
INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) {
|
||||
req->next_req = NULL;
|
||||
if (loop->pending_reqs_tail) {
|
||||
#ifdef _DEBUG
|
||||
/* Ensure the request is not already in the queue, or the queue
|
||||
* will get corrupted.
|
||||
*/
|
||||
uv_req_t* current = loop->pending_reqs_tail;
|
||||
do {
|
||||
assert(req != current);
|
||||
current = current->next_req;
|
||||
} while(current != loop->pending_reqs_tail);
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define DELEGATE_STREAM_REQ(loop, req, method, handle_at) \
|
||||
do { \
|
||||
switch (((uv_handle_t*) (req)->handle_at)->type) { \
|
||||
case UV_TCP: \
|
||||
uv_process_tcp_##method##_req(loop, \
|
||||
(uv_tcp_t*) ((req)->handle_at), \
|
||||
req); \
|
||||
break; \
|
||||
\
|
||||
case UV_NAMED_PIPE: \
|
||||
uv_process_pipe_##method##_req(loop, \
|
||||
(uv_pipe_t*) ((req)->handle_at), \
|
||||
req); \
|
||||
break; \
|
||||
\
|
||||
case UV_TTY: \
|
||||
uv_process_tty_##method##_req(loop, \
|
||||
(uv_tty_t*) ((req)->handle_at), \
|
||||
req); \
|
||||
break; \
|
||||
\
|
||||
default: \
|
||||
assert(0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
INLINE static int uv_process_reqs(uv_loop_t* loop) {
|
||||
uv_req_t* req;
|
||||
uv_req_t* first;
|
||||
uv_req_t* next;
|
||||
|
||||
if (loop->pending_reqs_tail == NULL)
|
||||
return 0;
|
||||
|
||||
first = loop->pending_reqs_tail->next_req;
|
||||
next = first;
|
||||
loop->pending_reqs_tail = NULL;
|
||||
|
||||
while (next != NULL) {
|
||||
req = next;
|
||||
next = req->next_req != first ? req->next_req : NULL;
|
||||
|
||||
switch (req->type) {
|
||||
case UV_READ:
|
||||
DELEGATE_STREAM_REQ(loop, req, read, data);
|
||||
break;
|
||||
|
||||
case UV_WRITE:
|
||||
DELEGATE_STREAM_REQ(loop, (uv_write_t*) req, write, handle);
|
||||
break;
|
||||
|
||||
case UV_ACCEPT:
|
||||
DELEGATE_STREAM_REQ(loop, req, accept, data);
|
||||
break;
|
||||
|
||||
case UV_CONNECT:
|
||||
DELEGATE_STREAM_REQ(loop, (uv_connect_t*) req, connect, handle);
|
||||
break;
|
||||
|
||||
case UV_SHUTDOWN:
|
||||
/* Tcp shutdown requests don't come here. */
|
||||
assert(((uv_shutdown_t*) req)->handle->type == UV_NAMED_PIPE);
|
||||
uv_process_pipe_shutdown_req(
|
||||
loop,
|
||||
(uv_pipe_t*) ((uv_shutdown_t*) req)->handle,
|
||||
(uv_shutdown_t*) req);
|
||||
break;
|
||||
|
||||
case UV_UDP_RECV:
|
||||
uv_process_udp_recv_req(loop, (uv_udp_t*) req->data, req);
|
||||
break;
|
||||
|
||||
case UV_UDP_SEND:
|
||||
uv_process_udp_send_req(loop,
|
||||
((uv_udp_send_t*) req)->handle,
|
||||
(uv_udp_send_t*) req);
|
||||
break;
|
||||
|
||||
case UV_WAKEUP:
|
||||
uv_process_async_wakeup_req(loop, (uv_async_t*) req->data, req);
|
||||
break;
|
||||
|
||||
case UV_SIGNAL_REQ:
|
||||
uv_process_signal_req(loop, (uv_signal_t*) req->data, req);
|
||||
break;
|
||||
|
||||
case UV_POLL_REQ:
|
||||
uv_process_poll_req(loop, (uv_poll_t*) req->data, req);
|
||||
break;
|
||||
|
||||
case UV_PROCESS_EXIT:
|
||||
uv_process_proc_exit(loop, (uv_process_t*) req->data);
|
||||
break;
|
||||
|
||||
case UV_FS_EVENT_REQ:
|
||||
uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* UV_WIN_REQ_INL_H_ */
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
@@ -0,0 +1,356 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
|
||||
RB_HEAD(uv_signal_tree_s, uv_signal_s);
|
||||
|
||||
static struct uv_signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree);
|
||||
static ssize_t volatile uv__signal_control_handler_refs = 0;
|
||||
static CRITICAL_SECTION uv__signal_lock;
|
||||
|
||||
|
||||
void uv_signals_init() {
|
||||
InitializeCriticalSection(&uv__signal_lock);
|
||||
}
|
||||
|
||||
|
||||
static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) {
|
||||
/* Compare signums first so all watchers with the same signnum end up */
|
||||
/* adjacent. */
|
||||
if (w1->signum < w2->signum) return -1;
|
||||
if (w1->signum > w2->signum) return 1;
|
||||
|
||||
/* Sort by loop pointer, so we can easily look up the first item after */
|
||||
/* { .signum = x, .loop = NULL } */
|
||||
if ((uintptr_t) w1->loop < (uintptr_t) w2->loop) return -1;
|
||||
if ((uintptr_t) w1->loop > (uintptr_t) w2->loop) return 1;
|
||||
|
||||
if ((uintptr_t) w1 < (uintptr_t) w2) return -1;
|
||||
if ((uintptr_t) w1 > (uintptr_t) w2) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare);
|
||||
|
||||
|
||||
/*
|
||||
* Dispatches signal {signum} to all active uv_signal_t watchers in all loops.
|
||||
* Returns 1 if the signal was dispatched to any watcher, or 0 if there were
|
||||
* no active signal watchers observing this signal.
|
||||
*/
|
||||
int uv__signal_dispatch(int signum) {
|
||||
uv_signal_t lookup;
|
||||
uv_signal_t* handle;
|
||||
int dispatched = 0;
|
||||
|
||||
EnterCriticalSection(&uv__signal_lock);
|
||||
|
||||
lookup.signum = signum;
|
||||
lookup.loop = NULL;
|
||||
|
||||
for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup);
|
||||
handle != NULL && handle->signum == signum;
|
||||
handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) {
|
||||
unsigned long previous = InterlockedExchange(
|
||||
(volatile LONG*) &handle->pending_signum, signum);
|
||||
|
||||
if (!previous) {
|
||||
POST_COMPLETION_FOR_REQ(handle->loop, &handle->signal_req);
|
||||
}
|
||||
|
||||
dispatched = 1;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&uv__signal_lock);
|
||||
|
||||
return dispatched;
|
||||
}
|
||||
|
||||
|
||||
static BOOL WINAPI uv__signal_control_handler(DWORD type) {
|
||||
switch (type) {
|
||||
case CTRL_C_EVENT:
|
||||
return uv__signal_dispatch(SIGINT);
|
||||
|
||||
case CTRL_BREAK_EVENT:
|
||||
return uv__signal_dispatch(SIGBREAK);
|
||||
|
||||
case CTRL_CLOSE_EVENT:
|
||||
if (uv__signal_dispatch(SIGHUP)) {
|
||||
/* Windows will terminate the process after the control handler */
|
||||
/* returns. After that it will just terminate our process. Therefore */
|
||||
/* block the signal handler so the main loop has some time to pick */
|
||||
/* up the signal and do something for a few seconds. */
|
||||
Sleep(INFINITE);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
case CTRL_LOGOFF_EVENT:
|
||||
case CTRL_SHUTDOWN_EVENT:
|
||||
/* These signals are only sent to services. Services have their own */
|
||||
/* notification mechanism, so there's no point in handling these. */
|
||||
|
||||
default:
|
||||
/* We don't handle these. */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__signal_register_control_handler() {
|
||||
/* When this function is called, the uv__signal_lock must be held. */
|
||||
|
||||
/* If the console control handler has already been hooked, just add a */
|
||||
/* reference. */
|
||||
if (uv__signal_control_handler_refs > 0) {
|
||||
uv__signal_control_handler_refs++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE))
|
||||
return GetLastError();
|
||||
|
||||
uv__signal_control_handler_refs++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_unregister_control_handler() {
|
||||
/* When this function is called, the uv__signal_lock must be held. */
|
||||
BOOL r;
|
||||
|
||||
/* Don't unregister if the number of console control handlers exceeds one. */
|
||||
/* Just remove a reference in that case. */
|
||||
if (uv__signal_control_handler_refs > 1) {
|
||||
uv__signal_control_handler_refs--;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(uv__signal_control_handler_refs == 1);
|
||||
|
||||
r = SetConsoleCtrlHandler(uv__signal_control_handler, FALSE);
|
||||
/* This should never fail; if it does it is probably a bug in libuv. */
|
||||
assert(r);
|
||||
|
||||
uv__signal_control_handler_refs--;
|
||||
}
|
||||
|
||||
|
||||
static int uv__signal_register(int signum) {
|
||||
switch (signum) {
|
||||
case SIGINT:
|
||||
case SIGBREAK:
|
||||
case SIGHUP:
|
||||
return uv__signal_register_control_handler();
|
||||
|
||||
case SIGWINCH:
|
||||
/* SIGWINCH is generated in tty.c. No need to register anything. */
|
||||
return 0;
|
||||
|
||||
case SIGILL:
|
||||
case SIGABRT_COMPAT:
|
||||
case SIGFPE:
|
||||
case SIGSEGV:
|
||||
case SIGTERM:
|
||||
case SIGABRT:
|
||||
/* Signal is never raised. */
|
||||
return 0;
|
||||
|
||||
default:
|
||||
/* Invalid signal. */
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv__signal_unregister(int signum) {
|
||||
switch (signum) {
|
||||
case SIGINT:
|
||||
case SIGBREAK:
|
||||
case SIGHUP:
|
||||
uv__signal_unregister_control_handler();
|
||||
return;
|
||||
|
||||
case SIGWINCH:
|
||||
/* SIGWINCH is generated in tty.c. No need to unregister anything. */
|
||||
return;
|
||||
|
||||
case SIGILL:
|
||||
case SIGABRT_COMPAT:
|
||||
case SIGFPE:
|
||||
case SIGSEGV:
|
||||
case SIGTERM:
|
||||
case SIGABRT:
|
||||
/* Nothing is registered for this signal. */
|
||||
return;
|
||||
|
||||
default:
|
||||
/* Libuv bug. */
|
||||
assert(0 && "Invalid signum");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
|
||||
uv_req_t* req;
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
|
||||
handle->pending_signum = 0;
|
||||
handle->signum = 0;
|
||||
handle->signal_cb = NULL;
|
||||
|
||||
req = &handle->signal_req;
|
||||
uv_req_init(loop, req);
|
||||
req->type = UV_SIGNAL_REQ;
|
||||
req->data = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_signal_stop(uv_signal_t* handle) {
|
||||
uv_signal_t* removed_handle;
|
||||
|
||||
/* If the watcher wasn't started, this is a no-op. */
|
||||
if (handle->signum == 0)
|
||||
return 0;
|
||||
|
||||
EnterCriticalSection(&uv__signal_lock);
|
||||
|
||||
uv__signal_unregister(handle->signum);
|
||||
|
||||
removed_handle = RB_REMOVE(uv_signal_tree_s, &uv__signal_tree, handle);
|
||||
assert(removed_handle == handle);
|
||||
|
||||
LeaveCriticalSection(&uv__signal_lock);
|
||||
|
||||
handle->signum = 0;
|
||||
uv__handle_stop(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
|
||||
int err;
|
||||
|
||||
/* If the user supplies signum == 0, then return an error already. If the */
|
||||
/* signum is otherwise invalid then uv__signal_register will find out */
|
||||
/* eventually. */
|
||||
if (signum == 0) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
/* Short circuit: if the signal watcher is already watching {signum} don't */
|
||||
/* go through the process of deregistering and registering the handler. */
|
||||
/* Additionally, this avoids pending signals getting lost in the (small) */
|
||||
/* time frame that handle->signum == 0. */
|
||||
if (signum == handle->signum) {
|
||||
handle->signal_cb = signal_cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the signal handler was already active, stop it first. */
|
||||
if (handle->signum != 0) {
|
||||
int r = uv_signal_stop(handle);
|
||||
/* uv_signal_stop is infallible. */
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
EnterCriticalSection(&uv__signal_lock);
|
||||
|
||||
err = uv__signal_register(signum);
|
||||
if (err) {
|
||||
/* Uh-oh, didn't work. */
|
||||
LeaveCriticalSection(&uv__signal_lock);
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
handle->signum = signum;
|
||||
RB_INSERT(uv_signal_tree_s, &uv__signal_tree, handle);
|
||||
|
||||
LeaveCriticalSection(&uv__signal_lock);
|
||||
|
||||
handle->signal_cb = signal_cb;
|
||||
uv__handle_start(handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
|
||||
uv_req_t* req) {
|
||||
long dispatched_signum;
|
||||
|
||||
assert(handle->type == UV_SIGNAL);
|
||||
assert(req->type == UV_SIGNAL_REQ);
|
||||
|
||||
dispatched_signum = InterlockedExchange(
|
||||
(volatile LONG*) &handle->pending_signum, 0);
|
||||
assert(dispatched_signum != 0);
|
||||
|
||||
/* Check if the pending signal equals the signum that we are watching for. */
|
||||
/* These can get out of sync when the handler is stopped and restarted */
|
||||
/* while the signal_req is pending. */
|
||||
if (dispatched_signum == handle->signum)
|
||||
handle->signal_cb(handle, dispatched_signum);
|
||||
|
||||
if (handle->flags & UV__HANDLE_CLOSING) {
|
||||
/* When it is closing, it must be stopped at this point. */
|
||||
assert(handle->signum == 0);
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle) {
|
||||
uv_signal_stop(handle);
|
||||
uv__handle_closing(handle);
|
||||
|
||||
if (handle->pending_signum == 0) {
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle) {
|
||||
assert(handle->flags & UV__HANDLE_CLOSING);
|
||||
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||
|
||||
assert(handle->signum == 0);
|
||||
assert(handle->pending_signum == 0);
|
||||
|
||||
handle->flags |= UV_HANDLE_CLOSED;
|
||||
|
||||
uv__handle_close(handle);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright the libuv project 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.
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Emulate snprintf() on MSVC<2015, _snprintf() doesn't zero-terminate the buffer
|
||||
* on overflow...
|
||||
*/
|
||||
int snprintf(char* buf, size_t len, const char* fmt, ...) {
|
||||
int n;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
n = _vscprintf(fmt, ap);
|
||||
vsnprintf_s(buf, len, _TRUNCATE, fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/* 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_STREAM_INL_H_
|
||||
#define UV_WIN_STREAM_INL_H_
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
|
||||
INLINE static void uv_stream_init(uv_loop_t* loop,
|
||||
uv_stream_t* handle,
|
||||
uv_handle_type type) {
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, type);
|
||||
handle->write_queue_size = 0;
|
||||
handle->activecnt = 0;
|
||||
}
|
||||
|
||||
|
||||
INLINE static void uv_connection_init(uv_stream_t* handle) {
|
||||
handle->flags |= UV_HANDLE_CONNECTION;
|
||||
handle->stream.conn.write_reqs_pending = 0;
|
||||
|
||||
uv_req_init(handle->loop, (uv_req_t*) &(handle->read_req));
|
||||
handle->read_req.event_handle = NULL;
|
||||
handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
|
||||
handle->read_req.type = UV_READ;
|
||||
handle->read_req.data = handle;
|
||||
|
||||
handle->stream.conn.shutdown_req = NULL;
|
||||
}
|
||||
|
||||
|
||||
#endif /* UV_WIN_STREAM_INL_H_ */
|
||||
@@ -0,0 +1,249 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
|
||||
int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
|
||||
int err;
|
||||
|
||||
err = ERROR_INVALID_PARAMETER;
|
||||
switch (stream->type) {
|
||||
case UV_TCP:
|
||||
err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb);
|
||||
break;
|
||||
case UV_NAMED_PIPE:
|
||||
err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
int uv_accept(uv_stream_t* server, uv_stream_t* client) {
|
||||
int err;
|
||||
|
||||
err = ERROR_INVALID_PARAMETER;
|
||||
switch (server->type) {
|
||||
case UV_TCP:
|
||||
err = uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client);
|
||||
break;
|
||||
case UV_NAMED_PIPE:
|
||||
err = uv_pipe_accept((uv_pipe_t*)server, client);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb,
|
||||
uv_read_cb read_cb) {
|
||||
int err;
|
||||
|
||||
if (handle->flags & UV_HANDLE_READING) {
|
||||
return UV_EALREADY;
|
||||
}
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_READABLE)) {
|
||||
return UV_ENOTCONN;
|
||||
}
|
||||
|
||||
err = ERROR_INVALID_PARAMETER;
|
||||
switch (handle->type) {
|
||||
case UV_TCP:
|
||||
err = uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb);
|
||||
break;
|
||||
case UV_NAMED_PIPE:
|
||||
err = uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb);
|
||||
break;
|
||||
case UV_TTY:
|
||||
err = uv_tty_read_start((uv_tty_t*) handle, alloc_cb, read_cb);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
int uv_read_stop(uv_stream_t* handle) {
|
||||
int err;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_READING))
|
||||
return 0;
|
||||
|
||||
err = 0;
|
||||
if (handle->type == UV_TTY) {
|
||||
err = uv_tty_read_stop((uv_tty_t*) handle);
|
||||
} else {
|
||||
if (handle->type == UV_NAMED_PIPE) {
|
||||
uv__pipe_stop_read((uv_pipe_t*) handle);
|
||||
} else {
|
||||
handle->flags &= ~UV_HANDLE_READING;
|
||||
}
|
||||
DECREASE_ACTIVE_COUNT(handle->loop, handle);
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
int uv_write(uv_write_t* req,
|
||||
uv_stream_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
uv_write_cb cb) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
int err;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_WRITABLE)) {
|
||||
return UV_EPIPE;
|
||||
}
|
||||
|
||||
err = ERROR_INVALID_PARAMETER;
|
||||
switch (handle->type) {
|
||||
case UV_TCP:
|
||||
err = uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb);
|
||||
break;
|
||||
case UV_NAMED_PIPE:
|
||||
err = uv_pipe_write(loop, req, (uv_pipe_t*) handle, bufs, nbufs, cb);
|
||||
break;
|
||||
case UV_TTY:
|
||||
err = uv_tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
int uv_write2(uv_write_t* req,
|
||||
uv_stream_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
uv_stream_t* send_handle,
|
||||
uv_write_cb cb) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
int err;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_WRITABLE)) {
|
||||
return UV_EPIPE;
|
||||
}
|
||||
|
||||
err = ERROR_INVALID_PARAMETER;
|
||||
switch (handle->type) {
|
||||
case UV_NAMED_PIPE:
|
||||
err = uv_pipe_write2(loop,
|
||||
req,
|
||||
(uv_pipe_t*) handle,
|
||||
bufs,
|
||||
nbufs,
|
||||
send_handle,
|
||||
cb);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
int uv_try_write(uv_stream_t* stream,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs) {
|
||||
if (stream->flags & UV__HANDLE_CLOSING)
|
||||
return UV_EBADF;
|
||||
if (!(stream->flags & UV_HANDLE_WRITABLE))
|
||||
return UV_EPIPE;
|
||||
|
||||
switch (stream->type) {
|
||||
case UV_TCP:
|
||||
return uv__tcp_try_write((uv_tcp_t*) stream, bufs, nbufs);
|
||||
case UV_TTY:
|
||||
return uv__tty_try_write((uv_tty_t*) stream, bufs, nbufs);
|
||||
case UV_NAMED_PIPE:
|
||||
return UV_EAGAIN;
|
||||
default:
|
||||
assert(0);
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_WRITABLE)) {
|
||||
return UV_EPIPE;
|
||||
}
|
||||
|
||||
uv_req_init(loop, (uv_req_t*) req);
|
||||
req->type = UV_SHUTDOWN;
|
||||
req->handle = handle;
|
||||
req->cb = cb;
|
||||
|
||||
handle->flags &= ~UV_HANDLE_WRITABLE;
|
||||
handle->stream.conn.shutdown_req = req;
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
|
||||
uv_want_endgame(loop, (uv_handle_t*)handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_is_readable(const uv_stream_t* handle) {
|
||||
return !!(handle->flags & UV_HANDLE_READABLE);
|
||||
}
|
||||
|
||||
|
||||
int uv_is_writable(const uv_stream_t* handle) {
|
||||
return !!(handle->flags & UV_HANDLE_WRITABLE);
|
||||
}
|
||||
|
||||
|
||||
int uv_stream_set_blocking(uv_stream_t* handle, int blocking) {
|
||||
if (handle->type != UV_NAMED_PIPE)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (blocking != 0)
|
||||
handle->flags |= UV_HANDLE_BLOCKING_WRITES;
|
||||
else
|
||||
handle->flags &= ~UV_HANDLE_BLOCKING_WRITES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,697 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
#define HAVE_CONDVAR_API() (pInitializeConditionVariable != NULL)
|
||||
|
||||
static int uv_cond_fallback_init(uv_cond_t* cond);
|
||||
static void uv_cond_fallback_destroy(uv_cond_t* cond);
|
||||
static void uv_cond_fallback_signal(uv_cond_t* cond);
|
||||
static void uv_cond_fallback_broadcast(uv_cond_t* cond);
|
||||
static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex);
|
||||
static int uv_cond_fallback_timedwait(uv_cond_t* cond,
|
||||
uv_mutex_t* mutex, uint64_t timeout);
|
||||
|
||||
static int uv_cond_condvar_init(uv_cond_t* cond);
|
||||
static void uv_cond_condvar_destroy(uv_cond_t* cond);
|
||||
static void uv_cond_condvar_signal(uv_cond_t* cond);
|
||||
static void uv_cond_condvar_broadcast(uv_cond_t* cond);
|
||||
static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex);
|
||||
static int uv_cond_condvar_timedwait(uv_cond_t* cond,
|
||||
uv_mutex_t* mutex, uint64_t timeout);
|
||||
|
||||
|
||||
static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) {
|
||||
DWORD result;
|
||||
HANDLE existing_event, created_event;
|
||||
|
||||
created_event = CreateEvent(NULL, 1, 0, NULL);
|
||||
if (created_event == 0) {
|
||||
/* Could fail in a low-memory situation? */
|
||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
||||
}
|
||||
|
||||
existing_event = InterlockedCompareExchangePointer(&guard->event,
|
||||
created_event,
|
||||
NULL);
|
||||
|
||||
if (existing_event == NULL) {
|
||||
/* We won the race */
|
||||
callback();
|
||||
|
||||
result = SetEvent(created_event);
|
||||
assert(result);
|
||||
guard->ran = 1;
|
||||
|
||||
} else {
|
||||
/* We lost the race. Destroy the event we created and wait for the */
|
||||
/* existing one to become signaled. */
|
||||
CloseHandle(created_event);
|
||||
result = WaitForSingleObject(existing_event, INFINITE);
|
||||
assert(result == WAIT_OBJECT_0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv_once(uv_once_t* guard, void (*callback)(void)) {
|
||||
/* Fast case - avoid WaitForSingleObject. */
|
||||
if (guard->ran) {
|
||||
return;
|
||||
}
|
||||
|
||||
uv__once_inner(guard, callback);
|
||||
}
|
||||
|
||||
|
||||
/* Verify that uv_thread_t can be stored in a TLS slot. */
|
||||
STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*));
|
||||
|
||||
static uv_key_t uv__current_thread_key;
|
||||
static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT;
|
||||
|
||||
|
||||
static void uv__init_current_thread_key(void) {
|
||||
if (uv_key_create(&uv__current_thread_key))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
struct thread_ctx {
|
||||
void (*entry)(void* arg);
|
||||
void* arg;
|
||||
uv_thread_t self;
|
||||
};
|
||||
|
||||
|
||||
static UINT __stdcall uv__thread_start(void* arg) {
|
||||
struct thread_ctx *ctx_p;
|
||||
struct thread_ctx ctx;
|
||||
|
||||
ctx_p = arg;
|
||||
ctx = *ctx_p;
|
||||
uv__free(ctx_p);
|
||||
|
||||
uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
|
||||
uv_key_set(&uv__current_thread_key, (void*) ctx.self);
|
||||
|
||||
ctx.entry(ctx.arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
|
||||
struct thread_ctx* ctx;
|
||||
int err;
|
||||
HANDLE thread;
|
||||
|
||||
ctx = uv__malloc(sizeof(*ctx));
|
||||
if (ctx == NULL)
|
||||
return UV_ENOMEM;
|
||||
|
||||
ctx->entry = entry;
|
||||
ctx->arg = arg;
|
||||
|
||||
/* Create the thread in suspended state so we have a chance to pass
|
||||
* its own creation handle to it */
|
||||
thread = (HANDLE) _beginthreadex(NULL,
|
||||
0,
|
||||
uv__thread_start,
|
||||
ctx,
|
||||
CREATE_SUSPENDED,
|
||||
NULL);
|
||||
if (thread == NULL) {
|
||||
err = errno;
|
||||
uv__free(ctx);
|
||||
} else {
|
||||
err = 0;
|
||||
*tid = thread;
|
||||
ctx->self = thread;
|
||||
ResumeThread(thread);
|
||||
}
|
||||
|
||||
switch (err) {
|
||||
case 0:
|
||||
return 0;
|
||||
case EACCES:
|
||||
return UV_EACCES;
|
||||
case EAGAIN:
|
||||
return UV_EAGAIN;
|
||||
case EINVAL:
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
return UV_EIO;
|
||||
}
|
||||
|
||||
|
||||
uv_thread_t uv_thread_self(void) {
|
||||
uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
|
||||
return (uv_thread_t) uv_key_get(&uv__current_thread_key);
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_join(uv_thread_t *tid) {
|
||||
if (WaitForSingleObject(*tid, INFINITE))
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
else {
|
||||
CloseHandle(*tid);
|
||||
*tid = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
||||
return *t1 == *t2;
|
||||
}
|
||||
|
||||
|
||||
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||
InitializeCriticalSection(mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_mutex_destroy(uv_mutex_t* mutex) {
|
||||
DeleteCriticalSection(mutex);
|
||||
}
|
||||
|
||||
|
||||
void uv_mutex_lock(uv_mutex_t* mutex) {
|
||||
EnterCriticalSection(mutex);
|
||||
}
|
||||
|
||||
|
||||
int uv_mutex_trylock(uv_mutex_t* mutex) {
|
||||
if (TryEnterCriticalSection(mutex))
|
||||
return 0;
|
||||
else
|
||||
return UV_EBUSY;
|
||||
}
|
||||
|
||||
|
||||
void uv_mutex_unlock(uv_mutex_t* mutex) {
|
||||
LeaveCriticalSection(mutex);
|
||||
}
|
||||
|
||||
|
||||
int uv_rwlock_init(uv_rwlock_t* rwlock) {
|
||||
/* Initialize the semaphore that acts as the write lock. */
|
||||
HANDLE handle = CreateSemaphoreW(NULL, 1, 1, NULL);
|
||||
if (handle == NULL)
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
rwlock->state_.write_semaphore_ = handle;
|
||||
|
||||
/* Initialize the critical section protecting the reader count. */
|
||||
InitializeCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
|
||||
/* Initialize the reader count. */
|
||||
rwlock->state_.num_readers_ = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
|
||||
DeleteCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
CloseHandle(rwlock->state_.write_semaphore_);
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
|
||||
/* Acquire the lock that protects the reader count. */
|
||||
EnterCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
|
||||
/* Increase the reader count, and lock for write if this is the first
|
||||
* reader.
|
||||
*/
|
||||
if (++rwlock->state_.num_readers_ == 1) {
|
||||
DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE);
|
||||
if (r != WAIT_OBJECT_0)
|
||||
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||
}
|
||||
|
||||
/* Release the lock that protects the reader count. */
|
||||
LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
}
|
||||
|
||||
|
||||
int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
|
||||
int err;
|
||||
|
||||
if (!TryEnterCriticalSection(&rwlock->state_.num_readers_lock_))
|
||||
return UV_EBUSY;
|
||||
|
||||
err = 0;
|
||||
|
||||
if (rwlock->state_.num_readers_ == 0) {
|
||||
/* Currently there are no other readers, which means that the write lock
|
||||
* needs to be acquired.
|
||||
*/
|
||||
DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0);
|
||||
if (r == WAIT_OBJECT_0)
|
||||
rwlock->state_.num_readers_++;
|
||||
else if (r == WAIT_TIMEOUT)
|
||||
err = UV_EBUSY;
|
||||
else if (r == WAIT_FAILED)
|
||||
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||
|
||||
} else {
|
||||
/* The write lock has already been acquired because there are other
|
||||
* active readers.
|
||||
*/
|
||||
rwlock->state_.num_readers_++;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
|
||||
EnterCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
|
||||
if (--rwlock->state_.num_readers_ == 0) {
|
||||
if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL))
|
||||
uv_fatal_error(GetLastError(), "ReleaseSemaphore");
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
|
||||
DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE);
|
||||
if (r != WAIT_OBJECT_0)
|
||||
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||
}
|
||||
|
||||
|
||||
int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
|
||||
DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0);
|
||||
if (r == WAIT_OBJECT_0)
|
||||
return 0;
|
||||
else if (r == WAIT_TIMEOUT)
|
||||
return UV_EBUSY;
|
||||
else
|
||||
uv_fatal_error(GetLastError(), "WaitForSingleObject");
|
||||
}
|
||||
|
||||
|
||||
void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
|
||||
if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL))
|
||||
uv_fatal_error(GetLastError(), "ReleaseSemaphore");
|
||||
}
|
||||
|
||||
|
||||
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
|
||||
*sem = CreateSemaphore(NULL, value, INT_MAX, NULL);
|
||||
if (*sem == NULL)
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_destroy(uv_sem_t* sem) {
|
||||
if (!CloseHandle(*sem))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_post(uv_sem_t* sem) {
|
||||
if (!ReleaseSemaphore(*sem, 1, NULL))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_sem_wait(uv_sem_t* sem) {
|
||||
if (WaitForSingleObject(*sem, INFINITE) != WAIT_OBJECT_0)
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
int uv_sem_trywait(uv_sem_t* sem) {
|
||||
DWORD r = WaitForSingleObject(*sem, 0);
|
||||
|
||||
if (r == WAIT_OBJECT_0)
|
||||
return 0;
|
||||
|
||||
if (r == WAIT_TIMEOUT)
|
||||
return UV_EAGAIN;
|
||||
|
||||
abort();
|
||||
return -1; /* Satisfy the compiler. */
|
||||
}
|
||||
|
||||
|
||||
/* This condition variable implementation is based on the SetEvent solution
|
||||
* (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
|
||||
* We could not use the SignalObjectAndWait solution (section 3.4) because
|
||||
* it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and
|
||||
* uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs.
|
||||
*/
|
||||
|
||||
static int uv_cond_fallback_init(uv_cond_t* cond) {
|
||||
int err;
|
||||
|
||||
/* Initialize the count to 0. */
|
||||
cond->fallback.waiters_count = 0;
|
||||
|
||||
InitializeCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
|
||||
/* Create an auto-reset event. */
|
||||
cond->fallback.signal_event = CreateEvent(NULL, /* no security */
|
||||
FALSE, /* auto-reset event */
|
||||
FALSE, /* non-signaled initially */
|
||||
NULL); /* unnamed */
|
||||
if (!cond->fallback.signal_event) {
|
||||
err = GetLastError();
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* Create a manual-reset event. */
|
||||
cond->fallback.broadcast_event = CreateEvent(NULL, /* no security */
|
||||
TRUE, /* manual-reset */
|
||||
FALSE, /* non-signaled */
|
||||
NULL); /* unnamed */
|
||||
if (!cond->fallback.broadcast_event) {
|
||||
err = GetLastError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
CloseHandle(cond->fallback.signal_event);
|
||||
error2:
|
||||
DeleteCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
static int uv_cond_condvar_init(uv_cond_t* cond) {
|
||||
pInitializeConditionVariable(&cond->cond_var);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_cond_init(uv_cond_t* cond) {
|
||||
uv__once_init();
|
||||
|
||||
if (HAVE_CONDVAR_API())
|
||||
return uv_cond_condvar_init(cond);
|
||||
else
|
||||
return uv_cond_fallback_init(cond);
|
||||
}
|
||||
|
||||
|
||||
static void uv_cond_fallback_destroy(uv_cond_t* cond) {
|
||||
if (!CloseHandle(cond->fallback.broadcast_event))
|
||||
abort();
|
||||
if (!CloseHandle(cond->fallback.signal_event))
|
||||
abort();
|
||||
DeleteCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
}
|
||||
|
||||
|
||||
static void uv_cond_condvar_destroy(uv_cond_t* cond) {
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
|
||||
void uv_cond_destroy(uv_cond_t* cond) {
|
||||
if (HAVE_CONDVAR_API())
|
||||
uv_cond_condvar_destroy(cond);
|
||||
else
|
||||
uv_cond_fallback_destroy(cond);
|
||||
}
|
||||
|
||||
|
||||
static void uv_cond_fallback_signal(uv_cond_t* cond) {
|
||||
int have_waiters;
|
||||
|
||||
/* Avoid race conditions. */
|
||||
EnterCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
have_waiters = cond->fallback.waiters_count > 0;
|
||||
LeaveCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
|
||||
if (have_waiters)
|
||||
SetEvent(cond->fallback.signal_event);
|
||||
}
|
||||
|
||||
|
||||
static void uv_cond_condvar_signal(uv_cond_t* cond) {
|
||||
pWakeConditionVariable(&cond->cond_var);
|
||||
}
|
||||
|
||||
|
||||
void uv_cond_signal(uv_cond_t* cond) {
|
||||
if (HAVE_CONDVAR_API())
|
||||
uv_cond_condvar_signal(cond);
|
||||
else
|
||||
uv_cond_fallback_signal(cond);
|
||||
}
|
||||
|
||||
|
||||
static void uv_cond_fallback_broadcast(uv_cond_t* cond) {
|
||||
int have_waiters;
|
||||
|
||||
/* Avoid race conditions. */
|
||||
EnterCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
have_waiters = cond->fallback.waiters_count > 0;
|
||||
LeaveCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
|
||||
if (have_waiters)
|
||||
SetEvent(cond->fallback.broadcast_event);
|
||||
}
|
||||
|
||||
|
||||
static void uv_cond_condvar_broadcast(uv_cond_t* cond) {
|
||||
pWakeAllConditionVariable(&cond->cond_var);
|
||||
}
|
||||
|
||||
|
||||
void uv_cond_broadcast(uv_cond_t* cond) {
|
||||
if (HAVE_CONDVAR_API())
|
||||
uv_cond_condvar_broadcast(cond);
|
||||
else
|
||||
uv_cond_fallback_broadcast(cond);
|
||||
}
|
||||
|
||||
|
||||
static int uv_cond_wait_helper(uv_cond_t* cond, uv_mutex_t* mutex,
|
||||
DWORD dwMilliseconds) {
|
||||
DWORD result;
|
||||
int last_waiter;
|
||||
HANDLE handles[2] = {
|
||||
cond->fallback.signal_event,
|
||||
cond->fallback.broadcast_event
|
||||
};
|
||||
|
||||
/* Avoid race conditions. */
|
||||
EnterCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
cond->fallback.waiters_count++;
|
||||
LeaveCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
|
||||
/* It's ok to release the <mutex> here since Win32 manual-reset events */
|
||||
/* maintain state when used with <SetEvent>. This avoids the "lost wakeup" */
|
||||
/* bug. */
|
||||
uv_mutex_unlock(mutex);
|
||||
|
||||
/* Wait for either event to become signaled due to <uv_cond_signal> being */
|
||||
/* called or <uv_cond_broadcast> being called. */
|
||||
result = WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds);
|
||||
|
||||
EnterCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
cond->fallback.waiters_count--;
|
||||
last_waiter = result == WAIT_OBJECT_0 + 1
|
||||
&& cond->fallback.waiters_count == 0;
|
||||
LeaveCriticalSection(&cond->fallback.waiters_count_lock);
|
||||
|
||||
/* Some thread called <pthread_cond_broadcast>. */
|
||||
if (last_waiter) {
|
||||
/* We're the last waiter to be notified or to stop waiting, so reset the */
|
||||
/* the manual-reset event. */
|
||||
ResetEvent(cond->fallback.broadcast_event);
|
||||
}
|
||||
|
||||
/* Reacquire the <mutex>. */
|
||||
uv_mutex_lock(mutex);
|
||||
|
||||
if (result == WAIT_OBJECT_0 || result == WAIT_OBJECT_0 + 1)
|
||||
return 0;
|
||||
|
||||
if (result == WAIT_TIMEOUT)
|
||||
return UV_ETIMEDOUT;
|
||||
|
||||
abort();
|
||||
return -1; /* Satisfy the compiler. */
|
||||
}
|
||||
|
||||
|
||||
static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
|
||||
if (uv_cond_wait_helper(cond, mutex, INFINITE))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
|
||||
if (!pSleepConditionVariableCS(&cond->cond_var, mutex, INFINITE))
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
|
||||
if (HAVE_CONDVAR_API())
|
||||
uv_cond_condvar_wait(cond, mutex);
|
||||
else
|
||||
uv_cond_fallback_wait(cond, mutex);
|
||||
}
|
||||
|
||||
|
||||
static int uv_cond_fallback_timedwait(uv_cond_t* cond,
|
||||
uv_mutex_t* mutex, uint64_t timeout) {
|
||||
return uv_cond_wait_helper(cond, mutex, (DWORD)(timeout / 1e6));
|
||||
}
|
||||
|
||||
|
||||
static int uv_cond_condvar_timedwait(uv_cond_t* cond,
|
||||
uv_mutex_t* mutex, uint64_t timeout) {
|
||||
if (pSleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6)))
|
||||
return 0;
|
||||
if (GetLastError() != ERROR_TIMEOUT)
|
||||
abort();
|
||||
return UV_ETIMEDOUT;
|
||||
}
|
||||
|
||||
|
||||
int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex,
|
||||
uint64_t timeout) {
|
||||
if (HAVE_CONDVAR_API())
|
||||
return uv_cond_condvar_timedwait(cond, mutex, timeout);
|
||||
else
|
||||
return uv_cond_fallback_timedwait(cond, mutex, timeout);
|
||||
}
|
||||
|
||||
|
||||
int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
|
||||
int err;
|
||||
|
||||
barrier->n = count;
|
||||
barrier->count = 0;
|
||||
|
||||
err = uv_mutex_init(&barrier->mutex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = uv_sem_init(&barrier->turnstile1, 0);
|
||||
if (err)
|
||||
goto error2;
|
||||
|
||||
err = uv_sem_init(&barrier->turnstile2, 1);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uv_sem_destroy(&barrier->turnstile1);
|
||||
error2:
|
||||
uv_mutex_destroy(&barrier->mutex);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void uv_barrier_destroy(uv_barrier_t* barrier) {
|
||||
uv_sem_destroy(&barrier->turnstile2);
|
||||
uv_sem_destroy(&barrier->turnstile1);
|
||||
uv_mutex_destroy(&barrier->mutex);
|
||||
}
|
||||
|
||||
|
||||
int uv_barrier_wait(uv_barrier_t* barrier) {
|
||||
int serial_thread;
|
||||
|
||||
uv_mutex_lock(&barrier->mutex);
|
||||
if (++barrier->count == barrier->n) {
|
||||
uv_sem_wait(&barrier->turnstile2);
|
||||
uv_sem_post(&barrier->turnstile1);
|
||||
}
|
||||
uv_mutex_unlock(&barrier->mutex);
|
||||
|
||||
uv_sem_wait(&barrier->turnstile1);
|
||||
uv_sem_post(&barrier->turnstile1);
|
||||
|
||||
uv_mutex_lock(&barrier->mutex);
|
||||
serial_thread = (--barrier->count == 0);
|
||||
if (serial_thread) {
|
||||
uv_sem_wait(&barrier->turnstile1);
|
||||
uv_sem_post(&barrier->turnstile2);
|
||||
}
|
||||
uv_mutex_unlock(&barrier->mutex);
|
||||
|
||||
uv_sem_wait(&barrier->turnstile2);
|
||||
uv_sem_post(&barrier->turnstile2);
|
||||
return serial_thread;
|
||||
}
|
||||
|
||||
|
||||
int uv_key_create(uv_key_t* key) {
|
||||
key->tls_index = TlsAlloc();
|
||||
if (key->tls_index == TLS_OUT_OF_INDEXES)
|
||||
return UV_ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_key_delete(uv_key_t* key) {
|
||||
if (TlsFree(key->tls_index) == FALSE)
|
||||
abort();
|
||||
key->tls_index = TLS_OUT_OF_INDEXES;
|
||||
}
|
||||
|
||||
|
||||
void* uv_key_get(uv_key_t* key) {
|
||||
void* value;
|
||||
|
||||
value = TlsGetValue(key->tls_index);
|
||||
if (value == NULL)
|
||||
if (GetLastError() != ERROR_SUCCESS)
|
||||
abort();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
void uv_key_set(uv_key_t* key, void* value) {
|
||||
if (TlsSetValue(key->tls_index, value) == FALSE)
|
||||
abort();
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "tree.h"
|
||||
#include "handle-inl.h"
|
||||
|
||||
|
||||
/* The number of milliseconds in one second. */
|
||||
#define UV__MILLISEC 1000
|
||||
|
||||
|
||||
void uv_update_time(uv_loop_t* loop) {
|
||||
uint64_t new_time = uv__hrtime(UV__MILLISEC);
|
||||
assert(new_time >= loop->time);
|
||||
loop->time = new_time;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
/*
|
||||
* compare start_id when both has the same due. start_id is
|
||||
* allocated with loop->timer_counter in uv_timer_start().
|
||||
*/
|
||||
if (a->start_id < b->start_id)
|
||||
return -1;
|
||||
if (a->start_id > b->start_id)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare);
|
||||
|
||||
|
||||
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_TIMER);
|
||||
handle->timer_cb = NULL;
|
||||
handle->repeat = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle) {
|
||||
if (handle->flags & UV__HANDLE_CLOSING) {
|
||||
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||
uv__handle_close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint64_t get_clamped_due_time(uint64_t loop_time, uint64_t timeout) {
|
||||
uint64_t clamped_timeout;
|
||||
|
||||
clamped_timeout = loop_time + timeout;
|
||||
if (clamped_timeout < timeout)
|
||||
clamped_timeout = (uint64_t) -1;
|
||||
|
||||
return clamped_timeout;
|
||||
}
|
||||
|
||||
|
||||
int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, uint64_t timeout,
|
||||
uint64_t repeat) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
uv_timer_t* old;
|
||||
|
||||
if (timer_cb == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (uv__is_active(handle))
|
||||
uv_timer_stop(handle);
|
||||
|
||||
handle->timer_cb = timer_cb;
|
||||
handle->due = get_clamped_due_time(loop->time, timeout);
|
||||
handle->repeat = repeat;
|
||||
uv__handle_start(handle);
|
||||
|
||||
/* start_id is the second index to be compared in uv__timer_cmp() */
|
||||
handle->start_id = handle->loop->timer_counter++;
|
||||
|
||||
old = RB_INSERT(uv_timer_tree_s, &loop->timers, handle);
|
||||
assert(old == NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_timer_stop(uv_timer_t* handle) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
RB_REMOVE(uv_timer_tree_s, &loop->timers, handle);
|
||||
uv__handle_stop(handle);
|
||||
|
||||
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) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (handle->repeat) {
|
||||
uv_timer_stop(handle);
|
||||
uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) {
|
||||
assert(handle->type == UV_TIMER);
|
||||
handle->repeat = repeat;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv_timer_get_repeat(const uv_timer_t* handle) {
|
||||
assert(handle->type == UV_TIMER);
|
||||
return handle->repeat;
|
||||
}
|
||||
|
||||
|
||||
DWORD uv__next_timeout(const uv_loop_t* loop) {
|
||||
uv_timer_t* timer;
|
||||
int64_t delta;
|
||||
|
||||
/* Check if there are any running timers
|
||||
* Need to cast away const first, since RB_MIN doesn't know what we are
|
||||
* going to do with this return value, it can't be marked const
|
||||
*/
|
||||
timer = RB_MIN(uv_timer_tree_s, &((uv_loop_t*)loop)->timers);
|
||||
if (timer) {
|
||||
delta = timer->due - loop->time;
|
||||
if (delta >= UINT_MAX - 1) {
|
||||
/* A timeout value of UINT_MAX means infinite, so that's no good. */
|
||||
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_loop_t* loop) {
|
||||
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)) {
|
||||
|
||||
uv_timer_stop(timer);
|
||||
uv_timer_again(timer);
|
||||
timer->timer_cb((uv_timer_t*) timer);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,926 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "handle-inl.h"
|
||||
#include "stream-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
|
||||
/*
|
||||
* Threshold of active udp streams for which to preallocate udp read buffers.
|
||||
*/
|
||||
const unsigned int uv_active_udp_streams_threshold = 0;
|
||||
|
||||
/* A zero-size buffer for use by uv_udp_read */
|
||||
static char uv_zero_[] = "";
|
||||
|
||||
int uv_udp_getsockname(const uv_udp_t* handle,
|
||||
struct sockaddr* name,
|
||||
int* namelen) {
|
||||
int result;
|
||||
|
||||
if (handle->socket == INVALID_SOCKET) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
result = getsockname(handle->socket, name, namelen);
|
||||
if (result != 0) {
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket,
|
||||
int family) {
|
||||
DWORD yes = 1;
|
||||
WSAPROTOCOL_INFOW info;
|
||||
int opt_len;
|
||||
|
||||
if (handle->socket != INVALID_SOCKET)
|
||||
return UV_EBUSY;
|
||||
|
||||
/* Set the socket to nonblocking mode */
|
||||
if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
/* Make the socket non-inheritable */
|
||||
if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
if (pSetFileCompletionNotificationModes) {
|
||||
/* All known Windows that support SetFileCompletionNotificationModes */
|
||||
/* have a bug that makes it impossible to use this function in */
|
||||
/* conjunction with datagram sockets. We can work around that but only */
|
||||
/* if the user is using the default UDP driver (AFD) and has no other */
|
||||
/* LSPs stacked on top. Here we check whether that is the case. */
|
||||
opt_len = (int) sizeof info;
|
||||
if (getsockopt(socket,
|
||||
SOL_SOCKET,
|
||||
SO_PROTOCOL_INFOW,
|
||||
(char*) &info,
|
||||
&opt_len) == SOCKET_ERROR) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
if (info.ProtocolChain.ChainLen == 1) {
|
||||
if (pSetFileCompletionNotificationModes((HANDLE)socket,
|
||||
FILE_SKIP_SET_EVENT_ON_HANDLE |
|
||||
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
|
||||
handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
|
||||
handle->func_wsarecv = uv_wsarecv_workaround;
|
||||
handle->func_wsarecvfrom = uv_wsarecvfrom_workaround;
|
||||
} else if (GetLastError() != ERROR_INVALID_FUNCTION) {
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle->socket = socket;
|
||||
|
||||
if (family == AF_INET6) {
|
||||
handle->flags |= UV_HANDLE_IPV6;
|
||||
} else {
|
||||
assert(!(handle->flags & UV_HANDLE_IPV6));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
|
||||
int domain;
|
||||
|
||||
/* Use the lower 8 bits for the domain */
|
||||
domain = flags & 0xFF;
|
||||
if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (flags & ~0xFF)
|
||||
return UV_EINVAL;
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_UDP);
|
||||
handle->socket = INVALID_SOCKET;
|
||||
handle->reqs_pending = 0;
|
||||
handle->activecnt = 0;
|
||||
handle->func_wsarecv = WSARecv;
|
||||
handle->func_wsarecvfrom = WSARecvFrom;
|
||||
handle->send_queue_size = 0;
|
||||
handle->send_queue_count = 0;
|
||||
uv_req_init(loop, (uv_req_t*) &(handle->recv_req));
|
||||
handle->recv_req.type = UV_UDP_RECV;
|
||||
handle->recv_req.data = handle;
|
||||
|
||||
/* If anything fails beyond this point we need to remove the handle from
|
||||
* the handle queue, since it was added by uv__handle_init.
|
||||
*/
|
||||
|
||||
if (domain != AF_UNSPEC) {
|
||||
SOCKET sock;
|
||||
DWORD err;
|
||||
|
||||
sock = socket(domain, SOCK_DGRAM, 0);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
err = WSAGetLastError();
|
||||
QUEUE_REMOVE(&handle->handle_queue);
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
err = uv_udp_set_socket(handle->loop, handle, sock, domain);
|
||||
if (err) {
|
||||
closesocket(sock);
|
||||
QUEUE_REMOVE(&handle->handle_queue);
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) {
|
||||
return uv_udp_init_ex(loop, handle, AF_UNSPEC);
|
||||
}
|
||||
|
||||
|
||||
void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) {
|
||||
uv_udp_recv_stop(handle);
|
||||
closesocket(handle->socket);
|
||||
handle->socket = INVALID_SOCKET;
|
||||
|
||||
uv__handle_closing(handle);
|
||||
|
||||
if (handle->reqs_pending == 0) {
|
||||
uv_want_endgame(loop, (uv_handle_t*) handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) {
|
||||
if (handle->flags & UV__HANDLE_CLOSING &&
|
||||
handle->reqs_pending == 0) {
|
||||
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||
uv__handle_close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv_udp_maybe_bind(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
unsigned int flags) {
|
||||
int r;
|
||||
int err;
|
||||
DWORD no = 0;
|
||||
|
||||
if (handle->flags & UV_HANDLE_BOUND)
|
||||
return 0;
|
||||
|
||||
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
|
||||
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (handle->socket == INVALID_SOCKET) {
|
||||
SOCKET sock = socket(addr->sa_family, SOCK_DGRAM, 0);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
err = uv_udp_set_socket(handle->loop, handle, sock, addr->sa_family);
|
||||
if (err) {
|
||||
closesocket(sock);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & UV_UDP_REUSEADDR) {
|
||||
DWORD yes = 1;
|
||||
/* Set SO_REUSEADDR on the socket. */
|
||||
if (setsockopt(handle->socket,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
(char*) &yes,
|
||||
sizeof yes) == SOCKET_ERROR) {
|
||||
err = WSAGetLastError();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr->sa_family == AF_INET6)
|
||||
handle->flags |= UV_HANDLE_IPV6;
|
||||
|
||||
if (addr->sa_family == AF_INET6 && !(flags & UV_UDP_IPV6ONLY)) {
|
||||
/* On windows IPV6ONLY is on by default. */
|
||||
/* If the user doesn't specify it libuv turns it off. */
|
||||
|
||||
/* TODO: how to handle errors? This may fail if there is no ipv4 stack */
|
||||
/* available, or when run on XP/2003 which have no support for dualstack */
|
||||
/* sockets. For now we're silently ignoring the error. */
|
||||
setsockopt(handle->socket,
|
||||
IPPROTO_IPV6,
|
||||
IPV6_V6ONLY,
|
||||
(char*) &no,
|
||||
sizeof no);
|
||||
}
|
||||
|
||||
r = bind(handle->socket, addr, addrlen);
|
||||
if (r == SOCKET_ERROR) {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
handle->flags |= UV_HANDLE_BOUND;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
|
||||
uv_req_t* req;
|
||||
uv_buf_t buf;
|
||||
DWORD bytes, flags;
|
||||
int result;
|
||||
|
||||
assert(handle->flags & UV_HANDLE_READING);
|
||||
assert(!(handle->flags & UV_HANDLE_READ_PENDING));
|
||||
|
||||
req = &handle->recv_req;
|
||||
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
|
||||
|
||||
/*
|
||||
* Preallocate a read buffer if the number of active streams is below
|
||||
* the threshold.
|
||||
*/
|
||||
if (loop->active_udp_streams < uv_active_udp_streams_threshold) {
|
||||
handle->flags &= ~UV_HANDLE_ZERO_READ;
|
||||
|
||||
handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->recv_buffer);
|
||||
if (handle->recv_buffer.len == 0) {
|
||||
handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0);
|
||||
return;
|
||||
}
|
||||
assert(handle->recv_buffer.base != NULL);
|
||||
|
||||
buf = handle->recv_buffer;
|
||||
memset(&handle->recv_from, 0, sizeof handle->recv_from);
|
||||
handle->recv_from_len = sizeof handle->recv_from;
|
||||
flags = 0;
|
||||
|
||||
result = handle->func_wsarecvfrom(handle->socket,
|
||||
(WSABUF*) &buf,
|
||||
1,
|
||||
&bytes,
|
||||
&flags,
|
||||
(struct sockaddr*) &handle->recv_from,
|
||||
&handle->recv_from_len,
|
||||
&req->u.io.overlapped,
|
||||
NULL);
|
||||
|
||||
if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
|
||||
/* Process the req without IOCP. */
|
||||
handle->flags |= UV_HANDLE_READ_PENDING;
|
||||
req->u.io.overlapped.InternalHigh = bytes;
|
||||
handle->reqs_pending++;
|
||||
uv_insert_pending_req(loop, req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||
/* The req will be processed with IOCP. */
|
||||
handle->flags |= UV_HANDLE_READ_PENDING;
|
||||
handle->reqs_pending++;
|
||||
} else {
|
||||
/* Make this req pending reporting an error. */
|
||||
SET_REQ_ERROR(req, WSAGetLastError());
|
||||
uv_insert_pending_req(loop, req);
|
||||
handle->reqs_pending++;
|
||||
}
|
||||
|
||||
} else {
|
||||
handle->flags |= UV_HANDLE_ZERO_READ;
|
||||
|
||||
buf.base = (char*) uv_zero_;
|
||||
buf.len = 0;
|
||||
flags = MSG_PEEK;
|
||||
|
||||
result = handle->func_wsarecv(handle->socket,
|
||||
(WSABUF*) &buf,
|
||||
1,
|
||||
&bytes,
|
||||
&flags,
|
||||
&req->u.io.overlapped,
|
||||
NULL);
|
||||
|
||||
if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
|
||||
/* Process the req without IOCP. */
|
||||
handle->flags |= UV_HANDLE_READ_PENDING;
|
||||
req->u.io.overlapped.InternalHigh = bytes;
|
||||
handle->reqs_pending++;
|
||||
uv_insert_pending_req(loop, req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||
/* The req will be processed with IOCP. */
|
||||
handle->flags |= UV_HANDLE_READ_PENDING;
|
||||
handle->reqs_pending++;
|
||||
} else {
|
||||
/* Make this req pending reporting an error. */
|
||||
SET_REQ_ERROR(req, WSAGetLastError());
|
||||
uv_insert_pending_req(loop, req);
|
||||
handle->reqs_pending++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb,
|
||||
uv_udp_recv_cb recv_cb) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
int err;
|
||||
|
||||
if (handle->flags & UV_HANDLE_READING) {
|
||||
return WSAEALREADY;
|
||||
}
|
||||
|
||||
err = uv_udp_maybe_bind(handle,
|
||||
(const struct sockaddr*) &uv_addr_ip4_any_,
|
||||
sizeof(uv_addr_ip4_any_),
|
||||
0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
handle->flags |= UV_HANDLE_READING;
|
||||
INCREASE_ACTIVE_COUNT(loop, handle);
|
||||
loop->active_udp_streams++;
|
||||
|
||||
handle->recv_cb = recv_cb;
|
||||
handle->alloc_cb = alloc_cb;
|
||||
|
||||
/* If reading was stopped and then started again, there could still be a */
|
||||
/* recv request pending. */
|
||||
if (!(handle->flags & UV_HANDLE_READ_PENDING))
|
||||
uv_udp_queue_recv(loop, handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_recv_stop(uv_udp_t* handle) {
|
||||
if (handle->flags & UV_HANDLE_READING) {
|
||||
handle->flags &= ~UV_HANDLE_READING;
|
||||
handle->loop->active_udp_streams--;
|
||||
DECREASE_ACTIVE_COUNT(loop, handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
uv_udp_send_cb cb) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
DWORD result, bytes;
|
||||
|
||||
uv_req_init(loop, (uv_req_t*) req);
|
||||
req->type = UV_UDP_SEND;
|
||||
req->handle = handle;
|
||||
req->cb = cb;
|
||||
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
|
||||
|
||||
result = WSASendTo(handle->socket,
|
||||
(WSABUF*)bufs,
|
||||
nbufs,
|
||||
&bytes,
|
||||
0,
|
||||
addr,
|
||||
addrlen,
|
||||
&req->u.io.overlapped,
|
||||
NULL);
|
||||
|
||||
if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
|
||||
/* Request completed immediately. */
|
||||
req->u.io.queued_bytes = 0;
|
||||
handle->reqs_pending++;
|
||||
handle->send_queue_size += req->u.io.queued_bytes;
|
||||
handle->send_queue_count++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
uv_insert_pending_req(loop, (uv_req_t*)req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||
/* Request queued by the kernel. */
|
||||
req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
|
||||
handle->reqs_pending++;
|
||||
handle->send_queue_size += req->u.io.queued_bytes;
|
||||
handle->send_queue_count++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
} else {
|
||||
/* Send failed due to an error. */
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
|
||||
uv_req_t* req) {
|
||||
uv_buf_t buf;
|
||||
int partial;
|
||||
|
||||
assert(handle->type == UV_UDP);
|
||||
|
||||
handle->flags &= ~UV_HANDLE_READ_PENDING;
|
||||
|
||||
if (!REQ_SUCCESS(req)) {
|
||||
DWORD err = GET_REQ_SOCK_ERROR(req);
|
||||
if (err == WSAEMSGSIZE) {
|
||||
/* Not a real error, it just indicates that the received packet */
|
||||
/* was bigger than the receive buffer. */
|
||||
} else if (err == WSAECONNRESET || err == WSAENETRESET) {
|
||||
/* A previous sendto operation failed; ignore this error. If */
|
||||
/* zero-reading we need to call WSARecv/WSARecvFrom _without_ the */
|
||||
/* MSG_PEEK flag to clear out the error queue. For nonzero reads, */
|
||||
/* immediately queue a new receive. */
|
||||
if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
/* A real error occurred. Report the error to the user only if we're */
|
||||
/* currently reading. */
|
||||
if (handle->flags & UV_HANDLE_READING) {
|
||||
uv_udp_recv_stop(handle);
|
||||
buf = (handle->flags & UV_HANDLE_ZERO_READ) ?
|
||||
uv_buf_init(NULL, 0) : handle->recv_buffer;
|
||||
handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
|
||||
/* Successful read */
|
||||
partial = !REQ_SUCCESS(req);
|
||||
handle->recv_cb(handle,
|
||||
req->u.io.overlapped.InternalHigh,
|
||||
&handle->recv_buffer,
|
||||
(const struct sockaddr*) &handle->recv_from,
|
||||
partial ? UV_UDP_PARTIAL : 0);
|
||||
} else if (handle->flags & UV_HANDLE_READING) {
|
||||
DWORD bytes, err, flags;
|
||||
struct sockaddr_storage from;
|
||||
int from_len;
|
||||
|
||||
/* Do a nonblocking receive */
|
||||
/* TODO: try to read multiple datagrams at once. FIONREAD maybe? */
|
||||
handle->alloc_cb((uv_handle_t*) handle, 65536, &buf);
|
||||
if (buf.len == 0) {
|
||||
handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
|
||||
goto done;
|
||||
}
|
||||
assert(buf.base != NULL);
|
||||
|
||||
memset(&from, 0, sizeof from);
|
||||
from_len = sizeof from;
|
||||
|
||||
flags = 0;
|
||||
|
||||
if (WSARecvFrom(handle->socket,
|
||||
(WSABUF*)&buf,
|
||||
1,
|
||||
&bytes,
|
||||
&flags,
|
||||
(struct sockaddr*) &from,
|
||||
&from_len,
|
||||
NULL,
|
||||
NULL) != SOCKET_ERROR) {
|
||||
|
||||
/* Message received */
|
||||
handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0);
|
||||
} else {
|
||||
err = WSAGetLastError();
|
||||
if (err == WSAEMSGSIZE) {
|
||||
/* Message truncated */
|
||||
handle->recv_cb(handle,
|
||||
bytes,
|
||||
&buf,
|
||||
(const struct sockaddr*) &from,
|
||||
UV_UDP_PARTIAL);
|
||||
} else if (err == WSAEWOULDBLOCK) {
|
||||
/* Kernel buffer empty */
|
||||
handle->recv_cb(handle, 0, &buf, NULL, 0);
|
||||
} else if (err == WSAECONNRESET || err == WSAENETRESET) {
|
||||
/* WSAECONNRESET/WSANETRESET is ignored because this just indicates
|
||||
* that a previous sendto operation failed.
|
||||
*/
|
||||
handle->recv_cb(handle, 0, &buf, NULL, 0);
|
||||
} else {
|
||||
/* Any other error that we want to report back to the user. */
|
||||
uv_udp_recv_stop(handle);
|
||||
handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
/* Post another read if still reading and not closing. */
|
||||
if ((handle->flags & UV_HANDLE_READING) &&
|
||||
!(handle->flags & UV_HANDLE_READ_PENDING)) {
|
||||
uv_udp_queue_recv(loop, handle);
|
||||
}
|
||||
|
||||
DECREASE_PENDING_REQ_COUNT(handle);
|
||||
}
|
||||
|
||||
|
||||
void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
|
||||
uv_udp_send_t* req) {
|
||||
int err;
|
||||
|
||||
assert(handle->type == UV_UDP);
|
||||
|
||||
assert(handle->send_queue_size >= req->u.io.queued_bytes);
|
||||
assert(handle->send_queue_count >= 1);
|
||||
handle->send_queue_size -= req->u.io.queued_bytes;
|
||||
handle->send_queue_count--;
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
|
||||
if (req->cb) {
|
||||
err = 0;
|
||||
if (!REQ_SUCCESS(req)) {
|
||||
err = GET_REQ_SOCK_ERROR(req);
|
||||
}
|
||||
req->cb(req, uv_translate_sys_error(err));
|
||||
}
|
||||
|
||||
DECREASE_PENDING_REQ_COUNT(handle);
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_set_membership4(uv_udp_t* handle,
|
||||
const struct sockaddr_in* multicast_addr,
|
||||
const char* interface_addr,
|
||||
uv_membership membership) {
|
||||
int err;
|
||||
int optname;
|
||||
struct ip_mreq mreq;
|
||||
|
||||
if (handle->flags & UV_HANDLE_IPV6)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* If the socket is unbound, bind to inaddr_any. */
|
||||
err = uv_udp_maybe_bind(handle,
|
||||
(const struct sockaddr*) &uv_addr_ip4_any_,
|
||||
sizeof(uv_addr_ip4_any_),
|
||||
UV_UDP_REUSEADDR);
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
|
||||
memset(&mreq, 0, sizeof mreq);
|
||||
|
||||
if (interface_addr) {
|
||||
err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
|
||||
mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
|
||||
|
||||
switch (membership) {
|
||||
case UV_JOIN_GROUP:
|
||||
optname = IP_ADD_MEMBERSHIP;
|
||||
break;
|
||||
case UV_LEAVE_GROUP:
|
||||
optname = IP_DROP_MEMBERSHIP;
|
||||
break;
|
||||
default:
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (setsockopt(handle->socket,
|
||||
IPPROTO_IP,
|
||||
optname,
|
||||
(char*) &mreq,
|
||||
sizeof mreq) == SOCKET_ERROR) {
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_set_membership6(uv_udp_t* handle,
|
||||
const struct sockaddr_in6* multicast_addr,
|
||||
const char* interface_addr,
|
||||
uv_membership membership) {
|
||||
int optname;
|
||||
int err;
|
||||
struct ipv6_mreq mreq;
|
||||
struct sockaddr_in6 addr6;
|
||||
|
||||
if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
|
||||
return UV_EINVAL;
|
||||
|
||||
err = uv_udp_maybe_bind(handle,
|
||||
(const struct sockaddr*) &uv_addr_ip6_any_,
|
||||
sizeof(uv_addr_ip6_any_),
|
||||
UV_UDP_REUSEADDR);
|
||||
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
|
||||
memset(&mreq, 0, sizeof(mreq));
|
||||
|
||||
if (interface_addr) {
|
||||
if (uv_ip6_addr(interface_addr, 0, &addr6))
|
||||
return UV_EINVAL;
|
||||
mreq.ipv6mr_interface = addr6.sin6_scope_id;
|
||||
} else {
|
||||
mreq.ipv6mr_interface = 0;
|
||||
}
|
||||
|
||||
mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr;
|
||||
|
||||
switch (membership) {
|
||||
case UV_JOIN_GROUP:
|
||||
optname = IPV6_ADD_MEMBERSHIP;
|
||||
break;
|
||||
case UV_LEAVE_GROUP:
|
||||
optname = IPV6_DROP_MEMBERSHIP;
|
||||
break;
|
||||
default:
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (setsockopt(handle->socket,
|
||||
IPPROTO_IPV6,
|
||||
optname,
|
||||
(char*) &mreq,
|
||||
sizeof mreq) == SOCKET_ERROR) {
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_set_membership(uv_udp_t* handle,
|
||||
const char* multicast_addr,
|
||||
const char* interface_addr,
|
||||
uv_membership membership) {
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
|
||||
if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0)
|
||||
return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
|
||||
else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0)
|
||||
return uv__udp_set_membership6(handle, &addr6, interface_addr, membership);
|
||||
else
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) {
|
||||
struct sockaddr_storage addr_st;
|
||||
struct sockaddr_in* addr4;
|
||||
struct sockaddr_in6* addr6;
|
||||
|
||||
addr4 = (struct sockaddr_in*) &addr_st;
|
||||
addr6 = (struct sockaddr_in6*) &addr_st;
|
||||
|
||||
if (!interface_addr) {
|
||||
memset(&addr_st, 0, sizeof addr_st);
|
||||
if (handle->flags & UV_HANDLE_IPV6) {
|
||||
addr_st.ss_family = AF_INET6;
|
||||
addr6->sin6_scope_id = 0;
|
||||
} else {
|
||||
addr_st.ss_family = AF_INET;
|
||||
addr4->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
} else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) {
|
||||
/* nothing, address was parsed */
|
||||
} else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) {
|
||||
/* nothing, address was parsed */
|
||||
} else {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_BOUND))
|
||||
return UV_EBADF;
|
||||
|
||||
if (addr_st.ss_family == AF_INET) {
|
||||
if (setsockopt(handle->socket,
|
||||
IPPROTO_IP,
|
||||
IP_MULTICAST_IF,
|
||||
(char*) &addr4->sin_addr,
|
||||
sizeof(addr4->sin_addr)) == SOCKET_ERROR) {
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
}
|
||||
} else if (addr_st.ss_family == AF_INET6) {
|
||||
if (setsockopt(handle->socket,
|
||||
IPPROTO_IPV6,
|
||||
IPV6_MULTICAST_IF,
|
||||
(char*) &addr6->sin6_scope_id,
|
||||
sizeof(addr6->sin6_scope_id)) == SOCKET_ERROR) {
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
}
|
||||
} else {
|
||||
assert(0 && "unexpected address family");
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
|
||||
BOOL optval = (BOOL) value;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_BOUND))
|
||||
return UV_EBADF;
|
||||
|
||||
if (setsockopt(handle->socket,
|
||||
SOL_SOCKET,
|
||||
SO_BROADCAST,
|
||||
(char*) &optval,
|
||||
sizeof optval)) {
|
||||
return uv_translate_sys_error(WSAGetLastError());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
|
||||
WSAPROTOCOL_INFOW protocol_info;
|
||||
int opt_len;
|
||||
int err;
|
||||
|
||||
/* Detect the address family of the socket. */
|
||||
opt_len = (int) sizeof protocol_info;
|
||||
if (getsockopt(sock,
|
||||
SOL_SOCKET,
|
||||
SO_PROTOCOL_INFOW,
|
||||
(char*) &protocol_info,
|
||||
&opt_len) == SOCKET_ERROR) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
err = uv_udp_set_socket(handle->loop,
|
||||
handle,
|
||||
sock,
|
||||
protocol_info.iAddressFamily);
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
#define SOCKOPT_SETTER(name, option4, option6, validate) \
|
||||
int uv_udp_set_##name(uv_udp_t* handle, int value) { \
|
||||
DWORD optval = (DWORD) value; \
|
||||
\
|
||||
if (!(validate(value))) { \
|
||||
return UV_EINVAL; \
|
||||
} \
|
||||
\
|
||||
if (!(handle->flags & UV_HANDLE_BOUND)) \
|
||||
return UV_EBADF; \
|
||||
\
|
||||
if (!(handle->flags & UV_HANDLE_IPV6)) { \
|
||||
/* Set IPv4 socket option */ \
|
||||
if (setsockopt(handle->socket, \
|
||||
IPPROTO_IP, \
|
||||
option4, \
|
||||
(char*) &optval, \
|
||||
sizeof optval)) { \
|
||||
return uv_translate_sys_error(WSAGetLastError()); \
|
||||
} \
|
||||
} else { \
|
||||
/* Set IPv6 socket option */ \
|
||||
if (setsockopt(handle->socket, \
|
||||
IPPROTO_IPV6, \
|
||||
option6, \
|
||||
(char*) &optval, \
|
||||
sizeof optval)) { \
|
||||
return uv_translate_sys_error(WSAGetLastError()); \
|
||||
} \
|
||||
} \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#define VALIDATE_TTL(value) ((value) >= 1 && (value) <= 255)
|
||||
#define VALIDATE_MULTICAST_TTL(value) ((value) >= -1 && (value) <= 255)
|
||||
#define VALIDATE_MULTICAST_LOOP(value) (1)
|
||||
|
||||
SOCKOPT_SETTER(ttl,
|
||||
IP_TTL,
|
||||
IPV6_HOPLIMIT,
|
||||
VALIDATE_TTL)
|
||||
SOCKOPT_SETTER(multicast_ttl,
|
||||
IP_MULTICAST_TTL,
|
||||
IPV6_MULTICAST_HOPS,
|
||||
VALIDATE_MULTICAST_TTL)
|
||||
SOCKOPT_SETTER(multicast_loop,
|
||||
IP_MULTICAST_LOOP,
|
||||
IPV6_MULTICAST_LOOP,
|
||||
VALIDATE_MULTICAST_LOOP)
|
||||
|
||||
#undef SOCKOPT_SETTER
|
||||
#undef VALIDATE_TTL
|
||||
#undef VALIDATE_MULTICAST_TTL
|
||||
#undef VALIDATE_MULTICAST_LOOP
|
||||
|
||||
|
||||
/* This function is an egress point, i.e. it returns libuv errors rather than
|
||||
* system errors.
|
||||
*/
|
||||
int uv__udp_bind(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
unsigned int flags) {
|
||||
int err;
|
||||
|
||||
err = uv_udp_maybe_bind(handle, addr, addrlen, flags);
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* This function is an egress point, i.e. it returns libuv errors rather than
|
||||
* system errors.
|
||||
*/
|
||||
int uv__udp_send(uv_udp_send_t* req,
|
||||
uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen,
|
||||
uv_udp_send_cb send_cb) {
|
||||
const struct sockaddr* bind_addr;
|
||||
int err;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_BOUND)) {
|
||||
if (addrlen == sizeof(uv_addr_ip4_any_)) {
|
||||
bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
|
||||
} else if (addrlen == sizeof(uv_addr_ip6_any_)) {
|
||||
bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
}
|
||||
|
||||
err = uv__send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
|
||||
if (err)
|
||||
return uv_translate_sys_error(err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_try_send(uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen) {
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,146 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
/* Ntdll function pointers */
|
||||
sRtlNtStatusToDosError pRtlNtStatusToDosError;
|
||||
sNtDeviceIoControlFile pNtDeviceIoControlFile;
|
||||
sNtQueryInformationFile pNtQueryInformationFile;
|
||||
sNtSetInformationFile pNtSetInformationFile;
|
||||
sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
|
||||
sNtQueryDirectoryFile pNtQueryDirectoryFile;
|
||||
sNtQuerySystemInformation pNtQuerySystemInformation;
|
||||
|
||||
|
||||
/* Kernel32 function pointers */
|
||||
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
||||
sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
|
||||
sCreateSymbolicLinkW pCreateSymbolicLinkW;
|
||||
sCancelIoEx pCancelIoEx;
|
||||
sInitializeConditionVariable pInitializeConditionVariable;
|
||||
sSleepConditionVariableCS pSleepConditionVariableCS;
|
||||
sSleepConditionVariableSRW pSleepConditionVariableSRW;
|
||||
sWakeAllConditionVariable pWakeAllConditionVariable;
|
||||
sWakeConditionVariable pWakeConditionVariable;
|
||||
sCancelSynchronousIo pCancelSynchronousIo;
|
||||
sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW;
|
||||
|
||||
|
||||
void uv_winapi_init() {
|
||||
HMODULE ntdll_module;
|
||||
HMODULE kernel32_module;
|
||||
|
||||
ntdll_module = GetModuleHandleA("ntdll.dll");
|
||||
if (ntdll_module == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetModuleHandleA");
|
||||
}
|
||||
|
||||
pRtlNtStatusToDosError = (sRtlNtStatusToDosError) GetProcAddress(
|
||||
ntdll_module,
|
||||
"RtlNtStatusToDosError");
|
||||
if (pRtlNtStatusToDosError == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
pNtDeviceIoControlFile = (sNtDeviceIoControlFile) GetProcAddress(
|
||||
ntdll_module,
|
||||
"NtDeviceIoControlFile");
|
||||
if (pNtDeviceIoControlFile == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
pNtQueryInformationFile = (sNtQueryInformationFile) GetProcAddress(
|
||||
ntdll_module,
|
||||
"NtQueryInformationFile");
|
||||
if (pNtQueryInformationFile == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
pNtSetInformationFile = (sNtSetInformationFile) GetProcAddress(
|
||||
ntdll_module,
|
||||
"NtSetInformationFile");
|
||||
if (pNtSetInformationFile == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
pNtQueryVolumeInformationFile = (sNtQueryVolumeInformationFile)
|
||||
GetProcAddress(ntdll_module, "NtQueryVolumeInformationFile");
|
||||
if (pNtQueryVolumeInformationFile == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
pNtQueryDirectoryFile = (sNtQueryDirectoryFile)
|
||||
GetProcAddress(ntdll_module, "NtQueryDirectoryFile");
|
||||
if (pNtQueryVolumeInformationFile == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
pNtQuerySystemInformation = (sNtQuerySystemInformation) GetProcAddress(
|
||||
ntdll_module,
|
||||
"NtQuerySystemInformation");
|
||||
if (pNtQuerySystemInformation == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
kernel32_module = GetModuleHandleA("kernel32.dll");
|
||||
if (kernel32_module == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetModuleHandleA");
|
||||
}
|
||||
|
||||
pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress(
|
||||
kernel32_module,
|
||||
"GetQueuedCompletionStatusEx");
|
||||
|
||||
pSetFileCompletionNotificationModes = (sSetFileCompletionNotificationModes)
|
||||
GetProcAddress(kernel32_module, "SetFileCompletionNotificationModes");
|
||||
|
||||
pCreateSymbolicLinkW = (sCreateSymbolicLinkW)
|
||||
GetProcAddress(kernel32_module, "CreateSymbolicLinkW");
|
||||
|
||||
pCancelIoEx = (sCancelIoEx)
|
||||
GetProcAddress(kernel32_module, "CancelIoEx");
|
||||
|
||||
pInitializeConditionVariable = (sInitializeConditionVariable)
|
||||
GetProcAddress(kernel32_module, "InitializeConditionVariable");
|
||||
|
||||
pSleepConditionVariableCS = (sSleepConditionVariableCS)
|
||||
GetProcAddress(kernel32_module, "SleepConditionVariableCS");
|
||||
|
||||
pSleepConditionVariableSRW = (sSleepConditionVariableSRW)
|
||||
GetProcAddress(kernel32_module, "SleepConditionVariableSRW");
|
||||
|
||||
pWakeAllConditionVariable = (sWakeAllConditionVariable)
|
||||
GetProcAddress(kernel32_module, "WakeAllConditionVariable");
|
||||
|
||||
pWakeConditionVariable = (sWakeConditionVariable)
|
||||
GetProcAddress(kernel32_module, "WakeConditionVariable");
|
||||
|
||||
pCancelSynchronousIo = (sCancelSynchronousIo)
|
||||
GetProcAddress(kernel32_module, "CancelSynchronousIo");
|
||||
|
||||
pGetFinalPathNameByHandleW = (sGetFinalPathNameByHandleW)
|
||||
GetProcAddress(kernel32_module, "GetFinalPathNameByHandleW");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,561 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
/* Whether there are any non-IFS LSPs stacked on TCP */
|
||||
int uv_tcp_non_ifs_lsp_ipv4;
|
||||
int uv_tcp_non_ifs_lsp_ipv6;
|
||||
|
||||
/* Ip address used to bind to any port at any interface */
|
||||
struct sockaddr_in uv_addr_ip4_any_;
|
||||
struct sockaddr_in6 uv_addr_ip6_any_;
|
||||
|
||||
|
||||
/*
|
||||
* Retrieves the pointer to a winsock extension function.
|
||||
*/
|
||||
static BOOL uv_get_extension_function(SOCKET socket, GUID guid,
|
||||
void **target) {
|
||||
int result;
|
||||
DWORD 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) {
|
||||
const GUID wsaid_acceptex = WSAID_ACCEPTEX;
|
||||
return uv_get_extension_function(socket, wsaid_acceptex, (void**)target);
|
||||
}
|
||||
|
||||
|
||||
BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) {
|
||||
const GUID wsaid_connectex = WSAID_CONNECTEX;
|
||||
return uv_get_extension_function(socket, wsaid_connectex, (void**)target);
|
||||
}
|
||||
|
||||
|
||||
static int error_means_no_support(DWORD error) {
|
||||
return error == WSAEPROTONOSUPPORT || error == WSAESOCKTNOSUPPORT ||
|
||||
error == WSAEPFNOSUPPORT || error == WSAEAFNOSUPPORT;
|
||||
}
|
||||
|
||||
|
||||
void uv_winsock_init() {
|
||||
WSADATA wsa_data;
|
||||
int errorno;
|
||||
SOCKET dummy;
|
||||
WSAPROTOCOL_INFOW protocol_info;
|
||||
int opt_len;
|
||||
|
||||
/* Initialize winsock */
|
||||
errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
if (errorno != 0) {
|
||||
uv_fatal_error(errorno, "WSAStartup");
|
||||
}
|
||||
|
||||
/* Set implicit binding address used by connectEx */
|
||||
if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
if (uv_ip6_addr("::", 0, &uv_addr_ip6_any_)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Detect non-IFS LSPs */
|
||||
dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
|
||||
if (dummy != INVALID_SOCKET) {
|
||||
opt_len = (int) sizeof protocol_info;
|
||||
if (getsockopt(dummy,
|
||||
SOL_SOCKET,
|
||||
SO_PROTOCOL_INFOW,
|
||||
(char*) &protocol_info,
|
||||
&opt_len) == SOCKET_ERROR)
|
||||
uv_fatal_error(WSAGetLastError(), "getsockopt");
|
||||
|
||||
if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES))
|
||||
uv_tcp_non_ifs_lsp_ipv4 = 1;
|
||||
|
||||
if (closesocket(dummy) == SOCKET_ERROR)
|
||||
uv_fatal_error(WSAGetLastError(), "closesocket");
|
||||
|
||||
} else if (!error_means_no_support(WSAGetLastError())) {
|
||||
/* Any error other than "socket type not supported" is fatal. */
|
||||
uv_fatal_error(WSAGetLastError(), "socket");
|
||||
}
|
||||
|
||||
/* Detect IPV6 support and non-IFS LSPs */
|
||||
dummy = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP);
|
||||
|
||||
if (dummy != INVALID_SOCKET) {
|
||||
opt_len = (int) sizeof protocol_info;
|
||||
if (getsockopt(dummy,
|
||||
SOL_SOCKET,
|
||||
SO_PROTOCOL_INFOW,
|
||||
(char*) &protocol_info,
|
||||
&opt_len) == SOCKET_ERROR)
|
||||
uv_fatal_error(WSAGetLastError(), "getsockopt");
|
||||
|
||||
if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES))
|
||||
uv_tcp_non_ifs_lsp_ipv6 = 1;
|
||||
|
||||
if (closesocket(dummy) == SOCKET_ERROR)
|
||||
uv_fatal_error(WSAGetLastError(), "closesocket");
|
||||
|
||||
} else if (!error_means_no_support(WSAGetLastError())) {
|
||||
/* Any error other than "socket type not supported" is fatal. */
|
||||
uv_fatal_error(WSAGetLastError(), "socket");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_ntstatus_to_winsock_error(NTSTATUS status) {
|
||||
switch (status) {
|
||||
case STATUS_SUCCESS:
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case STATUS_PENDING:
|
||||
return ERROR_IO_PENDING;
|
||||
|
||||
case STATUS_INVALID_HANDLE:
|
||||
case STATUS_OBJECT_TYPE_MISMATCH:
|
||||
return WSAENOTSOCK;
|
||||
|
||||
case STATUS_INSUFFICIENT_RESOURCES:
|
||||
case STATUS_PAGEFILE_QUOTA:
|
||||
case STATUS_COMMITMENT_LIMIT:
|
||||
case STATUS_WORKING_SET_QUOTA:
|
||||
case STATUS_NO_MEMORY:
|
||||
case STATUS_QUOTA_EXCEEDED:
|
||||
case STATUS_TOO_MANY_PAGING_FILES:
|
||||
case STATUS_REMOTE_RESOURCES:
|
||||
return WSAENOBUFS;
|
||||
|
||||
case STATUS_TOO_MANY_ADDRESSES:
|
||||
case STATUS_SHARING_VIOLATION:
|
||||
case STATUS_ADDRESS_ALREADY_EXISTS:
|
||||
return WSAEADDRINUSE;
|
||||
|
||||
case STATUS_LINK_TIMEOUT:
|
||||
case STATUS_IO_TIMEOUT:
|
||||
case STATUS_TIMEOUT:
|
||||
return WSAETIMEDOUT;
|
||||
|
||||
case STATUS_GRACEFUL_DISCONNECT:
|
||||
return WSAEDISCON;
|
||||
|
||||
case STATUS_REMOTE_DISCONNECT:
|
||||
case STATUS_CONNECTION_RESET:
|
||||
case STATUS_LINK_FAILED:
|
||||
case STATUS_CONNECTION_DISCONNECTED:
|
||||
case STATUS_PORT_UNREACHABLE:
|
||||
case STATUS_HOPLIMIT_EXCEEDED:
|
||||
return WSAECONNRESET;
|
||||
|
||||
case STATUS_LOCAL_DISCONNECT:
|
||||
case STATUS_TRANSACTION_ABORTED:
|
||||
case STATUS_CONNECTION_ABORTED:
|
||||
return WSAECONNABORTED;
|
||||
|
||||
case STATUS_BAD_NETWORK_PATH:
|
||||
case STATUS_NETWORK_UNREACHABLE:
|
||||
case STATUS_PROTOCOL_UNREACHABLE:
|
||||
return WSAENETUNREACH;
|
||||
|
||||
case STATUS_HOST_UNREACHABLE:
|
||||
return WSAEHOSTUNREACH;
|
||||
|
||||
case STATUS_CANCELLED:
|
||||
case STATUS_REQUEST_ABORTED:
|
||||
return WSAEINTR;
|
||||
|
||||
case STATUS_BUFFER_OVERFLOW:
|
||||
case STATUS_INVALID_BUFFER_SIZE:
|
||||
return WSAEMSGSIZE;
|
||||
|
||||
case STATUS_BUFFER_TOO_SMALL:
|
||||
case STATUS_ACCESS_VIOLATION:
|
||||
return WSAEFAULT;
|
||||
|
||||
case STATUS_DEVICE_NOT_READY:
|
||||
case STATUS_REQUEST_NOT_ACCEPTED:
|
||||
return WSAEWOULDBLOCK;
|
||||
|
||||
case STATUS_INVALID_NETWORK_RESPONSE:
|
||||
case STATUS_NETWORK_BUSY:
|
||||
case STATUS_NO_SUCH_DEVICE:
|
||||
case STATUS_NO_SUCH_FILE:
|
||||
case STATUS_OBJECT_PATH_NOT_FOUND:
|
||||
case STATUS_OBJECT_NAME_NOT_FOUND:
|
||||
case STATUS_UNEXPECTED_NETWORK_ERROR:
|
||||
return WSAENETDOWN;
|
||||
|
||||
case STATUS_INVALID_CONNECTION:
|
||||
return WSAENOTCONN;
|
||||
|
||||
case STATUS_REMOTE_NOT_LISTENING:
|
||||
case STATUS_CONNECTION_REFUSED:
|
||||
return WSAECONNREFUSED;
|
||||
|
||||
case STATUS_PIPE_DISCONNECTED:
|
||||
return WSAESHUTDOWN;
|
||||
|
||||
case STATUS_CONFLICTING_ADDRESSES:
|
||||
case STATUS_INVALID_ADDRESS:
|
||||
case STATUS_INVALID_ADDRESS_COMPONENT:
|
||||
return WSAEADDRNOTAVAIL;
|
||||
|
||||
case STATUS_NOT_SUPPORTED:
|
||||
case STATUS_NOT_IMPLEMENTED:
|
||||
return WSAEOPNOTSUPP;
|
||||
|
||||
case STATUS_ACCESS_DENIED:
|
||||
return WSAEACCES;
|
||||
|
||||
default:
|
||||
if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) &&
|
||||
(status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING))) {
|
||||
/* It's a windows error that has been previously mapped to an */
|
||||
/* ntstatus code. */
|
||||
return (DWORD) (status & 0xffff);
|
||||
} else {
|
||||
/* The default fallback for unmappable ntstatus codes. */
|
||||
return WSAEINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function provides a workaround for a bug in the winsock implementation
|
||||
* of WSARecv. The problem is that when SetFileCompletionNotificationModes is
|
||||
* used to avoid IOCP notifications of completed reads, WSARecv does not
|
||||
* reliably indicate whether we can expect a completion package to be posted
|
||||
* when the receive buffer is smaller than the received datagram.
|
||||
*
|
||||
* However it is desirable to use SetFileCompletionNotificationModes because
|
||||
* it yields a massive performance increase.
|
||||
*
|
||||
* This function provides a workaround for that bug, but it only works for the
|
||||
* specific case that we need it for. E.g. it assumes that the "avoid iocp"
|
||||
* bit has been set, and supports only overlapped operation. It also requires
|
||||
* the user to use the default msafd driver, doesn't work when other LSPs are
|
||||
* stacked on top of it.
|
||||
*/
|
||||
int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
|
||||
DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
|
||||
NTSTATUS status;
|
||||
void* apc_context;
|
||||
IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
|
||||
AFD_RECV_INFO info;
|
||||
DWORD error;
|
||||
|
||||
if (overlapped == NULL || completion_routine != NULL) {
|
||||
WSASetLastError(WSAEINVAL);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
info.BufferArray = buffers;
|
||||
info.BufferCount = buffer_count;
|
||||
info.AfdFlags = AFD_OVERLAPPED;
|
||||
info.TdiFlags = TDI_RECEIVE_NORMAL;
|
||||
|
||||
if (*flags & MSG_PEEK) {
|
||||
info.TdiFlags |= TDI_RECEIVE_PEEK;
|
||||
}
|
||||
|
||||
if (*flags & MSG_PARTIAL) {
|
||||
info.TdiFlags |= TDI_RECEIVE_PARTIAL;
|
||||
}
|
||||
|
||||
if (!((intptr_t) overlapped->hEvent & 1)) {
|
||||
apc_context = (void*) overlapped;
|
||||
} else {
|
||||
apc_context = NULL;
|
||||
}
|
||||
|
||||
iosb->Status = STATUS_PENDING;
|
||||
iosb->Pointer = 0;
|
||||
|
||||
status = pNtDeviceIoControlFile((HANDLE) socket,
|
||||
overlapped->hEvent,
|
||||
NULL,
|
||||
apc_context,
|
||||
iosb,
|
||||
IOCTL_AFD_RECEIVE,
|
||||
&info,
|
||||
sizeof(info),
|
||||
NULL,
|
||||
0);
|
||||
|
||||
*flags = 0;
|
||||
*bytes = (DWORD) iosb->Information;
|
||||
|
||||
switch (status) {
|
||||
case STATUS_SUCCESS:
|
||||
error = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case STATUS_PENDING:
|
||||
error = WSA_IO_PENDING;
|
||||
break;
|
||||
|
||||
case STATUS_BUFFER_OVERFLOW:
|
||||
error = WSAEMSGSIZE;
|
||||
break;
|
||||
|
||||
case STATUS_RECEIVE_EXPEDITED:
|
||||
error = ERROR_SUCCESS;
|
||||
*flags = MSG_OOB;
|
||||
break;
|
||||
|
||||
case STATUS_RECEIVE_PARTIAL_EXPEDITED:
|
||||
error = ERROR_SUCCESS;
|
||||
*flags = MSG_PARTIAL | MSG_OOB;
|
||||
break;
|
||||
|
||||
case STATUS_RECEIVE_PARTIAL:
|
||||
error = ERROR_SUCCESS;
|
||||
*flags = MSG_PARTIAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
error = uv_ntstatus_to_winsock_error(status);
|
||||
break;
|
||||
}
|
||||
|
||||
WSASetLastError(error);
|
||||
|
||||
if (error == ERROR_SUCCESS) {
|
||||
return 0;
|
||||
} else {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* See description of uv_wsarecv_workaround. */
|
||||
int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
|
||||
DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
|
||||
int* addr_len, WSAOVERLAPPED *overlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
|
||||
NTSTATUS status;
|
||||
void* apc_context;
|
||||
IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
|
||||
AFD_RECV_DATAGRAM_INFO info;
|
||||
DWORD error;
|
||||
|
||||
if (overlapped == NULL || addr == NULL || addr_len == NULL ||
|
||||
completion_routine != NULL) {
|
||||
WSASetLastError(WSAEINVAL);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
info.BufferArray = buffers;
|
||||
info.BufferCount = buffer_count;
|
||||
info.AfdFlags = AFD_OVERLAPPED;
|
||||
info.TdiFlags = TDI_RECEIVE_NORMAL;
|
||||
info.Address = addr;
|
||||
info.AddressLength = addr_len;
|
||||
|
||||
if (*flags & MSG_PEEK) {
|
||||
info.TdiFlags |= TDI_RECEIVE_PEEK;
|
||||
}
|
||||
|
||||
if (*flags & MSG_PARTIAL) {
|
||||
info.TdiFlags |= TDI_RECEIVE_PARTIAL;
|
||||
}
|
||||
|
||||
if (!((intptr_t) overlapped->hEvent & 1)) {
|
||||
apc_context = (void*) overlapped;
|
||||
} else {
|
||||
apc_context = NULL;
|
||||
}
|
||||
|
||||
iosb->Status = STATUS_PENDING;
|
||||
iosb->Pointer = 0;
|
||||
|
||||
status = pNtDeviceIoControlFile((HANDLE) socket,
|
||||
overlapped->hEvent,
|
||||
NULL,
|
||||
apc_context,
|
||||
iosb,
|
||||
IOCTL_AFD_RECEIVE_DATAGRAM,
|
||||
&info,
|
||||
sizeof(info),
|
||||
NULL,
|
||||
0);
|
||||
|
||||
*flags = 0;
|
||||
*bytes = (DWORD) iosb->Information;
|
||||
|
||||
switch (status) {
|
||||
case STATUS_SUCCESS:
|
||||
error = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case STATUS_PENDING:
|
||||
error = WSA_IO_PENDING;
|
||||
break;
|
||||
|
||||
case STATUS_BUFFER_OVERFLOW:
|
||||
error = WSAEMSGSIZE;
|
||||
break;
|
||||
|
||||
case STATUS_RECEIVE_EXPEDITED:
|
||||
error = ERROR_SUCCESS;
|
||||
*flags = MSG_OOB;
|
||||
break;
|
||||
|
||||
case STATUS_RECEIVE_PARTIAL_EXPEDITED:
|
||||
error = ERROR_SUCCESS;
|
||||
*flags = MSG_PARTIAL | MSG_OOB;
|
||||
break;
|
||||
|
||||
case STATUS_RECEIVE_PARTIAL:
|
||||
error = ERROR_SUCCESS;
|
||||
*flags = MSG_PARTIAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
error = uv_ntstatus_to_winsock_error(status);
|
||||
break;
|
||||
}
|
||||
|
||||
WSASetLastError(error);
|
||||
|
||||
if (error == ERROR_SUCCESS) {
|
||||
return 0;
|
||||
} else {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
|
||||
AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) {
|
||||
IO_STATUS_BLOCK iosb;
|
||||
IO_STATUS_BLOCK* iosb_ptr;
|
||||
HANDLE event = NULL;
|
||||
void* apc_context;
|
||||
NTSTATUS status;
|
||||
DWORD error;
|
||||
|
||||
if (overlapped != NULL) {
|
||||
/* Overlapped operation. */
|
||||
iosb_ptr = (IO_STATUS_BLOCK*) &overlapped->Internal;
|
||||
event = overlapped->hEvent;
|
||||
|
||||
/* Do not report iocp completion if hEvent is tagged. */
|
||||
if ((uintptr_t) event & 1) {
|
||||
event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1);
|
||||
apc_context = NULL;
|
||||
} else {
|
||||
apc_context = overlapped;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Blocking operation. */
|
||||
iosb_ptr = &iosb;
|
||||
event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (event == NULL) {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
apc_context = NULL;
|
||||
}
|
||||
|
||||
iosb_ptr->Status = STATUS_PENDING;
|
||||
status = pNtDeviceIoControlFile((HANDLE) socket,
|
||||
event,
|
||||
NULL,
|
||||
apc_context,
|
||||
iosb_ptr,
|
||||
IOCTL_AFD_POLL,
|
||||
info_in,
|
||||
sizeof *info_in,
|
||||
info_out,
|
||||
sizeof *info_out);
|
||||
|
||||
if (overlapped == NULL) {
|
||||
/* If this is a blocking operation, wait for the event to become */
|
||||
/* signaled, and then grab the real status from the io status block. */
|
||||
if (status == STATUS_PENDING) {
|
||||
DWORD r = WaitForSingleObject(event, INFINITE);
|
||||
|
||||
if (r == WAIT_FAILED) {
|
||||
DWORD saved_error = GetLastError();
|
||||
CloseHandle(event);
|
||||
WSASetLastError(saved_error);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
status = iosb.Status;
|
||||
}
|
||||
|
||||
CloseHandle(event);
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case STATUS_SUCCESS:
|
||||
error = ERROR_SUCCESS;
|
||||
break;
|
||||
|
||||
case STATUS_PENDING:
|
||||
error = WSA_IO_PENDING;
|
||||
break;
|
||||
|
||||
default:
|
||||
error = uv_ntstatus_to_winsock_error(status);
|
||||
break;
|
||||
}
|
||||
|
||||
WSASetLastError(error);
|
||||
|
||||
if (error == ERROR_SUCCESS) {
|
||||
return 0;
|
||||
} else {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/* 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_WINSOCK_H_
|
||||
#define UV_WIN_WINSOCK_H_
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <iptypes.h>
|
||||
#include <mswsock.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "winapi.h"
|
||||
|
||||
|
||||
/*
|
||||
* MinGW is missing these too
|
||||
*/
|
||||
#ifndef SO_UPDATE_CONNECT_CONTEXT
|
||||
# define SO_UPDATE_CONNECT_CONTEXT 0x7010
|
||||
#endif
|
||||
|
||||
#ifndef TCP_KEEPALIVE
|
||||
# define TCP_KEEPALIVE 3
|
||||
#endif
|
||||
|
||||
#ifndef IPV6_V6ONLY
|
||||
# define IPV6_V6ONLY 27
|
||||
#endif
|
||||
|
||||
#ifndef IPV6_HOPLIMIT
|
||||
# define IPV6_HOPLIMIT 21
|
||||
#endif
|
||||
|
||||
#ifndef SIO_BASE_HANDLE
|
||||
# define SIO_BASE_HANDLE 0x48000022
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TDI defines that are only in the DDK.
|
||||
* We only need receive flags so far.
|
||||
*/
|
||||
#ifndef TDI_RECEIVE_NORMAL
|
||||
#define TDI_RECEIVE_BROADCAST 0x00000004
|
||||
#define TDI_RECEIVE_MULTICAST 0x00000008
|
||||
#define TDI_RECEIVE_PARTIAL 0x00000010
|
||||
#define TDI_RECEIVE_NORMAL 0x00000020
|
||||
#define TDI_RECEIVE_EXPEDITED 0x00000040
|
||||
#define TDI_RECEIVE_PEEK 0x00000080
|
||||
#define TDI_RECEIVE_NO_RESPONSE_EXP 0x00000100
|
||||
#define TDI_RECEIVE_COPY_LOOKAHEAD 0x00000200
|
||||
#define TDI_RECEIVE_ENTIRE_MESSAGE 0x00000400
|
||||
#define TDI_RECEIVE_AT_DISPATCH_LEVEL 0x00000800
|
||||
#define TDI_RECEIVE_CONTROL_INFO 0x00001000
|
||||
#define TDI_RECEIVE_FORCE_INDICATION 0x00002000
|
||||
#define TDI_RECEIVE_NO_PUSH 0x00004000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The "Auxiliary Function Driver" is the windows kernel-mode driver that does
|
||||
* TCP, UDP etc. Winsock is just a layer that dispatches requests to it.
|
||||
* Having these definitions allows us to bypass winsock and make an AFD kernel
|
||||
* call directly, avoiding a bug in winsock's recvfrom implementation.
|
||||
*/
|
||||
|
||||
#define AFD_NO_FAST_IO 0x00000001
|
||||
#define AFD_OVERLAPPED 0x00000002
|
||||
#define AFD_IMMEDIATE 0x00000004
|
||||
|
||||
#define AFD_POLL_RECEIVE_BIT 0
|
||||
#define AFD_POLL_RECEIVE (1 << AFD_POLL_RECEIVE_BIT)
|
||||
#define AFD_POLL_RECEIVE_EXPEDITED_BIT 1
|
||||
#define AFD_POLL_RECEIVE_EXPEDITED (1 << AFD_POLL_RECEIVE_EXPEDITED_BIT)
|
||||
#define AFD_POLL_SEND_BIT 2
|
||||
#define AFD_POLL_SEND (1 << AFD_POLL_SEND_BIT)
|
||||
#define AFD_POLL_DISCONNECT_BIT 3
|
||||
#define AFD_POLL_DISCONNECT (1 << AFD_POLL_DISCONNECT_BIT)
|
||||
#define AFD_POLL_ABORT_BIT 4
|
||||
#define AFD_POLL_ABORT (1 << AFD_POLL_ABORT_BIT)
|
||||
#define AFD_POLL_LOCAL_CLOSE_BIT 5
|
||||
#define AFD_POLL_LOCAL_CLOSE (1 << AFD_POLL_LOCAL_CLOSE_BIT)
|
||||
#define AFD_POLL_CONNECT_BIT 6
|
||||
#define AFD_POLL_CONNECT (1 << AFD_POLL_CONNECT_BIT)
|
||||
#define AFD_POLL_ACCEPT_BIT 7
|
||||
#define AFD_POLL_ACCEPT (1 << AFD_POLL_ACCEPT_BIT)
|
||||
#define AFD_POLL_CONNECT_FAIL_BIT 8
|
||||
#define AFD_POLL_CONNECT_FAIL (1 << AFD_POLL_CONNECT_FAIL_BIT)
|
||||
#define AFD_POLL_QOS_BIT 9
|
||||
#define AFD_POLL_QOS (1 << AFD_POLL_QOS_BIT)
|
||||
#define AFD_POLL_GROUP_QOS_BIT 10
|
||||
#define AFD_POLL_GROUP_QOS (1 << AFD_POLL_GROUP_QOS_BIT)
|
||||
|
||||
#define AFD_NUM_POLL_EVENTS 11
|
||||
#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1)
|
||||
|
||||
typedef struct _AFD_RECV_DATAGRAM_INFO {
|
||||
LPWSABUF BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
ULONG TdiFlags;
|
||||
struct sockaddr* Address;
|
||||
int* AddressLength;
|
||||
} AFD_RECV_DATAGRAM_INFO, *PAFD_RECV_DATAGRAM_INFO;
|
||||
|
||||
typedef struct _AFD_RECV_INFO {
|
||||
LPWSABUF BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
ULONG TdiFlags;
|
||||
} AFD_RECV_INFO, *PAFD_RECV_INFO;
|
||||
|
||||
|
||||
#define _AFD_CONTROL_CODE(operation, method) \
|
||||
((FSCTL_AFD_BASE) << 12 | (operation << 2) | method)
|
||||
|
||||
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
||||
|
||||
#define AFD_RECEIVE 5
|
||||
#define AFD_RECEIVE_DATAGRAM 6
|
||||
#define AFD_POLL 9
|
||||
|
||||
#define IOCTL_AFD_RECEIVE \
|
||||
_AFD_CONTROL_CODE(AFD_RECEIVE, METHOD_NEITHER)
|
||||
|
||||
#define IOCTL_AFD_RECEIVE_DATAGRAM \
|
||||
_AFD_CONTROL_CODE(AFD_RECEIVE_DATAGRAM, METHOD_NEITHER)
|
||||
|
||||
#define IOCTL_AFD_POLL \
|
||||
_AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED)
|
||||
|
||||
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP {
|
||||
/* FIXME: __C89_NAMELESS was removed */
|
||||
/* __C89_NAMELESS */ union {
|
||||
ULONGLONG Alignment;
|
||||
/* __C89_NAMELESS */ struct {
|
||||
ULONG Length;
|
||||
DWORD Flags;
|
||||
};
|
||||
};
|
||||
struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next;
|
||||
SOCKET_ADDRESS Address;
|
||||
IP_PREFIX_ORIGIN PrefixOrigin;
|
||||
IP_SUFFIX_ORIGIN SuffixOrigin;
|
||||
IP_DAD_STATE DadState;
|
||||
ULONG ValidLifetime;
|
||||
ULONG PreferredLifetime;
|
||||
ULONG LeaseLifetime;
|
||||
} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP;
|
||||
|
||||
typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH {
|
||||
union {
|
||||
ULONGLONG Alignment;
|
||||
struct {
|
||||
ULONG Length;
|
||||
DWORD Flags;
|
||||
};
|
||||
};
|
||||
struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next;
|
||||
SOCKET_ADDRESS Address;
|
||||
IP_PREFIX_ORIGIN PrefixOrigin;
|
||||
IP_SUFFIX_ORIGIN SuffixOrigin;
|
||||
IP_DAD_STATE DadState;
|
||||
ULONG ValidLifetime;
|
||||
ULONG PreferredLifetime;
|
||||
ULONG LeaseLifetime;
|
||||
UINT8 OnLinkPrefixLength;
|
||||
} IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* UV_WIN_WINSOCK_H_ */
|
||||
Reference in New Issue
Block a user