libuv 1.45.0, #include cleanup, probably something else.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4308 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
2023-05-21 21:36:51 +00:00
parent 1ccb9183b4
commit f421606e21
299 changed files with 7167 additions and 4918 deletions

View File

@ -245,6 +245,9 @@ int uv_loop_init(uv_loop_t* loop) {
err = uv_mutex_init(&lfields->loop_metrics.lock);
if (err)
goto fail_metrics_mutex_init;
memset(&lfields->loop_metrics.metrics,
0,
sizeof(lfields->loop_metrics.metrics));
/* To prevent uninitialized memory access, loop->time must be initialized
* to zero before calling uv_update_time for the first time.
@ -279,9 +282,6 @@ int uv_loop_init(uv_loop_t* loop) {
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;
@ -424,6 +424,7 @@ int uv_backend_timeout(const uv_loop_t* loop) {
static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
uv__loop_internal_fields_t* lfields;
DWORD bytes;
ULONG_PTR key;
OVERLAPPED* overlapped;
@ -433,9 +434,10 @@ static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
uint64_t user_timeout;
int reset_timeout;
lfields = uv__get_internal_fields(loop);
timeout_time = loop->time + timeout;
if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
if (lfields->flags & UV_METRICS_IDLE_TIME) {
reset_timeout = 1;
user_timeout = timeout;
timeout = 0;
@ -450,6 +452,12 @@ static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
if (timeout != 0)
uv__metrics_set_provider_entry_time(loop);
/* Store the current timeout in a location that's globally accessible so
* other locations like uv__work_done() can determine whether the queue
* of events in the callback were waiting when poll was called.
*/
lfields->current_timeout = timeout;
GetQueuedCompletionStatus(loop->iocp,
&bytes,
&key,
@ -457,6 +465,8 @@ static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
timeout);
if (reset_timeout != 0) {
if (overlapped && timeout == 0)
uv__metrics_inc_events_waiting(loop, 1);
timeout = user_timeout;
reset_timeout = 0;
}
@ -469,6 +479,8 @@ static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
uv__metrics_update_idle_time(loop);
if (overlapped) {
uv__metrics_inc_events(loop, 1);
/* Package was dequeued */
req = uv__overlapped_to_req(overlapped);
uv__insert_pending_req(loop, req);
@ -503,6 +515,7 @@ static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
static void uv__poll(uv_loop_t* loop, DWORD timeout) {
uv__loop_internal_fields_t* lfields;
BOOL success;
uv_req_t* req;
OVERLAPPED_ENTRY overlappeds[128];
@ -511,11 +524,13 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
int repeat;
uint64_t timeout_time;
uint64_t user_timeout;
uint64_t actual_timeout;
int reset_timeout;
lfields = uv__get_internal_fields(loop);
timeout_time = loop->time + timeout;
if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
if (lfields->flags & UV_METRICS_IDLE_TIME) {
reset_timeout = 1;
user_timeout = timeout;
timeout = 0;
@ -524,12 +539,20 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
}
for (repeat = 0; ; repeat++) {
actual_timeout = timeout;
/* Only need to set the provider_entry_time if timeout != 0. The function
* will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
*/
if (timeout != 0)
uv__metrics_set_provider_entry_time(loop);
/* Store the current timeout in a location that's globally accessible so
* other locations like uv__work_done() can determine whether the queue
* of events in the callback were waiting when poll was called.
*/
lfields->current_timeout = timeout;
success = pGetQueuedCompletionStatusEx(loop->iocp,
overlappeds,
ARRAY_SIZE(overlappeds),
@ -543,9 +566,9 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
}
/* Placed here because on success the loop will break whether there is an
* empty package or not, or if GetQueuedCompletionStatus returned early then
* the timeout will be updated and the loop will run again. In either case
* the idle time will need to be updated.
* empty package or not, or if pGetQueuedCompletionStatusEx returned early
* then the timeout will be updated and the loop will run again. In either
* case the idle time will need to be updated.
*/
uv__metrics_update_idle_time(loop);
@ -555,6 +578,10 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
* meant only to wake us up.
*/
if (overlappeds[i].lpOverlapped) {
uv__metrics_inc_events(loop, 1);
if (actual_timeout == 0)
uv__metrics_inc_events_waiting(loop, 1);
req = uv__overlapped_to_req(overlappeds[i].lpOverlapped);
uv__insert_pending_req(loop, req);
}
@ -598,10 +625,17 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
if (!r)
uv_update_time(loop);
while (r != 0 && loop->stop_flag == 0) {
uv_update_time(loop);
/* Maintain backwards compatibility by processing timers before entering the
* while loop for UV_RUN_DEFAULT. Otherwise timers only need to be executed
* once, which should be done after polling in order to maintain proper
* execution order of the conceptual event loop. */
if (mode == UV_RUN_DEFAULT) {
if (r)
uv_update_time(loop);
uv__run_timers(loop);
}
while (r != 0 && loop->stop_flag == 0) {
can_sleep = loop->pending_reqs_tail == NULL && loop->idle_handles == NULL;
uv__process_reqs(loop);
@ -612,6 +646,8 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
timeout = uv_backend_timeout(loop);
uv__metrics_inc_loop_count(loop);
if (pGetQueuedCompletionStatusEx)
uv__poll(loop, timeout);
else
@ -632,18 +668,8 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
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_update_time(loop);
uv__run_timers(loop);
}
uv_update_time(loop);
uv__run_timers(loop);
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)

View File

@ -36,6 +36,8 @@
#include "handle-inl.h"
#include "fs-fd-hash-inl.h"
#include <winioctl.h>
#define UV_FS_FREE_PATHS 0x0002
#define UV_FS_FREE_PTR 0x0008
@ -1706,11 +1708,36 @@ void fs__closedir(uv_fs_t* req) {
INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
int do_lstat) {
FILE_FS_DEVICE_INFORMATION device_info;
FILE_ALL_INFORMATION file_info;
FILE_FS_VOLUME_INFORMATION volume_info;
NTSTATUS nt_status;
IO_STATUS_BLOCK io_status;
nt_status = pNtQueryVolumeInformationFile(handle,
&io_status,
&device_info,
sizeof device_info,
FileFsDeviceInformation);
/* Buffer overflow (a warning status code) is expected here. */
if (NT_ERROR(nt_status)) {
SetLastError(pRtlNtStatusToDosError(nt_status));
return -1;
}
/* If it's NUL device set fields as reasonable as possible and return. */
if (device_info.DeviceType == FILE_DEVICE_NULL) {
memset(statbuf, 0, sizeof(uv_stat_t));
statbuf->st_mode = _S_IFCHR;
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
((_S_IREAD | _S_IWRITE) >> 6);
statbuf->st_nlink = 1;
statbuf->st_blksize = 4096;
statbuf->st_rdev = FILE_DEVICE_NULL << 16;
return 0;
}
nt_status = pNtQueryInformationFile(handle,
&io_status,
&file_info,
@ -1915,6 +1942,37 @@ INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) {
}
INLINE static int fs__fstat_handle(int fd, HANDLE handle, uv_stat_t* statbuf) {
DWORD file_type;
/* Each file type is processed differently. */
file_type = uv_guess_handle(fd);
switch (file_type) {
/* Disk files use the existing logic from fs__stat_handle. */
case UV_FILE:
return fs__stat_handle(handle, statbuf, 0);
/* Devices and pipes are processed identically. There is no more information
* for them from any API. Fields are set as reasonably as possible and the
* function returns. */
case UV_TTY:
case UV_NAMED_PIPE:
memset(statbuf, 0, sizeof(uv_stat_t));
statbuf->st_mode = file_type == UV_TTY ? _S_IFCHR : _S_IFIFO;
statbuf->st_nlink = 1;
statbuf->st_rdev = (file_type == UV_TTY ? FILE_DEVICE_CONSOLE : FILE_DEVICE_NAMED_PIPE) << 16;
statbuf->st_ino = (uint64_t) handle;
return 0;
/* If file type is unknown it is an error. */
case UV_UNKNOWN_HANDLE:
default:
SetLastError(ERROR_INVALID_HANDLE);
return -1;
}
}
static void fs__stat(uv_fs_t* req) {
fs__stat_prepare_path(req->file.pathw);
fs__stat_impl(req, 0);
@ -1940,7 +1998,7 @@ static void fs__fstat(uv_fs_t* req) {
return;
}
if (fs__stat_handle(handle, &req->statbuf, 0) != 0) {
if (fs__fstat_handle(fd, handle, &req->statbuf) != 0) {
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
@ -2221,7 +2279,7 @@ static void fs__fchmod(uv_fs_t* req) {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
goto fchmod_cleanup;
}
/* Remeber to clear the flag later on */
/* Remember to clear the flag later on */
clear_archive_flag = 1;
} else {
clear_archive_flag = 0;
@ -2604,7 +2662,10 @@ static void fs__readlink(uv_fs_t* req) {
}
if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
SET_REQ_WIN32_ERROR(req, GetLastError());
DWORD error = GetLastError();
SET_REQ_WIN32_ERROR(req, error);
if (error == ERROR_NOT_A_REPARSE_POINT)
req->result = UV_EINVAL;
CloseHandle(handle);
return;
}

View File

@ -267,7 +267,6 @@ void uv__util_init(void);
uint64_t uv__hrtime(unsigned int scale);
__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);
int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16);

View File

@ -792,15 +792,17 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) {
/* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait
* up to 30 seconds for the pipe to become available with WaitNamedPipe. */
while (WaitNamedPipeW(handle->name, 30000)) {
while (WaitNamedPipeW(req->u.connect.name, 30000)) {
/* The pipe is now available, try to connect. */
pipeHandle = open_named_pipe(handle->name, &duplex_flags);
pipeHandle = open_named_pipe(req->u.connect.name, &duplex_flags);
if (pipeHandle != INVALID_HANDLE_VALUE)
break;
SwitchToThread();
}
uv__free(req->u.connect.name);
req->u.connect.name = NULL;
if (pipeHandle != INVALID_HANDLE_VALUE) {
SET_REQ_SUCCESS(req);
req->u.connect.pipeHandle = pipeHandle;
@ -828,6 +830,7 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
req->cb = cb;
req->u.connect.pipeHandle = INVALID_HANDLE_VALUE;
req->u.connect.duplex_flags = 0;
req->u.connect.name = NULL;
if (handle->flags & UV_HANDLE_PIPESERVER) {
err = ERROR_INVALID_PARAMETER;
@ -859,10 +862,19 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
pipeHandle = open_named_pipe(handle->name, &duplex_flags);
if (pipeHandle == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_PIPE_BUSY) {
req->u.connect.name = uv__malloc(nameSize);
if (!req->u.connect.name) {
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
}
memcpy(req->u.connect.name, handle->name, nameSize);
/* Wait for the server to make a pipe instance available. */
if (!QueueUserWorkItem(&pipe_connect_thread_proc,
req,
WT_EXECUTELONGFUNCTION)) {
uv__free(req->u.connect.name);
req->u.connect.name = NULL;
err = GetLastError();
goto error;
}
@ -1067,11 +1079,12 @@ int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
err = uv__tcp_xfer_import(
(uv_tcp_t*) client, item->xfer_type, &item->xfer_info);
uv__free(item);
if (err != 0)
return err;
uv__free(item);
} else {
pipe_client = (uv_pipe_t*) client;
uv__pipe_connection_init(pipe_client);
@ -1638,9 +1651,13 @@ static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) {
/* If the both ends of the IPC pipe are owned by the same process,
* the remote end pid may not yet be set. If so, do it here.
* TODO: this is weird; it'd probably better to use a handshake. */
if (*pid == 0)
*pid = GetCurrentProcessId();
if (*pid == 0) {
GetNamedPipeClientProcessId(handle->handle, pid);
if (*pid == GetCurrentProcessId()) {
GetNamedPipeServerProcessId(handle->handle, pid);
}
}
return *pid;
}
@ -2069,9 +2086,9 @@ void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
uv__queue_non_overlapped_write(handle);
}
if (handle->stream.conn.write_reqs_pending == 0)
if (handle->flags & UV_HANDLE_SHUTTING)
uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req);
if (handle->stream.conn.write_reqs_pending == 0 &&
uv__is_stream_shutting(handle))
uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req);
DECREASE_PENDING_REQ_COUNT(handle);
}
@ -2126,7 +2143,10 @@ void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
if (REQ_SUCCESS(req)) {
pipeHandle = req->u.connect.pipeHandle;
duplex_flags = req->u.connect.duplex_flags;
err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags);
if (handle->flags & UV_HANDLE_CLOSING)
err = UV_ECANCELED;
else
err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags);
if (err)
CloseHandle(pipeHandle);
} else {
@ -2149,7 +2169,6 @@ void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
/* Clear the shutdown_req field so we don't go here again. */
handle->stream.conn.shutdown_req = NULL;
handle->flags &= ~UV_HANDLE_SHUTTING;
UNREGISTER_HANDLE_REQ(loop, handle, req);
if (handle->flags & UV_HANDLE_CLOSING) {
@ -2342,7 +2361,10 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
if (pipe->ipc) {
assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE));
pipe->pipe.conn.ipc_remote_pid = uv_os_getppid();
GetNamedPipeClientProcessId(os_handle, &pipe->pipe.conn.ipc_remote_pid);
if (pipe->pipe.conn.ipc_remote_pid == GetCurrentProcessId()) {
GetNamedPipeServerProcessId(os_handle, &pipe->pipe.conn.ipc_remote_pid);
}
assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1);
}
return 0;

View File

@ -425,9 +425,8 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
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. */
* we find an AFD handle and are able to use the fast poll mechanism.
*/
#ifndef NDEBUG
base_socket = INVALID_SOCKET;
#endif

View File

@ -32,6 +32,9 @@
#include "internal.h"
#include "handle-inl.h"
#include "req-inl.h"
#include <dbghelp.h>
#include <shlobj.h>
#include <psapi.h> /* GetModuleBaseNameW */
#define SIGKILL 9
@ -144,7 +147,6 @@ static void uv__process_init(uv_loop_t* loop, uv_process_t* handle) {
handle->exit_signal = 0;
handle->wait_handle = INVALID_HANDLE_VALUE;
handle->process_handle = INVALID_HANDLE_VALUE;
handle->child_stdio_buffer = NULL;
handle->exit_cb_pending = 0;
UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT);
@ -947,9 +949,11 @@ int uv_spawn(uv_loop_t* loop,
STARTUPINFOW startup;
PROCESS_INFORMATION info;
DWORD process_flags;
BYTE* child_stdio_buffer;
uv__process_init(loop, process);
process->exit_cb = options->exit_cb;
child_stdio_buffer = NULL;
if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
return UV_ENOTSUP;
@ -1040,7 +1044,7 @@ int uv_spawn(uv_loop_t* loop,
}
}
err = uv__stdio_create(loop, options, &process->child_stdio_buffer);
err = uv__stdio_create(loop, options, &child_stdio_buffer);
if (err)
goto done;
@ -1059,12 +1063,12 @@ int uv_spawn(uv_loop_t* loop,
startup.lpTitle = NULL;
startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
startup.lpReserved2 = (BYTE*) process->child_stdio_buffer;
startup.cbReserved2 = uv__stdio_size(child_stdio_buffer);
startup.lpReserved2 = (BYTE*) child_stdio_buffer;
startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);
startup.hStdInput = uv__stdio_handle(child_stdio_buffer, 0);
startup.hStdOutput = uv__stdio_handle(child_stdio_buffer, 1);
startup.hStdError = uv__stdio_handle(child_stdio_buffer, 2);
process_flags = CREATE_UNICODE_ENVIRONMENT;
@ -1178,10 +1182,10 @@ int uv_spawn(uv_loop_t* loop,
uv__free(env);
uv__free(alloc_path);
if (process->child_stdio_buffer != NULL) {
if (child_stdio_buffer != NULL) {
/* Clean up child stdio handles. */
uv__stdio_destroy(process->child_stdio_buffer);
process->child_stdio_buffer = NULL;
uv__stdio_destroy(child_stdio_buffer);
child_stdio_buffer = NULL;
}
return uv_translate_sys_error(err);
@ -1193,7 +1197,120 @@ static int uv__kill(HANDLE process_handle, int signum) {
return UV_EINVAL;
}
/* Create a dump file for the targeted process, if the registry key
* `HKLM:Software\Microsoft\Windows\Windows Error Reporting\LocalDumps`
* exists. The location of the dumps can be influenced by the `DumpFolder`
* sub-key, which has a default value of `%LOCALAPPDATA%\CrashDumps`, see [0]
* for more detail. Note that if the dump folder does not exist, we attempt
* to create it, to match behavior with WER itself.
* [0]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps */
if (signum == SIGQUIT) {
HKEY registry_key;
DWORD pid, ret;
WCHAR basename[MAX_PATH];
/* Get target process name. */
GetModuleBaseNameW(process_handle, NULL, &basename[0], sizeof(basename));
/* Get PID of target process. */
pid = GetProcessId(process_handle);
/* Get LocalDumps directory path. */
ret = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps",
0,
KEY_QUERY_VALUE,
&registry_key);
if (ret == ERROR_SUCCESS) {
HANDLE hDumpFile = NULL;
WCHAR dump_folder[MAX_PATH], dump_name[MAX_PATH];
DWORD dump_folder_len = sizeof(dump_folder), key_type = 0;
ret = RegGetValueW(registry_key,
NULL,
L"DumpFolder",
RRF_RT_ANY,
&key_type,
(PVOID) dump_folder,
&dump_folder_len);
if (ret != ERROR_SUCCESS) {
/* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */
WCHAR* localappdata;
SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &localappdata);
_snwprintf_s(dump_folder,
sizeof(dump_folder),
_TRUNCATE,
L"%ls\\CrashDumps",
localappdata);
CoTaskMemFree(localappdata);
}
RegCloseKey(registry_key);
/* Create dump folder if it doesn't already exist. */
CreateDirectoryW(dump_folder, NULL);
/* Construct dump filename from process name and PID. */
_snwprintf_s(dump_name,
sizeof(dump_name),
_TRUNCATE,
L"%ls\\%ls.%d.dmp",
dump_folder,
basename,
pid);
hDumpFile = CreateFileW(dump_name,
GENERIC_WRITE,
0,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDumpFile != INVALID_HANDLE_VALUE) {
DWORD dump_options, sym_options;
FILE_DISPOSITION_INFO DeleteOnClose = { TRUE };
/* If something goes wrong while writing it out, delete the file. */
SetFileInformationByHandle(hDumpFile,
FileDispositionInfo,
&DeleteOnClose,
sizeof(DeleteOnClose));
/* Tell wine to dump ELF modules as well. */
sym_options = SymGetOptions();
SymSetOptions(sym_options | 0x40000000);
/* MiniDumpWithAvxXStateContext might be undef in server2012r2 or mingw < 12 */
#ifndef MiniDumpWithAvxXStateContext
#define MiniDumpWithAvxXStateContext 0x00200000
#endif
/* We default to a fairly complete dump. In the future, we may want to
* allow clients to customize what kind of dump to create. */
dump_options = MiniDumpWithFullMemory |
MiniDumpIgnoreInaccessibleMemory |
MiniDumpWithAvxXStateContext;
if (MiniDumpWriteDump(process_handle,
pid,
hDumpFile,
dump_options,
NULL,
NULL,
NULL)) {
/* Don't delete the file on close if we successfully wrote it out. */
FILE_DISPOSITION_INFO DontDeleteOnClose = { FALSE };
SetFileInformationByHandle(hDumpFile,
FileDispositionInfo,
&DontDeleteOnClose,
sizeof(DontDeleteOnClose));
}
SymSetOptions(sym_options);
CloseHandle(hDumpFile);
}
}
}
switch (signum) {
case SIGQUIT:
case SIGTERM:
case SIGKILL:
case SIGINT: {

View File

@ -204,7 +204,7 @@ 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) ||
handle->flags & UV_HANDLE_SHUTTING ||
uv__is_stream_shutting(handle) ||
uv__is_closing(handle)) {
return UV_ENOTCONN;
}
@ -214,7 +214,6 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
req->cb = cb;
handle->flags &= ~UV_HANDLE_WRITABLE;
handle->flags |= UV_HANDLE_SHUTTING;
handle->stream.conn.shutdown_req = req;
handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);

View File

@ -29,14 +29,6 @@
#include "req-inl.h"
/*
* Threshold of active tcp streams for which to preallocate tcp read buffers.
* (Due to node slab allocator performing poorly under this pattern,
* the optimization is temporarily disabled (threshold=0). This will be
* revisited once node allocator is improved.)
*/
const unsigned int uv_active_tcp_streams_threshold = 0;
/*
* Number of simultaneous pending AcceptEx calls.
*/
@ -214,7 +206,6 @@ void uv__process_tcp_shutdown_req(uv_loop_t* loop, uv_tcp_t* stream, uv_shutdown
assert(stream->flags & UV_HANDLE_CONNECTION);
stream->stream.conn.shutdown_req = NULL;
stream->flags &= ~UV_HANDLE_SHUTTING;
UNREGISTER_HANDLE_REQ(loop, stream, req);
err = 0;
@ -274,7 +265,6 @@ void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
}
uv__handle_close(handle);
loop->active_tcp_streams--;
}
@ -484,26 +474,9 @@ static void uv__tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) {
req = &handle->read_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_tcp_streams < uv_active_tcp_streams_threshold) {
handle->flags &= ~UV_HANDLE_ZERO_READ;
handle->tcp.conn.read_buffer = uv_buf_init(NULL, 0);
handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->tcp.conn.read_buffer);
if (handle->tcp.conn.read_buffer.base == NULL ||
handle->tcp.conn.read_buffer.len == 0) {
handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tcp.conn.read_buffer);
return;
}
assert(handle->tcp.conn.read_buffer.base != NULL);
buf = handle->tcp.conn.read_buffer;
} else {
handle->flags |= UV_HANDLE_ZERO_READ;
buf.base = (char*) &uv_zero_;
buf.len = 0;
}
handle->flags |= UV_HANDLE_ZERO_READ;
buf.base = (char*) &uv_zero_;
buf.len = 0;
/* Prepare the overlapped structure. */
memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
@ -550,7 +523,7 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
struct linger l = { 1, 0 };
/* Disallow setting SO_LINGER to zero due to some platform inconsistencies */
if (handle->flags & UV_HANDLE_SHUTTING)
if (uv__is_stream_shutting(handle))
return UV_EINVAL;
if (0 != setsockopt(handle->socket, SOL_SOCKET, SO_LINGER, (const char*)&l, sizeof(l)))
@ -654,7 +627,6 @@ 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) {
uv_loop_t* loop = server->loop;
int err = 0;
int family;
@ -716,8 +688,6 @@ int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
}
}
loop->active_tcp_streams++;
return err;
}
@ -1163,7 +1133,7 @@ void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
closesocket(handle->socket);
handle->socket = INVALID_SOCKET;
}
if (handle->flags & UV_HANDLE_SHUTTING)
if (uv__is_stream_shutting(handle))
uv__process_tcp_shutdown_req(loop,
handle,
handle->stream.conn.shutdown_req);
@ -1248,7 +1218,6 @@ void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
0) == 0) {
uv__connection_init((uv_stream_t*)handle);
handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
loop->active_tcp_streams++;
} else {
err = WSAGetLastError();
}
@ -1331,7 +1300,6 @@ int uv__tcp_xfer_import(uv_tcp_t* tcp,
tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
}
tcp->loop->active_tcp_streams++;
return 0;
}
@ -1432,7 +1400,7 @@ static void uv__tcp_try_cancel_reqs(uv_tcp_t* tcp) {
uv_tcp_non_ifs_lsp_ipv4;
/* If there are non-ifs LSPs then try to obtain a base handle for the socket.
* This will always fail on Windows XP/3k. */
*/
if (non_ifs_lsp) {
DWORD bytes;
if (WSAIoctl(socket,

View File

@ -180,6 +180,81 @@ int uv_thread_create_ex(uv_thread_t* tid,
return UV_EIO;
}
int uv_thread_setaffinity(uv_thread_t* tid,
char* cpumask,
char* oldmask,
size_t mask_size) {
int i;
HANDLE hproc;
DWORD_PTR procmask;
DWORD_PTR sysmask;
DWORD_PTR threadmask;
DWORD_PTR oldthreadmask;
int cpumasksize;
cpumasksize = uv_cpumask_size();
assert(cpumasksize > 0);
if (mask_size < (size_t)cpumasksize)
return UV_EINVAL;
hproc = GetCurrentProcess();
if (!GetProcessAffinityMask(hproc, &procmask, &sysmask))
return uv_translate_sys_error(GetLastError());
threadmask = 0;
for (i = 0; i < cpumasksize; i++) {
if (cpumask[i]) {
if (procmask & (1 << i))
threadmask |= 1 << i;
else
return UV_EINVAL;
}
}
oldthreadmask = SetThreadAffinityMask(*tid, threadmask);
if (oldthreadmask == 0)
return uv_translate_sys_error(GetLastError());
if (oldmask != NULL) {
for (i = 0; i < cpumasksize; i++)
oldmask[i] = (oldthreadmask >> i) & 1;
}
return 0;
}
int uv_thread_getaffinity(uv_thread_t* tid,
char* cpumask,
size_t mask_size) {
int i;
HANDLE hproc;
DWORD_PTR procmask;
DWORD_PTR sysmask;
DWORD_PTR threadmask;
int cpumasksize;
cpumasksize = uv_cpumask_size();
assert(cpumasksize > 0);
if (mask_size < (size_t)cpumasksize)
return UV_EINVAL;
hproc = GetCurrentProcess();
if (!GetProcessAffinityMask(hproc, &procmask, &sysmask))
return uv_translate_sys_error(GetLastError());
threadmask = SetThreadAffinityMask(*tid, procmask);
if (threadmask == 0 || SetThreadAffinityMask(*tid, threadmask) == 0)
return uv_translate_sys_error(GetLastError());
for (i = 0; i < cpumasksize; i++)
cpumask[i] = (threadmask >> i) & 1;
return 0;
}
int uv_thread_getcpu(void) {
return GetCurrentProcessorNumber();
}
uv_thread_t uv_thread_self(void) {
uv_thread_t key;
@ -374,6 +449,7 @@ void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
abort();
}
int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6)))
return 0;
@ -383,69 +459,6 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t 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)

View File

@ -23,12 +23,7 @@
#include <io.h>
#include <string.h>
#include <stdlib.h>
#if defined(_MSC_VER) && _MSC_VER < 1600
# include "uv/stdint-msvc2008.h"
#else
# include <stdint.h>
#endif
#include <stdint.h>
#ifndef COMMON_LVB_REVERSE_VIDEO
# define COMMON_LVB_REVERSE_VIDEO 0x4000
@ -175,14 +170,14 @@ void uv__console_init(void) {
0);
if (uv__tty_console_handle != INVALID_HANDLE_VALUE) {
CONSOLE_SCREEN_BUFFER_INFO sb_info;
QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
NULL,
WT_EXECUTELONGFUNCTION);
uv_mutex_init(&uv__tty_console_resize_mutex);
if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) {
uv__tty_console_width = sb_info.dwSize.X;
uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
}
QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
NULL,
WT_EXECUTELONGFUNCTION);
}
}
@ -2239,11 +2234,11 @@ void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
handle->stream.conn.write_reqs_pending--;
if (handle->stream.conn.write_reqs_pending == 0)
if (handle->flags & UV_HANDLE_SHUTTING)
uv__process_tty_shutdown_req(loop,
handle,
handle->stream.conn.shutdown_req);
if (handle->stream.conn.write_reqs_pending == 0 &&
uv__is_stream_shutting(handle))
uv__process_tty_shutdown_req(loop,
handle,
handle->stream.conn.shutdown_req);
DECREASE_PENDING_REQ_COUNT(handle);
}
@ -2274,7 +2269,6 @@ void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown
assert(req);
stream->stream.conn.shutdown_req = NULL;
stream->flags &= ~UV_HANDLE_SHUTTING;
UNREGISTER_HANDLE_REQ(loop, stream, req);
/* TTY shutdown is really just a no-op */
@ -2429,7 +2423,6 @@ static void uv__tty_console_signal_resize(void) {
height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
uv_mutex_lock(&uv__tty_console_resize_mutex);
assert(uv__tty_console_width != -1 && uv__tty_console_height != -1);
if (width != uv__tty_console_width || height != uv__tty_console_height) {
uv__tty_console_width = width;
uv__tty_console_height = height;

View File

@ -29,11 +29,6 @@
#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_getpeername(const uv_udp_t* handle,
@ -276,84 +271,35 @@ static void uv__udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
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->flags |= UV_HANDLE_ZERO_READ;
handle->recv_buffer = uv_buf_init(NULL, 0);
handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &handle->recv_buffer);
if (handle->recv_buffer.base == NULL || handle->recv_buffer.len == 0) {
handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0);
return;
}
assert(handle->recv_buffer.base != NULL);
buf.base = (char*) uv_zero_;
buf.len = 0;
flags = MSG_PEEK;
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++;
}
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 {
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++;
}
/* Make this req pending reporting an error. */
SET_REQ_ERROR(req, WSAGetLastError());
uv__insert_pending_req(loop, req);
handle->reqs_pending++;
}
}
@ -376,7 +322,6 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb,
handle->flags |= UV_HANDLE_READING;
INCREASE_ACTIVE_COUNT(loop, handle);
loop->active_udp_streams++;
handle->recv_cb = recv_cb;
handle->alloc_cb = alloc_cb;
@ -393,7 +338,6 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb,
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);
}
@ -497,57 +441,68 @@ void uv__process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
DWORD bytes, err, flags;
struct sockaddr_storage from;
int from_len;
int count;
/* Do a nonblocking receive.
* TODO: try to read multiple datagrams at once. FIONREAD maybe? */
buf = uv_buf_init(NULL, 0);
handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf);
if (buf.base == NULL || buf.len == 0) {
handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
goto done;
}
assert(buf.base != NULL);
/* Prevent loop starvation when the data comes in as fast as
* (or faster than) we can read it. */
count = 32;
memset(&from, 0, sizeof from);
from_len = sizeof from;
do {
/* Do at most `count` nonblocking receive. */
buf = uv_buf_init(NULL, 0);
handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf);
if (buf.base == NULL || buf.len == 0) {
handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
goto done;
}
flags = 0;
memset(&from, 0, sizeof from);
from_len = sizeof from;
if (WSARecvFrom(handle->socket,
(WSABUF*)&buf,
1,
&bytes,
&flags,
(struct sockaddr*) &from,
&from_len,
NULL,
NULL) != SOCKET_ERROR) {
flags = 0;
/* 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);
if (WSARecvFrom(handle->socket,
(WSABUF*)&buf,
1,
&bytes,
&flags,
(struct sockaddr*) &from,
&from_len,
NULL,
NULL) != SOCKET_ERROR) {
/* Message received */
err = ERROR_SUCCESS;
handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 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);
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);
}
}
}
while (err == ERROR_SUCCESS &&
count-- > 0 &&
/* The recv_cb callback may decide to pause or close the handle. */
(handle->flags & UV_HANDLE_READING) &&
!(handle->flags & UV_HANDLE_READ_PENDING));
}
done:

View File

@ -31,6 +31,7 @@
#include "internal.h"
/* clang-format off */
#include <sysinfoapi.h>
#include <winsock2.h>
#include <winperf.h>
#include <iphlpapi.h>
@ -121,9 +122,6 @@ int uv_exepath(char* buffer, size_t* size_ptr) {
goto error;
}
/* utf16_len contains the length, *not* including the terminating null. */
utf16_buffer[utf16_len] = L'\0';
/* Convert to UTF-8 */
utf8_len = WideCharToMultiByte(CP_UTF8,
0,
@ -151,6 +149,51 @@ int uv_exepath(char* buffer, size_t* size_ptr) {
}
static int uv__cwd(WCHAR** buf, DWORD *len) {
WCHAR* p;
DWORD n;
DWORD t;
t = GetCurrentDirectoryW(0, NULL);
for (;;) {
if (t == 0)
return uv_translate_sys_error(GetLastError());
/* |t| is the size of the buffer _including_ nul. */
p = uv__malloc(t * sizeof(*p));
if (p == NULL)
return UV_ENOMEM;
/* |n| is the size of the buffer _excluding_ nul but _only on success_.
* If |t| was too small because another thread changed the working
* directory, |n| is the size the buffer should be _including_ nul.
* It therefore follows we must resize when n >= t and fail when n == 0.
*/
n = GetCurrentDirectoryW(t, p);
if (n > 0)
if (n < t)
break;
uv__free(p);
t = n;
}
/* The returned directory should not have a trailing slash, unless it points
* at a drive root, like c:\. Remove it if needed.
*/
t = n - 1;
if (p[t] == L'\\' && !(n == 3 && p[1] == L':')) {
p[t] = L'\0';
n = t;
}
*buf = p;
*len = n;
return 0;
}
int uv_cwd(char* buffer, size_t* size) {
DWORD utf16_len;
WCHAR *utf16_buffer;
@ -160,30 +203,9 @@ int uv_cwd(char* buffer, size_t* size) {
return UV_EINVAL;
}
utf16_len = GetCurrentDirectoryW(0, NULL);
if (utf16_len == 0) {
return uv_translate_sys_error(GetLastError());
}
utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
if (utf16_buffer == NULL) {
return UV_ENOMEM;
}
utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
if (utf16_len == 0) {
uv__free(utf16_buffer);
return uv_translate_sys_error(GetLastError());
}
/* utf16_len contains the length, *not* including the terminating null. */
utf16_buffer[utf16_len] = L'\0';
/* The returned directory should not have a trailing slash, unless it points
* at a drive root, like c:\. Remove it if needed. */
if (utf16_buffer[utf16_len - 1] == L'\\' &&
!(utf16_len == 3 && utf16_buffer[1] == L':')) {
utf16_len--;
utf16_buffer[utf16_len] = L'\0';
r = uv__cwd(&utf16_buffer, &utf16_len);
if (r < 0) {
return r;
}
/* Check how much space we need */
@ -226,8 +248,9 @@ int uv_cwd(char* buffer, size_t* size) {
int uv_chdir(const char* dir) {
WCHAR *utf16_buffer;
size_t utf16_len, new_utf16_len;
DWORD utf16_len;
WCHAR drive_letter, env_var[4];
int r;
if (dir == NULL) {
return UV_EINVAL;
@ -262,32 +285,22 @@ int uv_chdir(const char* dir) {
return uv_translate_sys_error(GetLastError());
}
/* uv__cwd() will return a new buffer. */
uv__free(utf16_buffer);
utf16_buffer = NULL;
/* Windows stores the drive-local path in an "hidden" environment variable,
* which has the form "=C:=C:\Windows". SetCurrentDirectory does not update
* this, so we'll have to do it. */
new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
if (new_utf16_len > utf16_len ) {
uv__free(utf16_buffer);
utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR));
if (utf16_buffer == NULL) {
/* When updating the environment variable fails, return UV_OK anyway.
* We did successfully change current working directory, only updating
* hidden env variable failed. */
return 0;
}
new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer);
}
if (utf16_len == 0) {
uv__free(utf16_buffer);
r = uv__cwd(&utf16_buffer, &utf16_len);
if (r == UV_ENOMEM) {
/* When updating the environment variable fails, return UV_OK anyway.
* We did successfully change current working directory, only updating
* hidden env variable failed. */
return 0;
}
/* The returned directory should not have a trailing slash, unless it points
* at a drive root, like c:\. Remove it if needed. */
if (utf16_buffer[utf16_len - 1] == L'\\' &&
!(utf16_len == 3 && utf16_buffer[1] == L':')) {
utf16_len--;
utf16_buffer[utf16_len] = L'\0';
if (r < 0) {
return r;
}
if (utf16_len < 2 || utf16_buffer[1] != L':') {
@ -330,7 +343,7 @@ uint64_t uv_get_free_memory(void) {
memory_status.dwLength = sizeof(memory_status);
if (!GlobalMemoryStatusEx(&memory_status)) {
return -1;
return 0;
}
return (uint64_t)memory_status.ullAvailPhys;
@ -342,7 +355,7 @@ uint64_t uv_get_total_memory(void) {
memory_status.dwLength = sizeof(memory_status);
if (!GlobalMemoryStatusEx(&memory_status)) {
return -1;
return 0;
}
return (uint64_t)memory_status.ullTotalPhys;
@ -354,6 +367,11 @@ uint64_t uv_get_constrained_memory(void) {
}
uint64_t uv_get_available_memory(void) {
return uv_get_free_memory();
}
uv_pid_t uv_os_getpid(void) {
return GetCurrentProcessId();
}
@ -487,11 +505,43 @@ int uv_get_process_title(char* buffer, size_t size) {
}
/* https://github.com/libuv/libuv/issues/1674 */
int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) {
FILETIME ft;
int64_t t;
if (ts == NULL)
return UV_EFAULT;
switch (clock_id) {
case UV_CLOCK_MONOTONIC:
uv__once_init();
t = uv__hrtime(UV__NANOSEC);
ts->tv_sec = t / 1000000000;
ts->tv_nsec = t % 1000000000;
return 0;
case UV_CLOCK_REALTIME:
GetSystemTimePreciseAsFileTime(&ft);
/* In 100-nanosecond increments from 1601-01-01 UTC because why not? */
t = (int64_t) ft.dwHighDateTime << 32 | ft.dwLowDateTime;
/* Convert to UNIX epoch, 1970-01-01. Still in 100 ns increments. */
t -= 116444736000000000ll;
/* Now convert to seconds and nanoseconds. */
ts->tv_sec = t / 10000000;
ts->tv_nsec = t % 10000000 * 100;
return 0;
}
return UV_EINVAL;
}
uint64_t uv_hrtime(void) {
uv__once_init();
return uv__hrtime(UV__NANOSEC);
}
uint64_t uv__hrtime(unsigned int scale) {
LARGE_INTEGER counter;
double scaled_freq;
@ -678,71 +728,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
}
static int is_windows_version_or_greater(DWORD os_major,
DWORD os_minor,
WORD service_pack_major,
WORD service_pack_minor) {
OSVERSIONINFOEX osvi;
DWORDLONG condition_mask = 0;
int op = VER_GREATER_EQUAL;
/* Initialize the OSVERSIONINFOEX structure. */
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = os_major;
osvi.dwMinorVersion = os_minor;
osvi.wServicePackMajor = service_pack_major;
osvi.wServicePackMinor = service_pack_minor;
/* Initialize the condition mask. */
VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op);
VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op);
VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op);
VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op);
/* Perform the test. */
return (int) VerifyVersionInfo(
&osvi,
VER_MAJORVERSION | VER_MINORVERSION |
VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
condition_mask);
}
static int address_prefix_match(int family,
struct sockaddr* address,
struct sockaddr* prefix_address,
int prefix_len) {
uint8_t* address_data;
uint8_t* prefix_address_data;
int i;
assert(address->sa_family == family);
assert(prefix_address->sa_family == family);
if (family == AF_INET6) {
address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr);
prefix_address_data =
(uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr);
} else {
address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr);
prefix_address_data =
(uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr);
}
for (i = 0; i < prefix_len >> 3; i++) {
if (address_data[i] != prefix_address_data[i])
return 0;
}
if (prefix_len % 8)
return prefix_address_data[i] ==
(address_data[i] & (0xff << (8 - prefix_len % 8)));
return 1;
}
int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
int* count_ptr) {
IP_ADAPTER_ADDRESSES* win_address_buf;
@ -755,26 +740,13 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
uv_interface_address_t* uv_address;
int count;
int is_vista_or_greater;
ULONG flags;
*addresses_ptr = NULL;
*count_ptr = 0;
is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0);
if (is_vista_or_greater) {
flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER;
} else {
/* We need at least XP SP1. */
if (!is_windows_version_or_greater(5, 1, 1, 0))
return UV_ENOTSUP;
flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX;
}
flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER;
/* Fetch the size of the adapters reported by windows, and then get the list
* itself. */
@ -938,37 +910,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
sa = unicast_address->Address.lpSockaddr;
/* XP has no OnLinkPrefixLength field. */
if (is_vista_or_greater) {
prefix_len =
((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength;
} else {
/* Prior to Windows Vista the FirstPrefix pointed to the list with
* single prefix for each IP address assigned to the adapter.
* Order of FirstPrefix does not match order of FirstUnicastAddress,
* so we need to find corresponding prefix.
*/
IP_ADAPTER_PREFIX* prefix;
prefix_len = 0;
for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) {
/* We want the longest matching prefix. */
if (prefix->Address.lpSockaddr->sa_family != sa->sa_family ||
prefix->PrefixLength <= prefix_len)
continue;
if (address_prefix_match(sa->sa_family, sa,
prefix->Address.lpSockaddr, prefix->PrefixLength)) {
prefix_len = prefix->PrefixLength;
}
}
/* If there is no matching prefix information, return a single-host
* subnet mask (e.g. 255.255.255.255 for IPv4).
*/
if (!prefix_len)
prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32;
}
prefix_len =
((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength;
memset(uv_address, 0, sizeof *uv_address);
@ -1093,8 +1036,8 @@ int uv_os_homedir(char* buffer, size_t* size) {
if (r != UV_ENOENT)
return r;
/* USERPROFILE is not set, so call uv__getpwuid_r() */
r = uv__getpwuid_r(&pwd);
/* USERPROFILE is not set, so call uv_os_get_passwd() */
r = uv_os_get_passwd(&pwd);
if (r != 0) {
return r;
@ -1181,17 +1124,6 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
}
void uv_os_free_passwd(uv_passwd_t* pwd) {
if (pwd == NULL)
return;
uv__free(pwd->username);
uv__free(pwd->homedir);
pwd->username = NULL;
pwd->homedir = NULL;
}
/*
* Converts a UTF-16 string into a UTF-8 one. The resulting string is
* null-terminated.
@ -1288,7 +1220,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) {
}
int uv__getpwuid_r(uv_passwd_t* pwd) {
static int uv__getpwuid_r(uv_passwd_t* pwd) {
HANDLE token;
wchar_t username[UNLEN + 1];
wchar_t *path;
@ -1366,6 +1298,16 @@ int uv_os_get_passwd(uv_passwd_t* pwd) {
}
int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) {
return UV_ENOTSUP;
}
int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
return UV_ENOTSUP;
}
int uv_os_environ(uv_env_item_t** envitems, int* count) {
wchar_t* env;
wchar_t* penv;
@ -1769,6 +1711,22 @@ int uv_os_uname(uv_utsname_t* buffer) {
RegCloseKey(registry_key);
if (r == ERROR_SUCCESS) {
/* Windows 11 shares dwMajorVersion with Windows 10
* this workaround tries to disambiguate that by checking
* if the dwBuildNumber is from Windows 11 releases (>= 22000).
*
* This workaround replaces the ProductName key value
* from "Windows 10 *" to "Windows 11 *" */
if (os_info.dwMajorVersion == 10 &&
os_info.dwBuildNumber >= 22000 &&
product_name_w_size >= ARRAY_SIZE(L"Windows 10")) {
/* If ProductName starts with "Windows 10" */
if (wcsncmp(product_name_w, L"Windows 10", ARRAY_SIZE(L"Windows 10") - 1) == 0) {
/* Bump 10 to 11 */
product_name_w[9] = '1';
}
}
version_size = WideCharToMultiByte(CP_UTF8,
0,
product_name_w,