From 89b14fc0ff4da52ec112e0af4d8e7d87bfe5d637 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Mon, 1 Aug 2011 20:26:26 -0700 Subject: [PATCH] Upgrade libuv to 2e9a743 --- deps/uv/config-mingw.mk | 2 +- deps/uv/include/uv-win.h | 1 + deps/uv/msvs/libuv-test.vcxproj | 2 +- deps/uv/src/win/internal.h | 1 - deps/uv/src/win/pipe.c | 67 ++++++++---------- deps/uv/src/win/process.c | 151 +++++++++++++++++++++++++++------------- deps/uv/test/test-list.h | 6 ++ deps/uv/test/test-spawn.c | 44 ++++++++++++ 8 files changed, 185 insertions(+), 89 deletions(-) diff --git a/deps/uv/config-mingw.mk b/deps/uv/config-mingw.mk index 242bba9..abd244e 100644 --- a/deps/uv/config-mingw.mk +++ b/deps/uv/config-mingw.mk @@ -33,7 +33,7 @@ WIN_OBJS=$(WIN_SRCS:.c=.o) RUNNER_CFLAGS=$(CFLAGS) -D_GNU_SOURCE # Need _GNU_SOURCE for strdup? RUNNER_LINKFLAGS=$(LINKFLAGS) -RUNNER_LIBS=-lws2_32 -lrpcrt4 -lole32 +RUNNER_LIBS=-lws2_32 RUNNER_SRC=test/runner-win.c uv.a: $(WIN_OBJS) src/uv-common.o src/uv-eio.o src/eio/eio.o $(CARES_OBJS) diff --git a/deps/uv/include/uv-win.h b/deps/uv/include/uv-win.h index ec2429a..622bbea 100644 --- a/deps/uv/include/uv-win.h +++ b/deps/uv/include/uv-win.h @@ -178,6 +178,7 @@ typedef struct uv_buf_t { HANDLE child_pipe; \ } stdio_pipes[3]; \ int exit_signal; \ + DWORD spawn_errno; \ HANDLE wait_handle; \ HANDLE process_handle; \ HANDLE close_handle; diff --git a/deps/uv/msvs/libuv-test.vcxproj b/deps/uv/msvs/libuv-test.vcxproj index b91de56..44b138b 100644 --- a/deps/uv/msvs/libuv-test.vcxproj +++ b/deps/uv/msvs/libuv-test.vcxproj @@ -84,7 +84,7 @@ MachineX86 true Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;RpcRT4.Lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index e40ef3b..3783642 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -166,7 +166,6 @@ int uv_pipe_init_with_handle(uv_pipe_t* handle, HANDLE pipeHandle); int uv_stdio_pipe_server(uv_pipe_t* handle, DWORD access, char* name, size_t nameSize); void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err); void uv_pipe_endgame(uv_pipe_t* handle); -int uv_unique_pipe_name(char* name, size_t size); int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); int uv_pipe_accept(uv_pipe_t* server, uv_pipe_t* client); diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index b676324..1eb4973 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -32,29 +32,8 @@ static char uv_zero_[] = ""; -int uv_unique_pipe_name(char* name, size_t size) { - unsigned char* guid_str = NULL; - GUID guid; - int err; - - if (CoCreateGuid(&guid) != S_OK) { - err = -1; - goto done; - } - - if (UuidToStringA(&guid, &guid_str) != ERROR_SUCCESS) { - err = -1; - goto done; - } - - _snprintf(name, size, "\\\\.\\pipe\\uv\\%s", guid_str); - err = 0; - -done: - if (guid_str) { - RpcStringFreeA(&guid_str); - } - return err; +static void uv_unique_pipe_name(char* ptr, char* name, size_t size) { + _snprintf(name, size, "\\\\.\\pipe\\uv\\%p-%d", ptr, GetCurrentProcessId()); } @@ -92,26 +71,36 @@ int uv_pipe_init_with_handle(uv_pipe_t* handle, HANDLE pipeHandle) { int uv_stdio_pipe_server(uv_pipe_t* handle, DWORD access, char* name, size_t nameSize) { HANDLE pipeHandle; + int errno; int err; + char* ptr = (char*)handle; - err = uv_unique_pipe_name(name, nameSize); - if (err) { - goto done; - } + while (TRUE) { + uv_unique_pipe_name(ptr, name, nameSize); - pipeHandle = CreateNamedPipeA(name, - access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - 1, - 65536, - 65536, - 0, - NULL); + pipeHandle = CreateNamedPipeA(name, + access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 1, + 65536, + 65536, + 0, + NULL); - if (pipeHandle == INVALID_HANDLE_VALUE) { - uv_set_sys_error(GetLastError()); - err = -1; - goto done; + if (pipeHandle != INVALID_HANDLE_VALUE) { + /* No name collisions. We're done. */ + break; + } + + errno = GetLastError(); + if (errno != ERROR_PIPE_BUSY && errno != ERROR_ACCESS_DENIED) { + uv_set_sys_error(errno); + err = -1; + goto done; + } + + /* Pipe name collision. Increment the pointer and try again. */ + ptr++; } if (CreateIoCompletionPort(pipeHandle, diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index 6ad221f..5ce2d60 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -496,16 +496,16 @@ wchar_t* make_program_env(char** env_block) { } -/* - * Called on Windows thread-pool thread to indicate that - * a child process has exited. +/* + * Called on Windows thread-pool thread to indicate that + * a child process has exited. */ static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { uv_process_t* process = (uv_process_t*)data; - + assert(didTimeout == FALSE); assert(process); - + memset(&process->exit_req.overlapped, 0, sizeof(process->exit_req.overlapped)); /* Post completed */ @@ -518,13 +518,13 @@ static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { } -/* - * Called on Windows thread-pool thread to indicate that - * UnregisterWaitEx has completed. +/* + * Called on Windows thread-pool thread to indicate that + * UnregisterWaitEx has completed. */ static void CALLBACK close_wait_callback(void* data, BOOLEAN didTimeout) { uv_process_t* process = (uv_process_t*)data; - + assert(didTimeout == FALSE); assert(process); @@ -540,6 +540,54 @@ static void CALLBACK close_wait_callback(void* data, BOOLEAN didTimeout) { } +/* + * Called on windows thread pool when CreateProcess failed. It writes an error + * message to the process' intended stderr and then posts a PROCESS_EXIT + * packet to the completion port. + */ +static DWORD WINAPI spawn_failure(void* data) { + char syscall[] = "CreateProcessW: "; + char unknown[] = "unknown error\n"; + uv_process_t* process = (uv_process_t*) data; + HANDLE child_stderr = process->stdio_pipes[2].child_pipe; + char* buf = NULL; + DWORD count, written; + + WriteFile(child_stderr, syscall, sizeof(syscall) - 1, &written, NULL); + + count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + process->spawn_errno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &buf, + 0, + NULL); + + if (buf != NULL && count > 0) { + WriteFile(child_stderr, buf, count, &written, NULL); + LocalFree(buf); + } else { + WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL); + } + + FlushFileBuffers(child_stderr); + + memset(&process->exit_req.overlapped, 0, sizeof(process->exit_req.overlapped)); + + /* Post completed */ + if (!PostQueuedCompletionStatus(LOOP->iocp, + 0, + 0, + &process->exit_req.overlapped)) { + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); + } + + return 0; +} + + /* Called on main thread after a child process has exited. */ void uv_process_proc_exit(uv_process_t* handle) { int i; @@ -559,15 +607,18 @@ void uv_process_proc_exit(uv_process_t* handle) { handle->wait_handle = INVALID_HANDLE_VALUE; } - /* Clean-up the process handle. */ if (handle->process_handle != INVALID_HANDLE_VALUE) { /* Get the exit code. */ if (!GetExitCodeProcess(handle->process_handle, &exit_code)) { - exit_code = 1; + exit_code = 127; } + /* Clean-up the process handle. */ CloseHandle(handle->process_handle); handle->process_handle = INVALID_HANDLE_VALUE; + } else { + /* The process never even started in the first place. */ + exit_code = 127; } /* Fire the exit callback. */ @@ -618,10 +669,10 @@ static int uv_create_stdio_pipe_pair(uv_pipe_t* server_pipe, HANDLE* child_pipe, char pipe_name[64]; DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; - if (server_pipe->type != UV_NAMED_PIPE) { + if (server_pipe->type != UV_NAMED_PIPE) { uv_set_error(UV_EINVAL, 0); err = -1; - goto done; + goto done; } /* Create server pipe handle. */ @@ -681,13 +732,13 @@ done: int uv_spawn(uv_process_t* process, uv_process_options_t options) { - int err, i; + int err = 0, i; wchar_t* path; int size; wchar_t* application_path, *application, *arguments, *env, *cwd; STARTUPINFOW startup; PROCESS_INFORMATION info; - + uv_process_init(process); process->exit_cb = options.exit_cb; @@ -721,15 +772,15 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) { GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t)); path[size - 1] = L'\0'; - application_path = search_path(application, + application_path = search_path(application, cwd, path, DEFAULT_PATH_EXT); if (!application_path) { - uv_set_error(UV_EINVAL, 0); - err = -1; - goto done; + /* CreateProcess will fail, but this allows us to pass this error to */ + /* the user asynchronously. */ + application_path = application; } /* Create stdio pipes. */ @@ -771,39 +822,45 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) { startup.hStdOutput = process->stdio_pipes[1].child_pipe; startup.hStdError = process->stdio_pipes[2].child_pipe; - if (!CreateProcessW(application_path, - arguments, - NULL, - NULL, - 1, - CREATE_UNICODE_ENVIRONMENT, - env, - cwd, - &startup, - &info)) { - uv_set_sys_error(GetLastError()); - err = -1; - goto done; - } + if (CreateProcessW(application_path, + arguments, + NULL, + NULL, + 1, + CREATE_UNICODE_ENVIRONMENT, + env, + cwd, + &startup, + &info)) { + /* Spawn succeeded */ + process->process_handle = info.hProcess; + process->pid = info.dwProcessId; + + /* Setup notifications for when the child process exits. */ + if (!RegisterWaitForSingleObject(&process->wait_handle, process->process_handle, + exit_wait_callback, (void*)process, INFINITE, + WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { + uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); + } - process->process_handle = info.hProcess; - process->pid = info.dwProcessId; - - /* Setup notifications for when the child process exits. */ - if (!RegisterWaitForSingleObject(&process->wait_handle, process->process_handle, - exit_wait_callback, (void*)process, INFINITE, - WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { - uv_set_sys_error(GetLastError()); - err = -1; - goto done; - } + CloseHandle(info.hThread); - CloseHandle(info.hThread); - err = 0; + } else { + /* CreateProcessW failed, but this failure should be delivered */ + /* asynchronously to retain unix compatibility. So pretent spawn */ + /* succeeded, and start a thread instead that prints an error */ + /* to the child's intended stderr. */ + process->spawn_errno = GetLastError(); + if (!QueueUserWorkItem(spawn_failure, process, WT_EXECUTEDEFAULT)) { + uv_fatal_error(GetLastError(), "QueueUserWorkItem"); + } + } done: - free(application_path); free(application); + if (application_path != application) { + free(application_path); + } free(arguments); free(cwd); free(env); diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index c4a4e43..ebfdeb6 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -67,6 +67,9 @@ TEST_DECLARE (spawn_exit_code) TEST_DECLARE (spawn_stdout) TEST_DECLARE (spawn_stdin) TEST_DECLARE (spawn_and_kill) +#ifdef _WIN32 +TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) +#endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) HELPER_DECLARE (pipe_echo_server) @@ -148,6 +151,9 @@ TASK_LIST_START TEST_ENTRY (spawn_stdout) TEST_ENTRY (spawn_stdin) TEST_ENTRY (spawn_and_kill) +#ifdef _WIN32 + TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) +#endif #if 0 /* These are for testing the test runner. */ diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index 2ab6e6c..6378aab 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -226,3 +226,47 @@ TEST_IMPL(spawn_and_kill) { return 0; } + +#ifdef _WIN32 +TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { + int r; + uv_pipe_t out; + char name[64]; + HANDLE pipe_handle; + + uv_init(); + + init_process_options("spawn_helper2", exit_cb); + + uv_pipe_init(&out); + options.stdout_stream = &out; + + /* Create a pipe that'll cause a collision. */ + _snprintf(name, sizeof(name), "\\\\.\\pipe\\uv\\%p-%d", &out, GetCurrentProcessId()); + pipe_handle = CreateNamedPipeA(name, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 10, + 65536, + 65536, + 0, + NULL); + ASSERT(pipe_handle != INVALID_HANDLE_VALUE); + + r = uv_spawn(&process, options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0 || strcmp("hello world\r\n", output) == 0); + + return 0; +} +#endif \ No newline at end of file -- 2.7.4