diff options
author | isaacs <i@izs.me> | 2012-09-17 15:36:05 -0700 |
---|---|---|
committer | isaacs <i@izs.me> | 2012-09-17 15:36:15 -0700 |
commit | b788c5e77b564183826a4ccb69ec9a43a758a54a (patch) | |
tree | 8916ff311d3a7a5ae41022b9c8c198e78e1d55c3 | |
parent | 7144be70db2e6ce337d0f6ec47a28a06802d1c7a (diff) | |
download | node-b788c5e77b564183826a4ccb69ec9a43a758a54a.tar.gz |
uv: Upgrade to 778144f0
joyent/libuv@778144f0b5bc14fe55d79854d0a67124ceaa4b22
-rw-r--r-- | deps/uv/include/uv-private/uv-darwin.h | 3 | ||||
-rw-r--r-- | deps/uv/include/uv-private/uv-unix.h | 3 | ||||
-rw-r--r-- | deps/uv/include/uv.h | 21 | ||||
-rw-r--r-- | deps/uv/src/unix/fsevents.c | 56 | ||||
-rw-r--r-- | deps/uv/src/unix/kqueue.c | 6 | ||||
-rw-r--r-- | deps/uv/src/unix/linux/inotify.c | 3 | ||||
-rw-r--r-- | deps/uv/src/unix/pipe.c | 8 | ||||
-rw-r--r-- | deps/uv/src/unix/stream.c | 47 | ||||
-rw-r--r-- | deps/uv/src/unix/sunos.c | 2 | ||||
-rw-r--r-- | deps/uv/src/unix/tcp.c | 26 | ||||
-rw-r--r-- | deps/uv/src/unix/udp.c | 68 | ||||
-rw-r--r-- | deps/uv/src/win/fs-event.c | 3 | ||||
-rw-r--r-- | deps/uv/src/win/pipe.c | 6 | ||||
-rw-r--r-- | deps/uv/src/win/tcp.c | 15 | ||||
-rw-r--r-- | deps/uv/src/win/udp.c | 22 | ||||
-rw-r--r-- | deps/uv/test/benchmark-list.h | 8 | ||||
-rw-r--r-- | deps/uv/test/benchmark-multi-accept.c | 436 | ||||
-rw-r--r-- | deps/uv/test/dns-server.c | 5 | ||||
-rw-r--r-- | deps/uv/test/test-fs-event.c | 3 | ||||
-rw-r--r-- | deps/uv/test/test-list.h | 8 | ||||
-rw-r--r-- | deps/uv/test/test-tcp-open.c | 175 | ||||
-rw-r--r-- | deps/uv/test/test-udp-open.c | 154 | ||||
-rw-r--r-- | deps/uv/uv.gyp | 3 |
23 files changed, 982 insertions, 99 deletions
diff --git a/deps/uv/include/uv-private/uv-darwin.h b/deps/uv/include/uv-private/uv-darwin.h index 397c6a97a..4d68d0132 100644 --- a/deps/uv/include/uv-private/uv-darwin.h +++ b/deps/uv/include/uv-private/uv-darwin.h @@ -41,6 +41,9 @@ ev_io event_watcher; \ int fflags; \ int fd; \ + char* realpath; \ + int realpath_len; \ + int cf_flags; \ void* cf_eventstream; \ uv_async_t* cf_cb; \ ngx_queue_t cf_events; \ diff --git a/deps/uv/include/uv-private/uv-unix.h b/deps/uv/include/uv-private/uv-unix.h index 91ffbe4c9..71aee366c 100644 --- a/deps/uv/include/uv-private/uv-unix.h +++ b/deps/uv/include/uv-private/uv-unix.h @@ -185,8 +185,7 @@ typedef struct { int fd; \ UV_STREAM_PRIVATE_PLATFORM_FIELDS \ -#define UV_TCP_PRIVATE_FIELDS \ - uv_idle_t* idle_handle; /* for UV_TCP_SINGLE_ACCEPT handles */ \ +#define UV_TCP_PRIVATE_FIELDS /* empty */ #define UV_UDP_PRIVATE_FIELDS \ int fd; \ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 582e003e9..72da68d68 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -599,6 +599,11 @@ struct uv_tcp_s { UV_EXTERN int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle); +/* + * Opens an existing file descriptor or SOCKET as a tcp handle. + */ +UV_EXTERN int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock); + /* Enable/disable Nagle's algorithm. */ UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable); @@ -704,6 +709,11 @@ struct uv_udp_send_s { UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle); /* + * Opens an existing file descriptor or SOCKET as a udp handle. + */ +UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock); + +/* * Bind to a IPv4 address and port. * * Arguments: @@ -940,7 +950,7 @@ UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc); /* * Opens an existing file descriptor or HANDLE as a pipe. */ -UV_EXTERN void uv_pipe_open(uv_pipe_t*, uv_file file); +UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file); UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name); @@ -1665,7 +1675,14 @@ enum uv_fs_event_flags { * regular interval. * This flag is currently not implemented yet on any backend. */ - UV_FS_EVENT_STAT = 2 + UV_FS_EVENT_STAT = 2, + + /* + * By default, event watcher, when watching directory, is not registering + * (is ignoring) changes in it's subdirectories. + * This flag will override this behaviour on platforms that support it. + */ + UV_FS_EVENT_RECURSIVE = 3 }; diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c index 1a7e06e46..17dd11afa 100644 --- a/deps/uv/src/unix/fsevents.c +++ b/deps/uv/src/unix/fsevents.c @@ -66,8 +66,13 @@ void uv__fsevents_cb(uv_async_t* cb, int status) { handle = cb->data; UV__FSEVENTS_WALK(handle, { - if (handle->fd != -1) + if (handle->fd != -1) { +#ifdef MAC_OS_X_VERSION_10_7 handle->cb(handle, event->path, event->events, 0); +#else + handle->cb(handle, NULL, event->events, 0); +#endif /* MAC_OS_X_VERSION_10_7 */ + } }) if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 && handle->fd == -1) @@ -84,6 +89,8 @@ void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, size_t i; int len; char** paths; + char* path; + char* pos; uv_fs_event_t* handle; uv__fsevents_event_t* event; ngx_queue_t add_list; @@ -99,17 +106,50 @@ void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, kFSEventStreamEventFlagEventIdsWrapped | kFSEventStreamEventFlagHistoryDone | kFSEventStreamEventFlagMount | - kFSEventStreamEventFlagUnmount)) { + kFSEventStreamEventFlagUnmount | + kFSEventStreamEventFlagRootChanged)) { continue; } /* TODO: Report errors */ - len = strlen(paths[i]); + path = paths[i]; + len = strlen(path); + + /* Remove absolute path prefix */ + if (strstr(path, handle->realpath) == path) { + path += handle->realpath_len; + len -= handle->realpath_len; + + /* Skip back slash */ + if (*path != 0) { + path++; + len--; + } + } + +#ifdef MAC_OS_X_VERSION_10_7 + /* Ignore events with path equal to directory itself */ + if (len == 0) + continue; +#endif /* MAC_OS_X_VERSION_10_7 */ + + /* Do not emit events from subdirectories (without option set) */ + pos = strchr(path, '/'); + if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && + pos != NULL && + pos != path + 1) + continue; + +#ifndef MAC_OS_X_VERSION_10_7 + path = ""; + len = 0; +#endif /* MAC_OS_X_VERSION_10_7 */ + event = malloc(sizeof(*event) + len); if (event == NULL) break; - memcpy(event->path, paths[i], len + 1); + memcpy(event->path, path, len + 1); if (eventFlags[i] & kFSEventStreamEventFlagItemModified) event->events = UV_CHANGE; @@ -153,6 +193,11 @@ int uv__fsevents_init(uv_fs_event_t* handle) { ctx.release = NULL; ctx.copyDescription = NULL; + /* Get absolute path to file */ + handle->realpath = realpath(handle->filename, NULL); + if (handle->realpath != NULL) + handle->realpath_len = strlen(handle->realpath); + /* Initialize paths array */ path = CFStringCreateWithCString(NULL, handle->filename, @@ -220,6 +265,9 @@ int uv__fsevents_close(uv_fs_event_t* handle) { uv_mutex_destroy(&handle->cf_mutex); uv_sem_destroy(&handle->cf_sem); + free(handle->realpath); + handle->realpath = NULL; + handle->realpath_len = 0; return 0; } diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index b79dce3c2..46b9da2a2 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -93,9 +93,6 @@ int uv_fs_event_init(uv_loop_t* loop, struct stat statbuf; #endif /* defined(__APPLE__) */ - /* We don't support any flags yet. */ - assert(!flags); - /* TODO open asynchronously - but how do we report back errors? */ if ((fd = open(filename, O_RDONLY)) == -1) { uv__set_sys_error(loop, errno); @@ -112,6 +109,9 @@ int uv_fs_event_init(uv_loop_t* loop, #if defined(__APPLE__) /* Nullify field to perform checks later */ handle->cf_eventstream = NULL; + handle->realpath = NULL; + handle->realpath_len = 0; + handle->cf_flags = flags; if (fstat(fd, &statbuf)) goto fallback; diff --git a/deps/uv/src/unix/linux/inotify.c b/deps/uv/src/unix/linux/inotify.c index 3c2908832..97231db9a 100644 --- a/deps/uv/src/unix/linux/inotify.c +++ b/deps/uv/src/unix/linux/inotify.c @@ -176,9 +176,6 @@ int uv_fs_event_init(uv_loop_t* loop, int events; int wd; - /* We don't support any flags yet. */ - assert(!flags); - if (init_inotify(loop)) return -1; events = UV__IN_ATTRIB diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index 30809dc22..411a6563e 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -156,10 +156,10 @@ void uv__pipe_close(uv_pipe_t* handle) { } -void uv_pipe_open(uv_pipe_t* handle, uv_file fd) { - uv__stream_open((uv_stream_t*)handle, - fd, - UV_STREAM_READABLE | UV_STREAM_WRITABLE); +int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { + return uv__stream_open((uv_stream_t*)handle, + fd, + UV_STREAM_READABLE | UV_STREAM_WRITABLE); } diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 5321d90f4..b00cfb5b8 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -386,16 +386,6 @@ void uv__stream_destroy(uv_stream_t* stream) { } -static void uv__next_accept(uv_idle_t* idle, int status) { - uv_stream_t* stream = idle->data; - - uv_idle_stop(idle); - - if (stream->accepted_fd == -1) - uv__io_start(stream->loop, &stream->read_watcher); -} - - /* Implements a best effort approach to mitigating accept() EMFILE errors. * We have a spare file descriptor stashed away that we close to get below * the EMFILE limit. Next, we accept all pending connections and close them @@ -497,40 +487,17 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, int events) { stream->accepted_fd = fd; stream->connection_cb(stream, 0); - if (stream->accepted_fd != -1 || - (stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT)) { + if (stream->accepted_fd != -1) { /* The user hasn't yet accepted called uv_accept() */ uv__io_stop(loop, &stream->read_watcher); - break; + return; } - } - if (stream->fd != -1 && - stream->accepted_fd == -1 && - (stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT)) - { - /* Defer the next accept() syscall to the next event loop tick. - * This lets us guarantee fair load balancing in in multi-process setups. - * The problem is as follows: - * - * 1. Multiple processes listen on the same socket. - * 2. The OS scheduler commonly gives preference to one process to - * avoid task switches. - * 3. That process therefore accepts most of the new connections, - * leading to a (sometimes very) unevenly distributed load. - * - * Here is how we mitigate this issue: - * - * 1. Accept a connection. - * 2. Start an idle watcher. - * 3. Don't accept new connections until the idle callback fires. - * - * This works because the callback only fires when there have been - * no recent events, i.e. none of the watched file descriptors have - * recently been readable or writable. - */ - uv_tcp_t* tcp = (uv_tcp_t*) stream; - uv_idle_start(tcp->idle_handle, uv__next_accept); + if (stream->type == UV_TCP && (stream->flags & UV_TCP_SINGLE_ACCEPT)) { + /* Give other processes a chance to accept connections. */ + struct timespec timeout = { 0, 1 }; + nanosleep(&timeout, NULL); + } } } diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index 18412b8d0..bf1352480 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -196,8 +196,6 @@ int uv_fs_event_init(uv_loop_t* loop, int portfd; int first_run = 0; - /* We don't support any flags yet. */ - assert(!flags); if (loop->fs_fd == -1) { if ((portfd = port_create()) == -1) { uv__set_sys_error(loop, errno); diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index 186150be5..4325c5b12 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -30,7 +30,6 @@ int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); - tcp->idle_handle = NULL; return 0; } @@ -153,6 +152,13 @@ int uv__tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { } +int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { + return uv__stream_open((uv_stream_t*)handle, + sock, + UV_STREAM_READABLE | UV_STREAM_WRITABLE); +} + + int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, int* namelen) { socklen_t socklen; @@ -238,20 +244,9 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { single_accept = (val == NULL) || (atoi(val) != 0); /* on by default */ } - if (!single_accept) - goto no_single_accept; - - tcp->idle_handle = malloc(sizeof(*tcp->idle_handle)); - if (tcp->idle_handle == NULL) - return uv__set_sys_error(tcp->loop, ENOMEM); + if (single_accept) + tcp->flags |= UV_TCP_SINGLE_ACCEPT; - if (uv_idle_init(tcp->loop, tcp->idle_handle)) - abort(); - tcp->idle_handle->flags |= UV__HANDLE_INTERNAL; - - tcp->flags |= UV_TCP_SINGLE_ACCEPT; - -no_single_accept: if (maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE)) return -1; @@ -390,8 +385,5 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { void uv__tcp_close(uv_tcp_t* handle) { - if (handle->idle_handle) - uv_close((uv_handle_t*)handle->idle_handle, (uv_close_cb)free); - uv__stream_close((uv_stream_t*)handle); } diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index c49668744..6f5a3f14e 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -316,17 +316,15 @@ static int uv__bind(uv_udp_t* handle, goto out; } - /* Check for already active socket. */ - if (handle->fd != -1) { - uv__set_artificial_error(handle->loop, UV_EALREADY); - goto out; - } - - if ((fd = uv__socket(domain, SOCK_DGRAM, 0)) == -1) { - uv__set_sys_error(handle->loop, errno); - goto out; + if (handle->fd == -1) { + if ((fd = uv__socket(domain, SOCK_DGRAM, 0)) == -1) { + uv__set_sys_error(handle->loop, errno); + goto out; + } + handle->fd = fd; } + fd = handle->fd; yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) { uv__set_sys_error(handle->loop, errno); @@ -367,12 +365,13 @@ static int uv__bind(uv_udp_t* handle, goto out; } - handle->fd = fd; status = 0; out: - if (status) - close(fd); + if (status) { + close(handle->fd); + handle->fd = -1; + } errno = saved_errno; return status; @@ -486,6 +485,51 @@ int uv__udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned flags) { } +int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { + int saved_errno; + int status; + int yes; + + saved_errno = errno; + status = -1; + + /* Check for already active socket. */ + if (handle->fd != -1) { + uv__set_artificial_error(handle->loop, UV_EALREADY); + goto out; + } + + yes = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) { + uv__set_sys_error(handle->loop, errno); + goto out; + } + + /* On the BSDs, SO_REUSEADDR lets you reuse an address that's in the TIME_WAIT + * state (i.e. was until recently tied to a socket) while SO_REUSEPORT lets + * multiple processes bind to the same address. Yes, it's something of a + * misnomer but then again, SO_REUSEADDR was already taken. + * + * None of the above applies to Linux: SO_REUSEADDR implies SO_REUSEPORT on + * Linux and hence it does not have SO_REUSEPORT at all. + */ +#ifdef SO_REUSEPORT + yes = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) { + uv__set_sys_error(handle->loop, errno); + goto out; + } +#endif + + handle->fd = sock; + status = 0; + +out: + errno = saved_errno; + return status; +} + + int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership) { diff --git a/deps/uv/src/win/fs-event.c b/deps/uv/src/win/fs-event.c index c20c07308..1954cb091 100644 --- a/deps/uv/src/win/fs-event.c +++ b/deps/uv/src/win/fs-event.c @@ -138,9 +138,6 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, WCHAR* dir = NULL, *dir_to_watch, *filenamew = NULL; WCHAR short_path[MAX_PATH]; - /* We don't support any flags yet. */ - assert(!flags); - uv_fs_event_init_handle(loop, handle, filename, cb); /* Convert name to UTF16. */ diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index 59138224c..2436b036f 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -1634,12 +1634,13 @@ static void eof_timer_close_cb(uv_handle_t* handle) { } -void uv_pipe_open(uv_pipe_t* pipe, uv_file file) { +int 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, 0) == -1) { - return; + uv__set_sys_error(pipe->loop, WSAEINVAL); + return -1; } uv_pipe_connection_init(pipe); @@ -1651,4 +1652,5 @@ void uv_pipe_open(uv_pipe_t* pipe, uv_file file) { pipe->ipc_pid = uv_parent_pid(); assert(pipe->ipc_pid != -1); } + return 0; } diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index 1be1d186f..ff7b27b80 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -1378,3 +1378,18 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { uv_want_endgame(tcp->loop, (uv_handle_t*)tcp); } } + + +int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { + /* Make the socket non-inheritable */ + if (!SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)) { + uv__set_sys_error(handle->loop, GetLastError()); + return -1; + } + + if (uv_tcp_set_socket(handle->loop, handle, sock, 0) == -1) { + return -1; + } + + return 0; +} diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index 2436799b5..56b46239a 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -653,6 +653,28 @@ int uv_udp_set_broadcast(uv_udp_t* handle, int value) { } +int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { + int r; + DWORD yes = 1; + + if (uv_udp_set_socket(handle->loop, handle, sock) == -1) { + return -1; + } + + r = setsockopt(handle->socket, + SOL_SOCKET, + SO_REUSEADDR, + (char*) &yes, + sizeof yes); + if (r == SOCKET_ERROR) { + uv__set_sys_error(handle->loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + #define SOCKOPT_SETTER(name, option4, option6, validate) \ int uv_udp_set_##name(uv_udp_t* handle, int value) { \ DWORD optval = (DWORD) value; \ diff --git a/deps/uv/test/benchmark-list.h b/deps/uv/test/benchmark-list.h index 8b6efeef1..ed6d14158 100644 --- a/deps/uv/test/benchmark-list.h +++ b/deps/uv/test/benchmark-list.h @@ -33,6 +33,10 @@ BENCHMARK_DECLARE (tcp_pump1_client) BENCHMARK_DECLARE (pipe_pump100_client) BENCHMARK_DECLARE (pipe_pump1_client) +BENCHMARK_DECLARE (tcp_multi_accept2) +BENCHMARK_DECLARE (tcp_multi_accept4) +BENCHMARK_DECLARE (tcp_multi_accept8) + /* Run until X packets have been sent/received. */ BENCHMARK_DECLARE (udp_pummel_1v1) BENCHMARK_DECLARE (udp_pummel_1v10) @@ -112,6 +116,10 @@ TASK_LIST_START BENCHMARK_ENTRY (pipe_pound_1000) BENCHMARK_HELPER (pipe_pound_1000, pipe_echo_server) + BENCHMARK_ENTRY (tcp_multi_accept2) + BENCHMARK_ENTRY (tcp_multi_accept4) + BENCHMARK_ENTRY (tcp_multi_accept8) + BENCHMARK_ENTRY (udp_pummel_1v1) BENCHMARK_ENTRY (udp_pummel_1v10) BENCHMARK_ENTRY (udp_pummel_1v100) diff --git a/deps/uv/test/benchmark-multi-accept.c b/deps/uv/test/benchmark-multi-accept.c new file mode 100644 index 000000000..ce3f353d2 --- /dev/null +++ b/deps/uv/test/benchmark-multi-accept.c @@ -0,0 +1,436 @@ +/* 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 "task.h" +#include "uv.h" + +#define IPC_PIPE_NAME TEST_PIPENAME +#define NUM_CONNECTS (250 * 1000) + +union stream_handle { + uv_pipe_t pipe; + uv_tcp_t tcp; +}; + +/* Use as (uv_stream_t *) &handle_storage -- it's kind of clunky but it + * avoids aliasing warnings. + */ +typedef unsigned char handle_storage_t[sizeof(union stream_handle)]; + +/* Used for passing around the listen handle, not part of the benchmark proper. + * We have an overabundance of server types here. It works like this: + * + * 1. The main thread starts an IPC pipe server. + * 2. The worker threads connect to the IPC server and obtain a listen handle. + * 3. The worker threads start accepting requests on the listen handle. + * 4. The main thread starts connecting repeatedly. + * + * Step #4 should perhaps be farmed out over several threads. + */ +struct ipc_server_ctx { + handle_storage_t server_handle; + unsigned int num_connects; + uv_pipe_t ipc_pipe; +}; + +struct ipc_peer_ctx { + handle_storage_t peer_handle; + uv_write_t write_req; +}; + +struct ipc_client_ctx { + uv_connect_t connect_req; + uv_stream_t* server_handle; + uv_pipe_t ipc_pipe; + char scratch[16]; +}; + +/* Used in the actual benchmark. */ +struct server_ctx { + handle_storage_t server_handle; + unsigned int num_connects; + uv_async_t async_handle; + uv_thread_t thread_id; + uv_sem_t semaphore; +}; + +struct client_ctx { + handle_storage_t client_handle; + unsigned int num_connects; + uv_connect_t connect_req; + uv_idle_t idle_handle; +}; + +static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status); +static void ipc_write_cb(uv_write_t* req, int status); +static void ipc_close_cb(uv_handle_t* handle); +static void ipc_connect_cb(uv_connect_t* req, int status); +static void ipc_read2_cb(uv_pipe_t* ipc_pipe, + ssize_t nread, + uv_buf_t buf, + uv_handle_type type); +static uv_buf_t ipc_alloc_cb(uv_handle_t* handle, size_t suggested_size); + +static void sv_async_cb(uv_async_t* handle, int status); +static void sv_connection_cb(uv_stream_t* server_handle, int status); +static void sv_read_cb(uv_stream_t* handle, ssize_t nread, uv_buf_t buf); +static uv_buf_t sv_alloc_cb(uv_handle_t* handle, size_t suggested_size); + +static void cl_connect_cb(uv_connect_t* req, int status); +static void cl_idle_cb(uv_idle_t* handle, int status); +static void cl_close_cb(uv_handle_t* handle); + +static struct sockaddr_in listen_addr; + + +static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status) { + struct ipc_server_ctx* sc; + struct ipc_peer_ctx* pc; + uv_loop_t* loop; + uv_buf_t buf; + + loop = ipc_pipe->loop; + buf = uv_buf_init("PING", 4); + sc = container_of(ipc_pipe, struct ipc_server_ctx, ipc_pipe); + pc = calloc(1, sizeof(*pc)); + ASSERT(pc != NULL); + + if (ipc_pipe->type == UV_TCP) + ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &pc->peer_handle)); + else if (ipc_pipe->type == UV_NAMED_PIPE) + ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) &pc->peer_handle, 1)); + else + ASSERT(0); + + ASSERT(0 == uv_accept(ipc_pipe, (uv_stream_t*) &pc->peer_handle)); + ASSERT(0 == uv_write2(&pc->write_req, + (uv_stream_t*) &pc->peer_handle, + &buf, + 1, + (uv_stream_t*) &sc->server_handle, + ipc_write_cb)); + + if (--sc->num_connects == 0) + uv_close((uv_handle_t*) ipc_pipe, NULL); +} + + +static void ipc_write_cb(uv_write_t* req, int status) { + struct ipc_peer_ctx* ctx; + ctx = container_of(req, struct ipc_peer_ctx, write_req); + uv_close((uv_handle_t*) &ctx->peer_handle, ipc_close_cb); +} + + +static void ipc_close_cb(uv_handle_t* handle) { + struct ipc_peer_ctx* ctx; + ctx = container_of(handle, struct ipc_peer_ctx, peer_handle); + free(ctx); +} + + +static void ipc_connect_cb(uv_connect_t* req, int status) { + struct ipc_client_ctx* ctx; + ctx = container_of(req, struct ipc_client_ctx, connect_req); + ASSERT(0 == status); + ASSERT(0 == uv_read2_start((uv_stream_t*) &ctx->ipc_pipe, + ipc_alloc_cb, + ipc_read2_cb)); +} + + +static uv_buf_t ipc_alloc_cb(uv_handle_t* handle, size_t suggested_size) { + struct ipc_client_ctx* ctx; + ctx = container_of(handle, struct ipc_client_ctx, ipc_pipe); + return uv_buf_init(ctx->scratch, sizeof(ctx->scratch)); +} + + +static void ipc_read2_cb(uv_pipe_t* ipc_pipe, + ssize_t nread, + uv_buf_t buf, + uv_handle_type type) { + struct ipc_client_ctx* ctx; + uv_loop_t* loop; + + ctx = container_of(ipc_pipe, struct ipc_client_ctx, ipc_pipe); + loop = ipc_pipe->loop; + + if (type == UV_TCP) + ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) ctx->server_handle)); + else if (type == UV_NAMED_PIPE) + ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) ctx->server_handle, 0)); + else + ASSERT(0); + + ASSERT(0 == uv_accept((uv_stream_t*) &ctx->ipc_pipe, ctx->server_handle)); + uv_close((uv_handle_t*) &ctx->ipc_pipe, NULL); +} + + +/* Set up an IPC pipe server that hands out listen sockets to the worker + * threads. It's kind of cumbersome for such a simple operation, maybe we + * should revive uv_import() and uv_export(). + */ +static void send_listen_handles(uv_handle_type type, + unsigned int num_servers, + struct server_ctx* servers) { + struct ipc_server_ctx ctx; + uv_loop_t* loop; + unsigned int i; + + loop = uv_default_loop(); + ctx.num_connects = num_servers; + + if (type == UV_TCP) { + ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &ctx.server_handle)); + ASSERT(0 == uv_tcp_bind((uv_tcp_t*) &ctx.server_handle, listen_addr)); + } + else if (type == UV_NAMED_PIPE) { + ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) &ctx.server_handle, 0)); + ASSERT(0 == uv_pipe_bind((uv_pipe_t*) &ctx.server_handle, IPC_PIPE_NAME)); + } + else + ASSERT(0); + + ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1)); + ASSERT(0 == uv_pipe_bind(&ctx.ipc_pipe, IPC_PIPE_NAME)); + ASSERT(0 == uv_listen((uv_stream_t*) &ctx.ipc_pipe, 128, ipc_connection_cb)); + + for (i = 0; i < num_servers; i++) + uv_sem_post(&servers[i].semaphore); + + ASSERT(0 == uv_run(loop)); + uv_close((uv_handle_t*) &ctx.server_handle, NULL); + ASSERT(0 == uv_run(loop)); + + for (i = 0; i < num_servers; i++) + uv_sem_wait(&servers[i].semaphore); +} + + +static void get_listen_handle(uv_loop_t* loop, uv_stream_t* server_handle) { + struct ipc_client_ctx ctx; + + ctx.server_handle = server_handle; + ctx.server_handle->data = "server handle"; + + ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1)); + uv_pipe_connect(&ctx.connect_req, + &ctx.ipc_pipe, + IPC_PIPE_NAME, + ipc_connect_cb); + ASSERT(0 == uv_run(loop)); +} + + +static void server_cb(void *arg) { + struct server_ctx *ctx; + uv_loop_t* loop; + + ctx = arg; + loop = uv_loop_new(); + ASSERT(loop != NULL); + + ASSERT(0 == uv_async_init(loop, &ctx->async_handle, sv_async_cb)); + uv_unref((uv_handle_t*) &ctx->async_handle); + + /* Wait until the main thread is ready. */ + uv_sem_wait(&ctx->semaphore); + get_listen_handle(loop, (uv_stream_t*) &ctx->server_handle); + uv_sem_post(&ctx->semaphore); + + /* Now start the actual benchmark. */ + ASSERT(0 == uv_listen((uv_stream_t*) &ctx->server_handle, + 128, + sv_connection_cb)); + ASSERT(0 == uv_run(loop)); + + uv_loop_delete(loop); +} + + +static void sv_async_cb(uv_async_t* handle, int status) { + struct server_ctx* ctx; + ctx = container_of(handle, struct server_ctx, async_handle); + uv_close((uv_handle_t*) &ctx->server_handle, NULL); + uv_close((uv_handle_t*) &ctx->async_handle, NULL); +} + + +static void sv_connection_cb(uv_stream_t* server_handle, int status) { + handle_storage_t* storage; + struct server_ctx* ctx; + + ctx = container_of(server_handle, struct server_ctx, server_handle); + ASSERT(status == 0); + + storage = malloc(sizeof(*storage)); + ASSERT(storage != NULL); + + if (server_handle->type == UV_TCP) + ASSERT(0 == uv_tcp_init(server_handle->loop, (uv_tcp_t*) storage)); + else if (server_handle->type == UV_NAMED_PIPE) + ASSERT(0 == uv_pipe_init(server_handle->loop, (uv_pipe_t*) storage, 0)); + else + ASSERT(0); + + ASSERT(0 == uv_accept(server_handle, (uv_stream_t*) storage)); + ASSERT(0 == uv_read_start((uv_stream_t*) storage, sv_alloc_cb, sv_read_cb)); + ctx->num_connects++; +} + + +static uv_buf_t sv_alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char buf[32]; + return uv_buf_init(buf, sizeof(buf)); +} + + +static void sv_read_cb(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { + ASSERT(nread == -1); + ASSERT(uv_last_error(handle->loop).code == UV_EOF); + uv_close((uv_handle_t*) handle, (uv_close_cb) free); +} + + +static void cl_connect_cb(uv_connect_t* req, int status) { + struct client_ctx* ctx = container_of(req, struct client_ctx, connect_req); + uv_idle_start(&ctx->idle_handle, cl_idle_cb); + ASSERT(0 == status); +} + + +static void cl_idle_cb(uv_idle_t* handle, int status) { + struct client_ctx* ctx = container_of(handle, struct client_ctx, idle_handle); + uv_close((uv_handle_t*) &ctx->client_handle, cl_close_cb); + uv_idle_stop(&ctx->idle_handle); +} + + +static void cl_close_cb(uv_handle_t* handle) { + struct client_ctx* ctx; + + ctx = container_of(handle, struct client_ctx, client_handle); + + if (--ctx->num_connects == 0) { + uv_close((uv_handle_t*) &ctx->idle_handle, NULL); + return; + } + + ASSERT(0 == uv_tcp_init(handle->loop, (uv_tcp_t*) &ctx->client_handle)); + ASSERT(0 == uv_tcp_connect(&ctx->connect_req, + (uv_tcp_t*) &ctx->client_handle, + listen_addr, + cl_connect_cb)); +} + + +static int test_tcp(unsigned int num_servers, unsigned int num_clients) { + struct server_ctx* servers; + struct client_ctx* clients; + uv_loop_t* loop; + uv_tcp_t* handle; + unsigned int i; + double time; + + listen_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + loop = uv_default_loop(); + + servers = calloc(num_servers, sizeof(servers[0])); + clients = calloc(num_clients, sizeof(clients[0])); + ASSERT(servers != NULL); + ASSERT(clients != NULL); + + /* We're making the assumption here that from the perspective of the + * OS scheduler, threads are functionally equivalent to and interchangeable + * with full-blown processes. + */ + for (i = 0; i < num_servers; i++) { + struct server_ctx* ctx = servers + i; + ASSERT(0 == uv_sem_init(&ctx->semaphore, 0)); + ASSERT(0 == uv_thread_create(&ctx->thread_id, server_cb, ctx)); + } + + send_listen_handles(UV_TCP, num_servers, servers); + + for (i = 0; i < num_clients; i++) { + struct client_ctx* ctx = clients + i; + ctx->num_connects = NUM_CONNECTS / num_clients; + handle = (uv_tcp_t*) &ctx->client_handle; + handle->data = "client handle"; + ASSERT(0 == uv_tcp_init(loop, handle)); + ASSERT(0 == uv_tcp_connect(&ctx->connect_req, + handle, + listen_addr, + cl_connect_cb)); + ASSERT(0 == uv_idle_init(loop, &ctx->idle_handle)); + } + + { + uint64_t t = uv_hrtime(); + ASSERT(0 == uv_run(loop)); + t = uv_hrtime() - t; + time = t / 1e9; + } + + for (i = 0; i < num_servers; i++) { + struct server_ctx* ctx = servers + i; + uv_async_send(&ctx->async_handle); + ASSERT(0 == uv_thread_join(&ctx->thread_id)); + uv_sem_destroy(&ctx->semaphore); + } + + printf("accept%u: %.0f accepts/sec (%u total)\n", + num_servers, + NUM_CONNECTS / time, + NUM_CONNECTS); + + for (i = 0; i < num_servers; i++) { + struct server_ctx* ctx = servers + i; + printf(" thread #%u: %.0f accepts/sec (%u total, %.1f%%)\n", + i, + ctx->num_connects / time, + ctx->num_connects, + ctx->num_connects * 100.0 / NUM_CONNECTS); + } + + free(clients); + free(servers); + uv_loop_delete(uv_default_loop()); /* Silence valgrind. */ + + return 0; +} + + +BENCHMARK_IMPL(tcp_multi_accept2) { + return test_tcp(2, 40); +} + + +BENCHMARK_IMPL(tcp_multi_accept4) { + return test_tcp(4, 40); +} + + +BENCHMARK_IMPL(tcp_multi_accept8) { + return test_tcp(8, 40); +} diff --git a/deps/uv/test/dns-server.c b/deps/uv/test/dns-server.c index d885f4c86..e541e781c 100644 --- a/deps/uv/test/dns-server.c +++ b/deps/uv/test/dns-server.c @@ -153,7 +153,6 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { hdrbuf_remaining = DNSREC_LEN - readbuf_remaining; break; } else { - short int reclen_n; /* save header */ memcpy(&hdrbuf[DNSREC_LEN - hdrbuf_remaining], dnsreq, hdrbuf_remaining); dnsreq += hdrbuf_remaining; @@ -161,8 +160,8 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { hdrbuf_remaining = 0; /* get record length */ - reclen_n = *((short int*)hdrbuf); - rec_remaining = ntohs(reclen_n) - (DNSREC_LEN - 2); + rec_remaining = (unsigned) hdrbuf[0] * 256 + (unsigned) hdrbuf[1]; + rec_remaining -= (DNSREC_LEN - 2); } } diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c index 257d64ca5..4d4cfbc31 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -98,8 +98,7 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_RENAME); - ASSERT(filename == NULL || strcmp(filename, "file1") == 0 || - strstr(filename, "watch_dir") != NULL); + ASSERT(filename == NULL || strcmp(filename, "file1") == 0); uv_close((uv_handle_t*)handle, close_cb); } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 1423f806a..ce70c1f91 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -40,6 +40,7 @@ TEST_DECLARE (pipe_ping_pong) TEST_DECLARE (delayed_accept) TEST_DECLARE (multiple_listen) TEST_DECLARE (tcp_writealot) +TEST_DECLARE (tcp_open) TEST_DECLARE (tcp_connect_error_after_write) TEST_DECLARE (tcp_shutdown_after_write) TEST_DECLARE (tcp_bind_error_addrinuse) @@ -69,6 +70,7 @@ TEST_DECLARE (udp_dgram_too_big) TEST_DECLARE (udp_dual_stack) TEST_DECLARE (udp_ipv6_only) TEST_DECLARE (udp_options) +TEST_DECLARE (udp_open) TEST_DECLARE (pipe_bind_error_addrinuse) TEST_DECLARE (pipe_bind_error_addrnotavail) TEST_DECLARE (pipe_bind_error_inval) @@ -237,6 +239,9 @@ TASK_LIST_START TEST_ENTRY (tcp_writealot) TEST_HELPER (tcp_writealot, tcp4_echo_server) + TEST_ENTRY (tcp_open) + TEST_HELPER (tcp_open, tcp4_echo_server) + TEST_ENTRY (tcp_shutdown_after_write) TEST_HELPER (tcp_shutdown_after_write, tcp4_echo_server) @@ -271,6 +276,9 @@ TASK_LIST_START TEST_ENTRY (udp_multicast_join) TEST_ENTRY (udp_multicast_ttl) + TEST_ENTRY (udp_open) + TEST_HELPER (udp_open, udp4_echo_server) + TEST_ENTRY (pipe_bind_error_addrinuse) TEST_ENTRY (pipe_bind_error_addrnotavail) TEST_ENTRY (pipe_bind_error_inval) diff --git a/deps/uv/test/test-tcp-open.c b/deps/uv/test/test-tcp-open.c new file mode 100644 index 000000000..48188d245 --- /dev/null +++ b/deps/uv/test/test-tcp-open.c @@ -0,0 +1,175 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef _WIN32 +# include <unistd.h> +#endif + +static int shutdown_cb_called = 0; +static int connect_cb_called = 0; +static int write_cb_called = 0; +static int close_cb_called = 0; + +static uv_connect_t connect_req; +static uv_shutdown_t shutdown_req; +static uv_write_t write_req; + + +static void startup(void) { +#ifdef _WIN32 + struct WSAData wsa_data; + int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); +#endif +} + + +static uv_os_sock_t create_tcp_socket(void) { + uv_os_sock_t sock; + int r; + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); +#ifdef _WIN32 + ASSERT(sock != INVALID_SOCKET); +#else + ASSERT(sock >= 0); +#endif + +#ifndef _WIN32 + { + /* Allow reuse of the port. */ + int yes = 1; + r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + ASSERT(r == 0); + } +#endif + + return sock; +} + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + ASSERT(suggested_size <= sizeof slab); + return uv_buf_init(slab, sizeof slab); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(req == &shutdown_req); + ASSERT(status == 0); + + /* Now we wait for the EOF */ + shutdown_cb_called++; +} + + +static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { + ASSERT(tcp != NULL); + + if (nread >= 0) { + ASSERT(nread == 4); + ASSERT(memcmp("PING", buf.base, nread) == 0); + } + else { + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); + printf("GOT EOF\n"); + uv_close((uv_handle_t*)tcp, close_cb); + } +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req != NULL); + + if (status) { + uv_err_t err = uv_last_error(uv_default_loop()); + fprintf(stderr, "uv_write error: %s\n", uv_strerror(err)); + ASSERT(0); + } + + write_cb_called++; +} + + +static void connect_cb(uv_connect_t* req, int status) { + uv_buf_t buf = uv_buf_init("PING", 4); + uv_stream_t* stream; + int r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + stream = req->handle; + connect_cb_called++; + + r = uv_write(&write_req, stream, &buf, 1, write_cb); + ASSERT(r == 0); + + /* Shutdown on drain. */ + r = uv_shutdown(&shutdown_req, stream, shutdown_cb); + ASSERT(r == 0); + + /* Start reading */ + r = uv_read_start(stream, alloc_cb, read_cb); + ASSERT(r == 0); +} + + +TEST_IMPL(tcp_open) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_tcp_t client; + uv_os_sock_t sock; + int r; + + startup(); + sock = create_tcp_socket(); + + r = uv_tcp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_tcp_open(&client, sock); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, &client, addr, connect_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop()); + + ASSERT(shutdown_cb_called == 1); + ASSERT(connect_cb_called == 1); + ASSERT(write_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/deps/uv/test/test-udp-open.c b/deps/uv/test/test-udp-open.c new file mode 100644 index 000000000..9168549e6 --- /dev/null +++ b/deps/uv/test/test-udp-open.c @@ -0,0 +1,154 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef _WIN32 +# include <unistd.h> +#endif + +static int send_cb_called = 0; +static int close_cb_called = 0; + +static uv_udp_send_t send_req; + + +static void startup(void) { +#ifdef _WIN32 + struct WSAData wsa_data; + int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); +#endif +} + + +static uv_os_sock_t create_udp_socket(void) { + uv_os_sock_t sock; + int r; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); +#ifdef _WIN32 + ASSERT(sock != INVALID_SOCKET); +#else + ASSERT(sock >= 0); +#endif + +#ifndef _WIN32 + { + /* Allow reuse of the port. */ + int yes = 1; + r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + ASSERT(r == 0); + } +#endif + + return sock; +} + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + ASSERT(suggested_size <= sizeof slab); + return uv_buf_init(slab, sizeof slab); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + int r; + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards sv_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + ASSERT(flags == 0); + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(memcmp("PING", buf.base, nread) == 0); + + r = uv_udp_recv_stop(handle); + ASSERT(r == 0); + + uv_close((uv_handle_t*) handle, close_cb); +} + + +static void send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + + send_cb_called++; +} + + +TEST_IMPL(udp_open) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_buf_t buf = uv_buf_init("PING", 4); + uv_udp_t client; + uv_os_sock_t sock; + int r; + + startup(); + sock = create_udp_socket(); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + r = uv_udp_open(&client, sock); + ASSERT(r == 0); + + r = uv_udp_bind(&client, addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&client, alloc_cb, recv_cb); + ASSERT(r == 0); + + r = uv_udp_send(&send_req, &client, &buf, 1, addr, send_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop()); + + ASSERT(send_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index a5dbf153d..a7993e1d6 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -305,6 +305,7 @@ 'test/test-tcp-connect-error.c', 'test/test-tcp-connect-timeout.c', 'test/test-tcp-connect6-error.c', + 'test/test-tcp-open.c', 'test/test-tcp-write-error.c', 'test/test-tcp-write-to-half-open-connection.c', 'test/test-tcp-writealot.c', @@ -318,6 +319,7 @@ 'test/test-tty.c', 'test/test-udp-dgram-too-big.c', 'test/test-udp-ipv6.c', + 'test/test-udp-open.c', 'test/test-udp-options.c', 'test/test-udp-send-and-recv.c', 'test/test-udp-multicast-join.c', @@ -370,6 +372,7 @@ 'test/benchmark-list.h', 'test/benchmark-loop-count.c', 'test/benchmark-million-timers.c', + 'test/benchmark-multi-accept.c', 'test/benchmark-ping-pongs.c', 'test/benchmark-pound.c', 'test/benchmark-pump.c', |