From 9d72a742e3c35c13e24e69184a4befe5a7e84fef Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 9 Mar 2012 09:20:29 -0800 Subject: [PATCH] uv: upgrade 8c78cb4 --- deps/uv/config-mingw.mk | 2 +- deps/uv/include/uv.h | 2 +- deps/uv/src/unix/cygwin.c | 1 + deps/uv/src/unix/stream.c | 38 +++++- deps/uv/src/win/core.c | 1 + deps/uv/src/win/error.c | 2 + deps/uv/src/win/fs.c | 3 +- deps/uv/src/win/handle.c | 15 ++- deps/uv/src/win/pipe.c | 187 ++++++++++++++------------ deps/uv/src/win/process.c | 2 +- deps/uv/src/win/stream.c | 5 +- deps/uv/src/win/tcp.c | 27 +++- deps/uv/src/win/tty.c | 21 ++- deps/uv/src/win/udp.c | 3 + deps/uv/src/win/util.c | 11 +- deps/uv/src/win/winapi.h | 13 +- deps/uv/test/benchmark-pound.c | 4 +- deps/uv/test/benchmark-thread.c | 2 +- deps/uv/test/run-tests.c | 15 ++- deps/uv/test/runner-win.c | 2 +- deps/uv/test/runner.c | 5 +- deps/uv/test/test-counters-init.c | 30 ++--- deps/uv/test/test-cwd-and-chdir.c | 21 +-- deps/uv/test/test-fs.c | 66 ++++++++- deps/uv/test/test-ipc-send-recv.c | 208 +++++++++++++++++++++++++++++ deps/uv/test/test-ipc.c | 35 +++-- deps/uv/test/test-list.h | 25 ++++ deps/uv/test/test-ref.c | 76 ++++++++++- deps/uv/test/test-shutdown-close.c | 101 ++++++++++++++ deps/uv/test/test-tty.c | 64 +++++++-- deps/uv/uv.gyp | 2 + 31 files changed, 808 insertions(+), 181 deletions(-) create mode 100644 deps/uv/test/test-ipc-send-recv.c create mode 100644 deps/uv/test/test-shutdown-close.c diff --git a/deps/uv/config-mingw.mk b/deps/uv/config-mingw.mk index 12e9578397..48c6325ad8 100644 --- a/deps/uv/config-mingw.mk +++ b/deps/uv/config-mingw.mk @@ -34,7 +34,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 +RUNNER_LIBS=-lws2_32 -lpsapi -liphlpapi RUNNER_SRC=test/runner-win.c uv.a: $(WIN_OBJS) src/uv-common.o $(CARES_OBJS) diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 6c90a87183..e3a8b702ec 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -1388,7 +1388,7 @@ UV_EXTERN extern uint64_t uv_hrtime(void); /* - * Opens a shared library. The filename is in utf-8. On success, -1 is + * Opens a shared library. The filename is in utf-8. On success, -1 is returned * and the variable pointed by library receives a handle to the library. */ UV_EXTERN uv_err_t uv_dlopen(const char* filename, uv_lib_t* library); diff --git a/deps/uv/src/unix/cygwin.c b/deps/uv/src/unix/cygwin.c index a3d1736505..aef8d7672e 100644 --- a/deps/uv/src/unix/cygwin.c +++ b/deps/uv/src/unix/cygwin.c @@ -19,6 +19,7 @@ */ #include "uv.h" +#include "../uv-common.h" #include #include diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 111bbc2dec..b5ade16add 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -22,14 +22,17 @@ #include "uv.h" #include "internal.h" -#include -#include -#include +#include #include #include -#include +#include +#include -#include +#include +#include +#include +#include +#include static void uv__stream_connect(uv_stream_t*); @@ -513,6 +516,28 @@ static void uv__write_callbacks(uv_stream_t* stream) { } +static uv_handle_type uv__handle_type(int fd) { + struct sockaddr_storage ss; + socklen_t len; + + memset(&ss, 0, sizeof(ss)); + len = sizeof(ss); + + if (getsockname(fd, (struct sockaddr*)&ss, &len)) + return UV_UNKNOWN_HANDLE; + + switch (ss.ss_family) { + case AF_UNIX: + return UV_NAMED_PIPE; + case AF_INET: + case AF_INET6: + return UV_TCP; + } + + return UV_UNKNOWN_HANDLE; +} + + static void uv__read(uv_stream_t* stream) { uv_buf_t buf; ssize_t nread; @@ -633,7 +658,8 @@ static void uv__read(uv_stream_t* stream) { if (stream->accepted_fd >= 0) { - stream->read2_cb((uv_pipe_t*)stream, nread, buf, UV_TCP); + stream->read2_cb((uv_pipe_t*)stream, nread, buf, + uv__handle_type(stream->accepted_fd)); } else { stream->read2_cb((uv_pipe_t*)stream, nread, buf, UV_UNKNOWN_HANDLE); } diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index 8b44540a1a..accacc5a65 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -59,6 +59,7 @@ static void uv_init(void) { static void uv_loop_init(uv_loop_t* loop) { + loop->uv_ares_handles_ = NULL; /* Create an I/O completion port */ loop->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); if (loop->iocp == NULL) { diff --git a/deps/uv/src/win/error.c b/deps/uv/src/win/error.c index 4fc87349fb..1951fc3aea 100644 --- a/deps/uv/src/win/error.c +++ b/deps/uv/src/win/error.c @@ -91,6 +91,8 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case WSAEFAULT: return UV_EFAULT; case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH; case WSAEHOSTUNREACH: return UV_EHOSTUNREACH; + case ERROR_OPERATION_ABORTED: return UV_EINTR; + case WSAEINTR: return UV_EINTR; case ERROR_INVALID_DATA: return UV_EINVAL; case WSAEINVAL: return UV_EINVAL; case ERROR_CANT_RESOLVE_FILENAME: return UV_ELOOP; diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 9c112b80d2..507336eed4 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -547,7 +547,8 @@ static void fs__stat(uv_fs_t* req, const wchar_t* path) { req->stat.st_size = ((int64_t) info.nFileSizeHigh << 32) + (int64_t) info.nFileSizeLow; - req->stat.st_nlink = info.nNumberOfLinks; + req->stat.st_nlink = (info.nNumberOfLinks <= SHRT_MAX) ? + (short) info.nNumberOfLinks : SHRT_MAX; req->ptr = &req->stat; req->result = 0; diff --git a/deps/uv/src/win/handle.c b/deps/uv/src/win/handle.c index ba0af755fe..a1ff7275c1 100644 --- a/deps/uv/src/win/handle.c +++ b/deps/uv/src/win/handle.c @@ -27,9 +27,15 @@ uv_handle_type uv_guess_handle(uv_file file) { - HANDLE handle = (HANDLE) _get_osfhandle(file); + HANDLE handle; DWORD mode; + if (file < 0) { + return UV_UNKNOWN_HANDLE; + } + + handle = (HANDLE) _get_osfhandle(file); + switch (GetFileType(handle)) { case FILE_TYPE_CHAR: if (GetConsoleMode(handle, &mode)) { @@ -85,9 +91,10 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) { tcp = (uv_tcp_t*)handle; /* If we don't shutdown before calling closesocket, windows will */ /* silently discard the kernel send buffer and reset the connection. */ - if (!(tcp->flags & UV_HANDLE_SHUT)) { + if ((tcp->flags & UV_HANDLE_CONNECTION) && + !(tcp->flags & UV_HANDLE_SHUT)) { shutdown(tcp->socket, SD_SEND); - tcp->flags |= UV_HANDLE_SHUT; + tcp->flags |= UV_HANDLE_SHUTTING | UV_HANDLE_SHUT; } tcp->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); closesocket(tcp->socket); @@ -173,7 +180,7 @@ void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle) { void uv_process_endgames(uv_loop_t* loop) { uv_handle_t* handle; - while (loop->endgame_handles) { + while (loop->endgame_handles && loop->refs > 0) { handle = loop->endgame_handles; loop->endgame_handles = handle->endgame_next; diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index 54d90e0739..83220a23b0 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -98,59 +98,61 @@ static void uv_pipe_connection_init(uv_pipe_t* handle) { } -static int open_named_pipe(uv_pipe_t* handle) { - /* - * Assume that we have a duplex pipe first, so attempt to +static HANDLE open_named_pipe(WCHAR* name, DWORD* duplex_flags) { + HANDLE pipeHandle; + + /* + * Assume that we have a duplex pipe first, so attempt to * connect with GENERIC_READ | GENERIC_WRITE. */ - handle->handle = CreateFileW(handle->name, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, - NULL); - - if (handle->handle != INVALID_HANDLE_VALUE) { - return 0; + pipeHandle = CreateFileW(name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + if (pipeHandle != INVALID_HANDLE_VALUE) { + *duplex_flags = 0; + return pipeHandle; } - /* - * If the pipe is not duplex CreateFileW fails with + /* + * If the pipe is not duplex CreateFileW fails with * ERROR_ACCESS_DENIED. In that case try to connect * as a read-only or write-only. */ if (GetLastError() == ERROR_ACCESS_DENIED) { - handle->handle = CreateFileW(handle->name, - GENERIC_READ | FILE_WRITE_ATTRIBUTES, - 0, - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, - NULL); - - if (handle->handle != INVALID_HANDLE_VALUE) { - handle->flags |= UV_HANDLE_SHUT; - return 0; + pipeHandle = CreateFileW(name, + GENERIC_READ | FILE_WRITE_ATTRIBUTES, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if (pipeHandle != INVALID_HANDLE_VALUE) { + *duplex_flags = UV_HANDLE_SHUTTING; + return pipeHandle; } } if (GetLastError() == ERROR_ACCESS_DENIED) { - handle->handle = CreateFileW(handle->name, - GENERIC_WRITE | FILE_READ_ATTRIBUTES, - 0, - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, - NULL); - - if (handle->handle != INVALID_HANDLE_VALUE) { - handle->flags |= UV_HANDLE_EOF; - return 0; + pipeHandle = CreateFileW(name, + GENERIC_WRITE | FILE_READ_ATTRIBUTES, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if (pipeHandle != INVALID_HANDLE_VALUE) { + *duplex_flags = UV_HANDLE_EOF; + return pipeHandle; } } - return -1; + return INVALID_HANDLE_VALUE; } @@ -208,13 +210,18 @@ done: static int uv_set_pipe_handle(uv_loop_t* loop, uv_pipe_t* handle, - HANDLE pipeHandle) { + HANDLE pipeHandle, DWORD duplex_flags) { NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_MODE_INFORMATION mode_info; DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) { + /* If this returns ERROR_INVALID_PARAMETER we probably opened something */ + /* that is not a pipe. */ + if (GetLastError() == ERROR_INVALID_PARAMETER) { + SetLastError(WSAENOTSOCK); + } return -1; } @@ -242,6 +249,9 @@ static int uv_set_pipe_handle(uv_loop_t* loop, uv_pipe_t* handle, } } + handle->handle = pipeHandle; + handle->flags |= duplex_flags; + return 0; } @@ -276,11 +286,25 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { IO_STATUS_BLOCK io_status; FILE_PIPE_LOCAL_INFORMATION pipe_info; - if (handle->flags & UV_HANDLE_SHUTTING && - !(handle->flags & UV_HANDLE_SHUT) && + if ((handle->flags & UV_HANDLE_CONNECTION) && + handle->shutdown_req != NULL && handle->write_reqs_pending == 0) { req = handle->shutdown_req; + /* Clear the shutdown_req field so we don't go here again. */ + handle->shutdown_req = NULL; + + if (handle->flags & UV_HANDLE_CLOSING) { + /* Already closing. Cancel the shutdown. */ + if (req->cb) { + uv__set_sys_error(loop, WSAEINTR); + req->cb(req, -1); + } + uv_unref(loop); + DECREASE_PENDING_REQ_COUNT(handle); + return; + } + /* Try to avoid flushing the pipe buffer in the thread pool. */ nt_status = pNtQueryInformationFile(handle->handle, &io_status, @@ -295,13 +319,12 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { uv__set_sys_error(loop, pRtlNtStatusToDosError(nt_status)); req->cb(req, -1); } + uv_unref(loop); DECREASE_PENDING_REQ_COUNT(handle); return; } if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) { - handle->flags |= UV_HANDLE_SHUT; - /* Short-circuit, no need to call FlushFileBuffers. */ uv_insert_pending_req(loop, (uv_req_t*) req); return; @@ -312,8 +335,6 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { req, WT_EXECUTELONGFUNCTION); if (result) { - /* Mark the handle as shut now to avoid going through this again. */ - handle->flags |= UV_HANDLE_SHUT; return; } else { @@ -323,6 +344,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { uv__set_sys_error(loop, GetLastError()); req->cb(req, -1); } + uv_unref(loop); DECREASE_PENDING_REQ_COUNT(handle); return; } @@ -338,7 +360,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { free(handle->pending_socket_info); handle->pending_socket_info = NULL; } - + if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(handle->read_req.wait_handle); @@ -449,7 +471,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { goto error; } - if (uv_set_pipe_handle(loop, handle, handle->accept_reqs[0].pipeHandle)) { + if (uv_set_pipe_handle(loop, handle, handle->accept_reqs[0].pipeHandle, 0)) { uv__set_sys_error(loop, GetLastError()); goto error; } @@ -476,11 +498,12 @@ error: static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { - HANDLE pipeHandle = INVALID_HANDLE_VALUE; int errno; uv_loop_t* loop; uv_pipe_t* handle; uv_connect_t* req; + HANDLE pipeHandle = INVALID_HANDLE_VALUE; + DWORD duplex_flags; req = (uv_connect_t*) parameter; assert(req); @@ -493,7 +516,8 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { /* We wait for the pipe to become available with WaitNamedPipe. */ while (WaitNamedPipeW(handle->name, 30000)) { /* The pipe is now available, try to connect. */ - if (open_named_pipe(handle) == 0) { + pipeHandle = open_named_pipe(handle->name, &duplex_flags); + if (pipeHandle != INVALID_HANDLE_VALUE) { break; } @@ -501,8 +525,7 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { } if (pipeHandle != INVALID_HANDLE_VALUE && - !uv_set_pipe_handle(loop, handle, pipeHandle)) { - handle->handle = pipeHandle; + !uv_set_pipe_handle(loop, handle, pipeHandle, duplex_flags)) { SET_REQ_SUCCESS(req); } else { SET_REQ_ERROR(req, GetLastError()); @@ -519,8 +542,8 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb) { uv_loop_t* loop = handle->loop; int errno, nameSize; - - handle->handle = INVALID_HANDLE_VALUE; + HANDLE pipeHandle = INVALID_HANDLE_VALUE; + DWORD duplex_flags; uv_req_init(loop, (uv_req_t*) req); req->type = UV_CONNECT; @@ -539,7 +562,8 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, goto error; } - if (open_named_pipe(handle) != 0) { + pipeHandle = open_named_pipe(handle->name, &duplex_flags); + if (pipeHandle == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_PIPE_BUSY) { /* Wait for the server to make a pipe instance available. */ if (!QueueUserWorkItem(&pipe_connect_thread_proc, @@ -549,6 +573,7 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, goto error; } + uv_ref(loop); handle->reqs_pending++; return; @@ -558,15 +583,12 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, goto error; } - assert(handle->handle != INVALID_HANDLE_VALUE); + assert(pipeHandle != INVALID_HANDLE_VALUE); - /* Ensure that what we just opened is actually a pipe */ - if (!GetNamedPipeInfo(handle->handle, NULL, NULL, NULL, NULL)) { - errno = WSAENOTSOCK; - goto error; - } - - if (uv_set_pipe_handle(loop, (uv_pipe_t*)req->handle, handle->handle)) { + if (uv_set_pipe_handle(loop, + (uv_pipe_t*) req->handle, + pipeHandle, + duplex_flags)) { errno = GetLastError(); goto error; } @@ -574,6 +596,7 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, SET_REQ_SUCCESS(req); uv_insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; + uv_ref(loop); return; error: @@ -582,15 +605,15 @@ error: handle->name = NULL; } - if (handle->handle != INVALID_HANDLE_VALUE) { - CloseHandle(handle->handle); - handle->handle = INVALID_HANDLE_VALUE; + if (pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); } /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, errno); uv_insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; + uv_ref(loop); return; } @@ -617,6 +640,7 @@ void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err) { } if (handle->flags & UV_HANDLE_CONNECTION) { + handle->flags |= UV_HANDLE_SHUTTING; eof_timer_destroy(handle); } @@ -625,8 +649,6 @@ void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err) { CloseHandle(handle->handle); handle->handle = INVALID_HANDLE_VALUE; } - - handle->flags |= UV_HANDLE_SHUT; } @@ -649,7 +671,7 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, return; } - if (uv_set_pipe_handle(loop, handle, req->pipeHandle)) { + if (uv_set_pipe_handle(loop, handle, req->pipeHandle, 0)) { CloseHandle(req->pipeHandle); req->pipeHandle = INVALID_HANDLE_VALUE; SET_REQ_ERROR(req, GetLastError()); @@ -872,7 +894,7 @@ static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, GetLastError()); goto error; - } + } } else { memset(&req->overlapped, 0, sizeof(req->overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { @@ -1077,7 +1099,7 @@ static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req, ipc_frame.header.raw_data_length = bufs[0].len; } - /* + /* * Use the provided req if we're only doing a single write. * If we're doing multiple writes, use ipc_header_write_req to do * the first write, and then use the provided req for the second write. @@ -1085,7 +1107,7 @@ static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req, if (!(ipc_frame.header.flags & UV_IPC_RAW_DATA)) { ipc_header_req = req; } else { - /* + /* * Try to use the preallocated write req if it's available. * Otherwise allocate a new one. */ @@ -1129,10 +1151,7 @@ static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req, handle->write_queue_size += req->queued_bytes; } - if (handle->write_reqs_pending == 0) { - uv_ref(loop); - } - + uv_ref(loop); handle->reqs_pending++; handle->write_reqs_pending++; @@ -1187,10 +1206,7 @@ static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req, } } - if (handle->write_reqs_pending == 0) { - uv_ref(loop); - } - + uv_ref(loop); handle->reqs_pending++; handle->write_reqs_pending++; @@ -1365,7 +1381,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, /* Successful read */ if (handle->ipc) { assert(handle->remaining_ipc_rawdata_bytes >= bytes); - handle->remaining_ipc_rawdata_bytes = + handle->remaining_ipc_rawdata_bytes = handle->remaining_ipc_rawdata_bytes - bytes; if (handle->read2_cb) { handle->read2_cb(handle, bytes, buf, @@ -1446,15 +1462,12 @@ void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, uv_queue_non_overlapped_write(handle); } - if (handle->write_reqs_pending == 0) { - uv_unref(loop); - } - if (handle->write_reqs_pending == 0 && handle->flags & UV_HANDLE_SHUTTING) { uv_want_endgame(loop, (uv_handle_t*)handle); } + uv_unref(loop); DECREASE_PENDING_REQ_COUNT(handle); } @@ -1501,6 +1514,7 @@ void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, } } + uv_unref(loop); DECREASE_PENDING_REQ_COUNT(handle); } @@ -1525,6 +1539,7 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, req->cb(req, 0); } + uv_unref(loop); DECREASE_PENDING_REQ_COUNT(handle); } @@ -1540,6 +1555,7 @@ static void eof_timer_init(uv_pipe_t* pipe) { r = uv_timer_init(pipe->loop, pipe->eof_timer); assert(r == 0); /* timers can't fail */ pipe->eof_timer->data = pipe; + uv_unref(pipe->loop); } @@ -1602,6 +1618,7 @@ static void eof_timer_destroy(uv_pipe_t* pipe) { assert(pipe->flags && UV_HANDLE_CONNECTION); if (pipe->eof_timer) { + uv_ref(pipe->loop); uv_close((uv_handle_t*) pipe->eof_timer, eof_timer_close_cb); pipe->eof_timer = NULL; } @@ -1618,7 +1635,7 @@ void uv_pipe_open(uv_pipe_t* pipe, uv_file file) { HANDLE os_handle = (HANDLE)_get_osfhandle(file); if (os_handle == INVALID_HANDLE_VALUE || - uv_set_pipe_handle(pipe->loop, pipe, os_handle) == -1) { + uv_set_pipe_handle(pipe->loop, pipe, os_handle, 0) == -1) { return; } diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index 84781d6314..74744860a6 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -832,7 +832,7 @@ done: static int duplicate_std_handle(uv_loop_t* loop, DWORD id, HANDLE* dup) { HANDLE handle; HANDLE current_process = GetCurrentProcess(); - + handle = GetStdHandle(id); if (handle == NULL) { diff --git a/deps/uv/src/win/stream.c b/deps/uv/src/win/stream.c index ea7363ef03..d36bc682a8 100644 --- a/deps/uv/src/win/stream.c +++ b/deps/uv/src/win/stream.c @@ -47,6 +47,8 @@ void uv_connection_init(uv_stream_t* handle) { handle->read_req.wait_handle = INVALID_HANDLE_VALUE; handle->read_req.type = UV_READ; handle->read_req.data = handle; + + handle->shutdown_req = NULL; } @@ -169,6 +171,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { handle->flags |= UV_HANDLE_SHUTTING; handle->shutdown_req = req; handle->reqs_pending++; + uv_ref(loop); uv_want_endgame(loop, (uv_handle_t*)handle); @@ -194,5 +197,5 @@ int uv_is_readable(uv_stream_t* handle) { int uv_is_writable(uv_stream_t* handle) { - return !(handle->flags & UV_HANDLE_SHUT); + return !(handle->flags & UV_HANDLE_SHUTTING); } diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index 956053c6ff..ab656a1c1c 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -167,11 +167,13 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { uv_tcp_accept_t* req; if (handle->flags & UV_HANDLE_CONNECTION && - handle->flags & UV_HANDLE_SHUTTING && - !(handle->flags & UV_HANDLE_SHUT) && + handle->shutdown_req != NULL && handle->write_reqs_pending == 0) { - if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) { + if (handle->flags & UV_HANDLE_CLOSING) { + status = -1; + sys_error = WSAEINTR; + } else if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) { status = 0; handle->flags |= UV_HANDLE_SHUT; } else { @@ -185,6 +187,9 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { handle->shutdown_req->cb(handle->shutdown_req, status); } + handle->shutdown_req = NULL; + + uv_unref(loop); DECREASE_PENDING_REQ_COUNT(handle); return; } @@ -548,7 +553,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { if (server->processed_accepts >= uv_simultaneous_server_accepts) { server->processed_accepts = 0; - /* + /* * All previously queued accept requests are now processed. * We now switch to queueing just a single accept. */ @@ -639,10 +644,12 @@ int uv__tcp_connect(uv_connect_t* req, if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { /* Process the req without IOCP. */ handle->reqs_pending++; + uv_ref(loop); uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(success)) { /* The req will be processed with IOCP. */ handle->reqs_pending++; + uv_ref(loop); } else { uv__set_sys_error(loop, WSAGetLastError()); return -1; @@ -698,9 +705,11 @@ int uv__tcp_connect6(uv_connect_t* req, if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { handle->reqs_pending++; + uv_ref(loop); uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(success)) { handle->reqs_pending++; + uv_ref(loop); } else { uv__set_sys_error(loop, WSAGetLastError()); return -1; @@ -795,12 +804,14 @@ int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, handle->reqs_pending++; handle->write_reqs_pending++; uv_insert_pending_req(loop, (uv_req_t*) req); + uv_ref(loop); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* Request queued by the kernel. */ req->queued_bytes = uv_count_bufs(bufs, bufcnt); handle->reqs_pending++; handle->write_reqs_pending++; handle->write_queue_size += req->queued_bytes; + uv_ref(loop); } else { /* Send failed due to an error. */ uv__set_sys_error(loop, WSAGetLastError()); @@ -831,7 +842,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, err = GET_REQ_SOCK_ERROR(req); if (err == WSAECONNABORTED) { - /* + /* * Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with Unix. */ uv__set_error(loop, UV_ECONNRESET, err); @@ -900,7 +911,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, handle->read_cb((uv_stream_t*)handle, 0, buf); } else { if (err == WSAECONNABORTED) { - /* + /* * Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with Unix. */ uv__set_error(loop, UV_ECONNRESET, err); @@ -946,6 +957,7 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, } DECREASE_PENDING_REQ_COUNT(handle); + uv_unref(loop); } @@ -1020,6 +1032,7 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, } DECREASE_PENDING_REQ_COUNT(handle); + uv_unref(loop); } @@ -1086,7 +1099,7 @@ int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid, LPWSAPROTOCOL_INFOW protocol_info) { assert(!(handle->flags & UV_HANDLE_CONNECTION)); - /* + /* * We're about to share the socket with another process. Because * this is a listening socket, we assume that the other process will * be accepting connections on it. So, before sharing the socket diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index dd2fdbcabc..5bf90bec24 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -1683,6 +1683,7 @@ int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, handle->reqs_pending++; handle->write_reqs_pending++; + uv_ref(loop); req->queued_bytes = 0; @@ -1715,10 +1716,13 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, } DECREASE_PENDING_REQ_COUNT(handle); + uv_unref(loop); } void uv_tty_close(uv_tty_t* handle) { + handle->flags |= UV_HANDLE_SHUTTING; + uv_tty_read_stop(handle); CloseHandle(handle->handle); @@ -1729,17 +1733,22 @@ void uv_tty_close(uv_tty_t* handle) { void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { - if (handle->flags & UV_HANDLE_CONNECTION && - handle->flags & UV_HANDLE_SHUTTING && - !(handle->flags & UV_HANDLE_SHUT) && + if ((handle->flags && UV_HANDLE_CONNECTION) && + handle->shutdown_req != NULL && handle->write_reqs_pending == 0) { - handle->flags |= UV_HANDLE_SHUT; - /* TTY shutdown is really just a no-op */ if (handle->shutdown_req->cb) { - handle->shutdown_req->cb(handle->shutdown_req, 0); + if (handle->flags & UV_HANDLE_CLOSING) { + uv__set_sys_error(loop, WSAEINTR); + handle->shutdown_req->cb(handle->shutdown_req, -1); + } else { + handle->shutdown_req->cb(handle->shutdown_req, 0); + } } + handle->shutdown_req = NULL; + + uv_unref(loop); DECREASE_PENDING_REQ_COUNT(handle); return; } diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index ad05164dff..1964401e44 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -400,11 +400,13 @@ static int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], /* Request completed immediately. */ req->queued_bytes = 0; handle->reqs_pending++; + uv_ref(loop); uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* Request queued by the kernel. */ req->queued_bytes = uv_count_bufs(bufs, bufcnt); handle->reqs_pending++; + uv_ref(loop); } else { /* Send failed due to an error. */ uv__set_sys_error(loop, WSAGetLastError()); @@ -569,6 +571,7 @@ void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, } } + uv_unref(loop); DECREASE_PENDING_REQ_COUNT(handle); } diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c index 15618e928d..9f731ecfad 100644 --- a/deps/uv/src/win/util.c +++ b/deps/uv/src/win/util.c @@ -490,11 +490,13 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, unsigned long size = 0; IP_ADAPTER_ADDRESSES* adapter_addresses; IP_ADAPTER_ADDRESSES* adapter_address; - IP_ADAPTER_UNICAST_ADDRESS_XP* unicast_address; uv_interface_address_t* address; struct sockaddr* sock_addr; int length; char* name; + /* Use IP_ADAPTER_UNICAST_ADDRESS_XP to retain backwards compatibility */ + /* with Windows XP */ + IP_ADAPTER_UNICAST_ADDRESS_XP* unicast_address; if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size) != ERROR_BUFFER_OVERFLOW) { @@ -517,7 +519,8 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, for (adapter_address = adapter_addresses; adapter_address != NULL; adapter_address = adapter_address->Next) { - unicast_address = adapter_address->FirstUnicastAddress; + unicast_address = (IP_ADAPTER_UNICAST_ADDRESS_XP*) + adapter_address->FirstUnicastAddress; while (unicast_address) { (*count)++; unicast_address = unicast_address->Next; @@ -536,7 +539,8 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, adapter_address != NULL; adapter_address = adapter_address->Next) { name = NULL; - unicast_address = adapter_address->FirstUnicastAddress; + unicast_address = (IP_ADAPTER_UNICAST_ADDRESS_XP*) + adapter_address->FirstUnicastAddress; while (unicast_address) { sock_addr = unicast_address->Address.lpSockaddr; @@ -610,6 +614,7 @@ void uv_filetime_to_time_t(FILETIME* file_time, time_t* stat_time) { time.tm_hour = system_time.wHour; time.tm_min = system_time.wMinute; time.tm_sec = system_time.wSecond; + time.tm_isdst = -1; *stat_time = mktime(&time); } else { diff --git a/deps/uv/src/win/winapi.h b/deps/uv/src/win/winapi.h index c9552ed241..b63c02dce0 100644 --- a/deps/uv/src/win/winapi.h +++ b/deps/uv/src/win/winapi.h @@ -4323,10 +4323,17 @@ typedef NTSTATUS (NTAPI *sNtSetInformationFile) /* * Kernel32 headers */ -#define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 -#define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 +#ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS +# define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 +#endif + +#ifdef FILE_SKIP_SET_EVENT_ON_HANDLE +# define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 +#endif -#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 +#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY +# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 +#endif #ifdef __MINGW32__ typedef struct _OVERLAPPED_ENTRY { diff --git a/deps/uv/test/benchmark-pound.c b/deps/uv/test/benchmark-pound.c index 64f404ca94..1693c31c8b 100644 --- a/deps/uv/test/benchmark-pound.c +++ b/deps/uv/test/benchmark-pound.c @@ -107,7 +107,9 @@ static void connect_cb(uv_connect_t* req, int status) { if (status != 0) { #if DEBUG - fprintf(stderr, "connect error %s\n", uv_err_name(uv_last_error())); + fprintf(stderr, + "connect error %s\n", + uv_err_name(uv_last_error(uv_default_loop()))); #endif uv_close((uv_handle_t*)req->handle, close_cb); conns_failed++; diff --git a/deps/uv/test/benchmark-thread.c b/deps/uv/test/benchmark-thread.c index cc7fd938aa..b37a7fd6d0 100644 --- a/deps/uv/test/benchmark-thread.c +++ b/deps/uv/test/benchmark-thread.c @@ -25,7 +25,7 @@ #include #include -#define NUM_THREADS (100 * 1000) +#define NUM_THREADS (20 * 1000) static volatile int num_threads; diff --git a/deps/uv/test/run-tests.c b/deps/uv/test/run-tests.c index 4bc5258734..8f100233cf 100644 --- a/deps/uv/test/run-tests.c +++ b/deps/uv/test/run-tests.c @@ -86,7 +86,7 @@ static void ipc_on_connection(uv_stream_t* server, int status) { uv_tcp_t* conn; if (!connection_accepted) { - /* + /* * Accept the connection and close it. Also let the other * side know. */ @@ -121,7 +121,7 @@ static int ipc_helper(int listen_after_write) { * data is transfered over the channel. XXX edit this comment after handle * transfer is added. */ - + uv_write_t write_req; int r; uv_buf_t buf; @@ -131,8 +131,8 @@ static int ipc_helper(int listen_after_write) { uv_pipe_open(&channel, 0); - ASSERT(uv_is_readable((uv_stream_t*)&channel)); - ASSERT(uv_is_writable((uv_stream_t*)&channel)); + ASSERT(uv_is_readable((uv_stream_t*) &channel)); + ASSERT(uv_is_writable((uv_stream_t*) &channel)); r = uv_tcp_init(uv_default_loop(), &tcp_server); ASSERT(r == 0); @@ -208,7 +208,7 @@ static int stdio_over_pipes_helper() { uv_buf_t buf[ARRAY_SIZE(buffers)]; int r, i; uv_loop_t* loop = uv_default_loop(); - + ASSERT(UV_NAMED_PIPE == uv_guess_handle(0)); ASSERT(UV_NAMED_PIPE == uv_guess_handle(1)); @@ -271,6 +271,11 @@ static int maybe_run_test(int argc, char **argv) { return ipc_helper(1); } + if (strcmp(argv[1], "ipc_send_recv_helper") == 0) { + int ipc_send_recv_helper(void); /* See test-ipc-send-recv.c */ + return ipc_send_recv_helper(); + } + if (strcmp(argv[1], "stdio_over_pipes_helper") == 0) { return stdio_over_pipes_helper(); } diff --git a/deps/uv/test/runner-win.c b/deps/uv/test/runner-win.c index 6b6d500c44..d2a986a714 100644 --- a/deps/uv/test/runner-win.c +++ b/deps/uv/test/runner-win.c @@ -135,7 +135,7 @@ int process_start(char *name, char *part, process_info_t *p) { p->stdio_in = nul; p->stdio_out = file; p->process = pi.hProcess; - p->name = name; + p->name = part; return 0; diff --git a/deps/uv/test/runner.c b/deps/uv/test/runner.c index 8c7bd7f053..be790f7e78 100644 --- a/deps/uv/test/runner.c +++ b/deps/uv/test/runner.c @@ -186,7 +186,8 @@ out: process_terminate(&processes[i]); } - if (process_wait(processes, process_count - 1, -1) < 0) { + if (process_count > 0 && + process_wait(processes, process_count - 1, -1) < 0) { FATAL("process_wait failed"); } @@ -194,6 +195,8 @@ out: if (status != 0 || task->show_output) { if (status != 0) { LOGF("\n`%s` failed: %s\n", test, errmsg); + } else { + LOGF("\n"); } for (i = 0; i < process_count; i++) { diff --git a/deps/uv/test/test-counters-init.c b/deps/uv/test/test-counters-init.c index 423d349c3b..29e7acdf9f 100644 --- a/deps/uv/test/test-counters-init.c +++ b/deps/uv/test/test-counters-init.c @@ -92,21 +92,21 @@ static void create_cb(uv_fs_t* req) { TEST_IMPL(counters_init) { int r; - int eio_init_prev; - int req_init_prev; - int handle_init_prev; - int stream_init_prev; - int tcp_init_prev; - int udp_init_prev; - int pipe_init_prev; - int tty_init_prev; - int prepare_init_prev; - int check_init_prev; - int idle_init_prev; - int async_init_prev; - int timer_init_prev; - int process_init_prev; - int fs_event_init_prev; + uint64_t eio_init_prev; + uint64_t req_init_prev; + uint64_t handle_init_prev; + uint64_t stream_init_prev; + uint64_t tcp_init_prev; + uint64_t udp_init_prev; + uint64_t pipe_init_prev; + uint64_t tty_init_prev; + uint64_t prepare_init_prev; + uint64_t check_init_prev; + uint64_t idle_init_prev; + uint64_t async_init_prev; + uint64_t timer_init_prev; + uint64_t process_init_prev; + uint64_t fs_event_init_prev; /* req_init and eio_init test by uv_fs_open() */ unlink("test_file"); diff --git a/deps/uv/test/test-cwd-and-chdir.c b/deps/uv/test/test-cwd-and-chdir.c index deafdc9488..d72c26a80d 100644 --- a/deps/uv/test/test-cwd-and-chdir.c +++ b/deps/uv/test/test-cwd-and-chdir.c @@ -37,17 +37,20 @@ TEST_IMPL(cwd_and_chdir) { err = uv_cwd(buffer_orig, size); ASSERT(err.code == UV_OK); - last_slash = strrchr(buffer_orig, + /* Remove trailing slash unless at a root directory. */ #ifdef _WIN32 - '\\' -#else - '/' -#endif - ); - + last_slash = strrchr(buffer_orig, '\\'); ASSERT(last_slash); - - *last_slash = '\0'; + if (last_slash > buffer_orig && *(last_slash - 1) != ':') { + *last_slash = '\0'; + } +#else /* Unix */ + last_slash = strrchr(buffer_orig, '/'); + ASSERT(last_slash); + if (last_slash != buffer_orig) { + *last_slash = '\0'; + } +#endif err = uv_chdir(buffer_orig); ASSERT(err.code == UV_OK); diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index 619846d3ce..a8fe0abb97 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -115,7 +115,7 @@ void check_permission(const char* filename, int mode) { s = req.ptr; #ifdef _WIN32 - /* + /* * On Windows, chmod can only modify S_IWUSR (_S_IWRITE) bit, * so only testing for the specified flags. */ @@ -186,8 +186,14 @@ static void chown_cb(uv_fs_t* req) { static void chown_root_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_CHOWN); +#ifdef _WIN32 + /* On windows, chown is a no-op and always succeeds. */ + ASSERT(req->result == 0); +#else + /* On unix, chown'ing the root directory is not allowed. */ ASSERT(req->result == -1); ASSERT(req->errorno == UV_EPERM); +#endif chown_cb_count++; uv_fs_req_cleanup(req); } @@ -1218,7 +1224,7 @@ TEST_IMPL(fs_symlink) { */ return 0; } else if (uv_last_error(loop).sys_errno_ == ERROR_PRIVILEGE_NOT_HELD) { - /* + /* * Creating a symlink is only allowed when running elevated. * We pass the test and bail out early if we get ERROR_PRIVILEGE_NOT_HELD. */ @@ -1667,3 +1673,59 @@ TEST_IMPL(fs_rename_to_existing_file) { return 0; } + + +TEST_IMPL(fs_read_file_eof) { + int r; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_write(loop, &write_req, open_req1.result, test_buf, + sizeof(test_buf), -1, NULL); + ASSERT(r != -1); + ASSERT(write_req.result != -1); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(loop, &open_req1, "test_file", O_RDONLY, 0, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), -1, + NULL); + ASSERT(r != -1); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), + read_req.result, NULL); + ASSERT(r == 0); + ASSERT(read_req.result == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + /* Cleanup */ + unlink("test_file"); + + return 0; +} diff --git a/deps/uv/test/test-ipc-send-recv.c b/deps/uv/test/test-ipc-send-recv.c new file mode 100644 index 0000000000..96b47f0710 --- /dev/null +++ b/deps/uv/test/test-ipc-send-recv.c @@ -0,0 +1,208 @@ +/* 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 "task.h" + +#include +#include + +/* See test-ipc.ctx */ +void spawn_helper(uv_pipe_t* channel, + uv_process_t* process, + const char* helper); + +union handles { + uv_handle_t handle; + uv_stream_t stream; + uv_pipe_t pipe; + uv_tcp_t tcp; + uv_tty_t tty; +}; + +struct echo_ctx { + uv_pipe_t channel; + uv_write_t write_req; + uv_handle_type expected_type; + union handles send; + union handles recv; +}; + +static struct echo_ctx ctx; +static int num_recv_handles; + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + /* we're not actually reading anything so a small buffer is okay */ + static char buf[8]; + return uv_buf_init(buf, sizeof(buf)); +} + + +static void recv_cb(uv_pipe_t* handle, + ssize_t nread, + uv_buf_t buf, + uv_handle_type pending) { + int r; + + ASSERT(pending == ctx.expected_type); + ASSERT(handle == &ctx.channel); + ASSERT(nread >= 0); + + if (pending == UV_NAMED_PIPE) + r = uv_pipe_init(ctx.channel.loop, &ctx.recv.pipe, 0); + else if (pending == UV_TCP) + r = uv_tcp_init(ctx.channel.loop, &ctx.recv.tcp); + else + abort(); + ASSERT(r == 0); + + r = uv_accept((uv_stream_t*)&ctx.channel, &ctx.recv.stream); + ASSERT(r == 0); + + uv_close((uv_handle_t*)&ctx.channel, NULL); + uv_close(&ctx.send.handle, NULL); + uv_close(&ctx.recv.handle, NULL); + num_recv_handles++; +} + + +static int run_test(void) { + uv_process_t process; + uv_buf_t buf; + int r; + + spawn_helper(&ctx.channel, &process, "ipc_send_recv_helper"); + + buf = uv_buf_init(".", 1); + r = uv_write2(&ctx.write_req, + (uv_stream_t*)&ctx.channel, + &buf, 1, + &ctx.send.stream, + NULL); + ASSERT(r == 0); + + r = uv_read2_start((uv_stream_t*)&ctx.channel, alloc_cb, recv_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(num_recv_handles == 1); + + return 0; +} + + +TEST_IMPL(ipc_send_recv_pipe) { + int r; + + ctx.expected_type = UV_NAMED_PIPE; + + r = uv_pipe_init(uv_default_loop(), &ctx.send.pipe, 1); + ASSERT(r == 0); + + r = uv_pipe_bind(&ctx.send.pipe, TEST_PIPENAME); + ASSERT(r == 0); + + return run_test(); +} + + +TEST_IMPL(ipc_send_recv_tcp) { + int r; + + ctx.expected_type = UV_TCP; + + r = uv_tcp_init(uv_default_loop(), &ctx.send.tcp); + ASSERT(r == 0); + + r = uv_tcp_bind(&ctx.send.tcp, uv_ip4_addr("127.0.0.1", TEST_PORT)); + ASSERT(r == 0); + + return run_test(); +} + + +/* Everything here runs in a child process. */ + +static void write2_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + uv_close(&ctx.recv.handle, NULL); + uv_close((uv_handle_t*)&ctx.channel, NULL); +} + + +static void read2_cb(uv_pipe_t* handle, + ssize_t nread, + uv_buf_t buf, + uv_handle_type pending) { + int r; + + ASSERT(pending == UV_NAMED_PIPE || pending == UV_TCP); + ASSERT(handle == &ctx.channel); + ASSERT(nread >= 0); + + buf = uv_buf_init(".", 1); + + if (pending == UV_NAMED_PIPE) + r = uv_pipe_init(ctx.channel.loop, &ctx.recv.pipe, 0); + else if (pending == UV_TCP) + r = uv_tcp_init(ctx.channel.loop, &ctx.recv.tcp); + else + abort(); + ASSERT(r == 0); + + r = uv_accept((uv_stream_t*)handle, &ctx.recv.stream); + ASSERT(r == 0); + + r = uv_write2(&ctx.write_req, + (uv_stream_t*)&ctx.channel, + &buf, 1, + &ctx.recv.stream, + write2_cb); + ASSERT(r == 0); +} + + +/* stdin is a duplex channel over which a handle is sent. + * We receive it and send it back where it came from. + */ +int ipc_send_recv_helper(void) { + int r; + + memset(&ctx, 0, sizeof(ctx)); + + r = uv_pipe_init(uv_default_loop(), &ctx.channel, 1); + ASSERT(r == 0); + + uv_pipe_open(&ctx.channel, 0); + ASSERT(uv_is_readable((uv_stream_t*)&ctx.channel)); + ASSERT(uv_is_writable((uv_stream_t*)&ctx.channel)); + + r = uv_read2_start((uv_stream_t*)&ctx.channel, alloc_cb, read2_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + return 0; +} diff --git a/deps/uv/test/test-ipc.c b/deps/uv/test/test-ipc.c index 0908879510..ba122af902 100644 --- a/deps/uv/test/test-ipc.c +++ b/deps/uv/test/test-ipc.c @@ -25,9 +25,6 @@ #include #include -static char exepath[1024]; -static size_t exepath_size = 1024; -static char* args[3]; static uv_pipe_t channel; static uv_tcp_t tcp_server; @@ -184,31 +181,44 @@ static void on_read(uv_pipe_t* pipe, ssize_t nread, uv_buf_t buf, } -int run_ipc_test(const char* helper) { - int r; +void spawn_helper(uv_pipe_t* channel, + uv_process_t* process, + const char* helper) { uv_process_options_t options; - uv_process_t process; + size_t exepath_size; + char exepath[1024]; + char* args[3]; + int r; - r = uv_pipe_init(uv_default_loop(), &channel, 1); + r = uv_pipe_init(uv_default_loop(), channel, 1); ASSERT(r == 0); - ASSERT(channel.ipc); - - memset(&options, 0, sizeof(uv_process_options_t)); + ASSERT(channel->ipc); + exepath_size = sizeof(exepath); r = uv_exepath(exepath, &exepath_size); ASSERT(r == 0); + exepath[exepath_size] = '\0'; args[0] = exepath; args[1] = (char*)helper; args[2] = NULL; + + memset(&options, 0, sizeof(options)); options.file = exepath; options.args = args; options.exit_cb = exit_cb; - options.stdin_stream = &channel; + options.stdin_stream = channel; - r = uv_spawn(uv_default_loop(), &process, options); + r = uv_spawn(uv_default_loop(), process, options); ASSERT(r == 0); +} + + +static int run_ipc_test(const char* helper) { + uv_process_t process; + int r; + spawn_helper(&channel, &process, helper); uv_read2_start((uv_stream_t*)&channel, on_alloc, on_read); r = uv_run(uv_default_loop()); @@ -218,6 +228,7 @@ int run_ipc_test(const char* helper) { ASSERT(remote_conn_accepted == 1); ASSERT(read2_cb_called == 1); ASSERT(exit_cb_called == 1); + return 0; } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 38d39259e9..ed27cf12e7 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -24,6 +24,8 @@ TEST_DECLARE (tty) TEST_DECLARE (stdio_over_pipes) TEST_DECLARE (ipc_listen_before_write) TEST_DECLARE (ipc_listen_after_write) +TEST_DECLARE (ipc_send_recv_pipe) +TEST_DECLARE (ipc_send_recv_tcp) TEST_DECLARE (tcp_ping_pong) TEST_DECLARE (tcp_ping_pong_v6) TEST_DECLARE (pipe_ping_pong) @@ -61,6 +63,8 @@ TEST_DECLARE (pipe_connect_bad_name) TEST_DECLARE (pipe_connect_to_file) TEST_DECLARE (connection_fail) TEST_DECLARE (connection_fail_doesnt_auto_close) +TEST_DECLARE (shutdown_close_tcp) +TEST_DECLARE (shutdown_close_pipe) TEST_DECLARE (shutdown_eof) TEST_DECLARE (callback_stack) TEST_DECLARE (error_message) @@ -81,12 +85,16 @@ TEST_DECLARE (fs_event_ref) TEST_DECLARE (tcp_ref) TEST_DECLARE (tcp_ref2) TEST_DECLARE (tcp_ref3) +TEST_DECLARE (tcp_ref4) +TEST_DECLARE (tcp_ref5) TEST_DECLARE (udp_ref) TEST_DECLARE (udp_ref2) TEST_DECLARE (udp_ref3) TEST_DECLARE (pipe_ref) TEST_DECLARE (pipe_ref2) TEST_DECLARE (pipe_ref3) +TEST_DECLARE (pipe_ref4) +TEST_DECLARE (pipe_ref5) TEST_DECLARE (process_ref) TEST_DECLARE (async) TEST_DECLARE (get_currentexe) @@ -124,6 +132,7 @@ TEST_DECLARE (fs_utime) TEST_DECLARE (fs_futime) TEST_DECLARE (fs_file_open_append) TEST_DECLARE (fs_stat_missing_path) +TEST_DECLARE (fs_read_file_eof) TEST_DECLARE (fs_event_watch_dir) TEST_DECLARE (fs_event_watch_file) TEST_DECLARE (fs_event_watch_file_current_dir) @@ -167,6 +176,8 @@ TASK_LIST_START TEST_ENTRY (stdio_over_pipes) TEST_ENTRY (ipc_listen_before_write) TEST_ENTRY (ipc_listen_after_write) + TEST_ENTRY (ipc_send_recv_pipe) + TEST_ENTRY (ipc_send_recv_tcp) TEST_ENTRY (tcp_ping_pong) TEST_HELPER (tcp_ping_pong, tcp4_echo_server) @@ -217,6 +228,11 @@ TASK_LIST_START TEST_ENTRY (connection_fail) TEST_ENTRY (connection_fail_doesnt_auto_close) + TEST_ENTRY (shutdown_close_tcp) + TEST_HELPER (shutdown_close_tcp, tcp4_echo_server) + TEST_ENTRY (shutdown_close_pipe) + TEST_HELPER (shutdown_close_pipe, pipe_echo_server) + TEST_ENTRY (shutdown_eof) TEST_HELPER (shutdown_eof, tcp4_echo_server) @@ -243,6 +259,10 @@ TASK_LIST_START TEST_ENTRY (tcp_ref2) TEST_ENTRY (tcp_ref3) TEST_HELPER (tcp_ref3, tcp4_echo_server) + TEST_ENTRY (tcp_ref4) + TEST_HELPER (tcp_ref4, tcp4_echo_server) + TEST_ENTRY (tcp_ref5) + TEST_HELPER (tcp_ref5, tcp4_echo_server) TEST_ENTRY (udp_ref) TEST_ENTRY (udp_ref2) TEST_ENTRY (udp_ref3) @@ -251,6 +271,10 @@ TASK_LIST_START TEST_ENTRY (pipe_ref2) TEST_ENTRY (pipe_ref3) TEST_HELPER (pipe_ref3, pipe_echo_server) + TEST_ENTRY (pipe_ref4) + TEST_HELPER (pipe_ref4, pipe_echo_server) + TEST_ENTRY (pipe_ref5) + TEST_HELPER (pipe_ref5, pipe_echo_server) TEST_ENTRY (process_ref) TEST_ENTRY (loop_handles) @@ -308,6 +332,7 @@ TASK_LIST_START TEST_ENTRY (fs_futime) TEST_ENTRY (fs_symlink) TEST_ENTRY (fs_stat_missing_path) + TEST_ENTRY (fs_read_file_eof) TEST_ENTRY (fs_file_open_append) TEST_ENTRY (fs_event_watch_dir) TEST_ENTRY (fs_event_watch_file) diff --git a/deps/uv/test/test-ref.c b/deps/uv/test/test-ref.c index 2b8aabbc6f..cab3a70300 100644 --- a/deps/uv/test/test-ref.c +++ b/deps/uv/test/test-ref.c @@ -26,11 +26,39 @@ #include +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; +static uv_connect_t connect_req; + +static char buffer[32767]; + + static void fail_cb(void) { FATAL("fail_cb should not have been called"); } +static void write_unref_cb(uv_connect_t* req, int status) { + uv_buf_t buf = uv_buf_init(buffer, sizeof buffer); + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + uv_write(&write_req, req->handle, &buf, 1, (uv_write_cb) fail_cb); + uv_unref(uv_default_loop()); /* uv_write refs the loop */ +} + + + +static void shutdown_unref_cb(uv_connect_t* req, int status) { + ASSERT(req == &connect_req); + ASSERT(status == 0); + + uv_shutdown(&shutdown_req, req->handle, (uv_shutdown_cb) fail_cb); + uv_unref(uv_default_loop()); /* uv_shutdown refs the loop */ +} + + TEST_IMPL(ref) { uv_run(uv_default_loop()); return 0; @@ -142,10 +170,9 @@ TEST_IMPL(tcp_ref2) { TEST_IMPL(tcp_ref3) { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); - uv_connect_t req; uv_tcp_t h; uv_tcp_init(uv_default_loop(), &h); - uv_tcp_connect(&req, &h, addr, (uv_connect_cb)fail_cb); + uv_tcp_connect(&connect_req, &h, addr, (uv_connect_cb)fail_cb); uv_unref(uv_default_loop()); uv_unref(uv_default_loop()); /* connect req refs the loop */ uv_run(uv_default_loop()); @@ -153,6 +180,28 @@ TEST_IMPL(tcp_ref3) { } +TEST_IMPL(tcp_ref4) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_tcp_t h; + uv_tcp_init(uv_default_loop(), &h); + uv_tcp_connect(&connect_req, &h, addr, write_unref_cb); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(tcp_ref5) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_tcp_t h; + uv_tcp_init(uv_default_loop(), &h); + uv_tcp_connect(&connect_req, &h, addr, shutdown_unref_cb); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + TEST_IMPL(udp_ref) { uv_udp_t h; uv_udp_init(uv_default_loop(), &h); @@ -210,10 +259,9 @@ TEST_IMPL(pipe_ref2) { TEST_IMPL(pipe_ref3) { - uv_connect_t req; uv_pipe_t h; uv_pipe_init(uv_default_loop(), &h, 0); - uv_pipe_connect(&req, &h, TEST_PIPENAME, (uv_connect_cb)fail_cb); + uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, (uv_connect_cb)fail_cb); uv_unref(uv_default_loop()); uv_unref(uv_default_loop()); /* connect req refs the loop */ uv_run(uv_default_loop()); @@ -221,6 +269,26 @@ TEST_IMPL(pipe_ref3) { } +TEST_IMPL(pipe_ref4) { + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, write_unref_cb); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(pipe_ref5) { + uv_pipe_t h; + uv_pipe_init(uv_default_loop(), &h, 0); + uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, shutdown_unref_cb); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + TEST_IMPL(process_ref) { /* spawn_helper4 blocks indefinitely. */ char *argv[] = { NULL, "spawn_helper4", NULL }; diff --git a/deps/uv/test/test-shutdown-close.c b/deps/uv/test/test-shutdown-close.c new file mode 100644 index 0000000000..864f7cecae --- /dev/null +++ b/deps/uv/test/test-shutdown-close.c @@ -0,0 +1,101 @@ +/* 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. + */ + +/* + * These tests verify that the uv_shutdown callback is always made, even when + * it is immediately followed by an uv_close call. + */ + +#include "uv.h" +#include "task.h" + + +static uv_shutdown_t shutdown_req; +static uv_connect_t connect_req; + +static int connect_cb_called = 0; +static int shutdown_cb_called = 0; +static int close_cb_called = 0; + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(req == &shutdown_req); + ASSERT(status == 0 || + (status == -1 && uv_last_error(uv_default_loop()).code == UV_EINTR)); + shutdown_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + int r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); + ASSERT(r == 0); + uv_close((uv_handle_t*) req->handle, close_cb); + + connect_cb_called++; +} + + +TEST_IMPL(shutdown_close_tcp) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_tcp_t h; + int r; + + r = uv_tcp_init(uv_default_loop(), &h); + ASSERT(r == 0); + r = uv_tcp_connect(&connect_req, &h, addr, connect_cb); + ASSERT(r == 0); + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(connect_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(shutdown_close_pipe) { + uv_pipe_t h; + int r; + + r = uv_pipe_init(uv_default_loop(), &h, 0); + ASSERT(r == 0); + uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_cb); + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(connect_cb_called == 1); + ASSERT(shutdown_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/deps/uv/test/test-tty.c b/deps/uv/test/test-tty.c index 1e3e1f280c..ded59c9f77 100644 --- a/deps/uv/test/test-tty.c +++ b/deps/uv/test/test-tty.c @@ -22,23 +22,64 @@ #include "uv.h" #include "task.h" +#ifdef _WIN32 +# include +# include +#else /* Unix */ +# include +# include +#endif + + TEST_IMPL(tty) { int r, width, height; - uv_tty_t tty; + int ttyin_fd, ttyout_fd; + uv_tty_t tty_in, tty_out; uv_loop_t* loop = uv_default_loop(); + /* Make sure we have an FD that refers to a tty */ +#ifdef _WIN32 + HANDLE handle; + handle = CreateFileA("conin$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + ASSERT(handle != INVALID_HANDLE_VALUE); + ttyin_fd = _open_osfhandle((intptr_t) handle, 0); + + handle = CreateFileA("conout$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + ASSERT(handle != INVALID_HANDLE_VALUE); + ttyout_fd = _open_osfhandle((intptr_t) handle, 0); + +#else /* unix */ + ttyin_fd = open("/dev/tty", O_RDONLY, 0); + ttyout_fd = open("/dev/tty", O_WRONLY, 0); +#endif + + ASSERT(ttyin_fd >= 0); + ASSERT(ttyout_fd >= 0); + ASSERT(UV_UNKNOWN_HANDLE == uv_guess_handle(-1)); - /* - * Not necessarily a problem if this assert goes off. E.G you are piping - * this test to a file. 0 == stdin. - */ - ASSERT(UV_TTY == uv_guess_handle(0)); + ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); + ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); + + r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); + ASSERT(r == 0); - r = uv_tty_init(uv_default_loop(), &tty, 0, 1); + r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 2); ASSERT(r == 0); - r = uv_tty_get_winsize(&tty, &width, &height); + r = uv_tty_get_winsize(&tty_out, &width, &height); ASSERT(r == 0); printf("width=%d height=%d\n", width, height); @@ -51,16 +92,17 @@ TEST_IMPL(tty) { ASSERT(height > 10); /* Turn on raw mode. */ - r = uv_tty_set_mode(&tty, 1); + r = uv_tty_set_mode(&tty_in, 1); ASSERT(r == 0); /* Turn off raw mode. */ - r = uv_tty_set_mode(&tty, 0); + r = uv_tty_set_mode(&tty_in, 0); ASSERT(r == 0); /* TODO check the actual mode! */ - uv_close((uv_handle_t*)&tty, NULL); + uv_close((uv_handle_t*) &tty_in, NULL); + uv_close((uv_handle_t*) &tty_out, NULL); uv_run(loop); diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 9a828305ae..6b46b38a68 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -309,6 +309,7 @@ 'test/test-hrtime.c', 'test/test-idle.c', 'test/test-ipc.c', + 'test/test-ipc-send-recv.c', 'test/test-list.h', 'test/test-loop-handles.c', 'test/test-multiple-listen.c', @@ -319,6 +320,7 @@ 'test/test-platform-output.c', 'test/test-process-title.c', 'test/test-ref.c', + 'test/test-shutdown-close.c', 'test/test-shutdown-eof.c', 'test/test-spawn.c', 'test/test-stdio-over-pipes.c',