summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorisaacs <i@izs.me>2012-09-17 15:36:05 -0700
committerisaacs <i@izs.me>2012-09-17 15:36:15 -0700
commitb788c5e77b564183826a4ccb69ec9a43a758a54a (patch)
tree8916ff311d3a7a5ae41022b9c8c198e78e1d55c3
parent7144be70db2e6ce337d0f6ec47a28a06802d1c7a (diff)
downloadnode-b788c5e77b564183826a4ccb69ec9a43a758a54a.tar.gz
uv: Upgrade to 778144f0
joyent/libuv@778144f0b5bc14fe55d79854d0a67124ceaa4b22
-rw-r--r--deps/uv/include/uv-private/uv-darwin.h3
-rw-r--r--deps/uv/include/uv-private/uv-unix.h3
-rw-r--r--deps/uv/include/uv.h21
-rw-r--r--deps/uv/src/unix/fsevents.c56
-rw-r--r--deps/uv/src/unix/kqueue.c6
-rw-r--r--deps/uv/src/unix/linux/inotify.c3
-rw-r--r--deps/uv/src/unix/pipe.c8
-rw-r--r--deps/uv/src/unix/stream.c47
-rw-r--r--deps/uv/src/unix/sunos.c2
-rw-r--r--deps/uv/src/unix/tcp.c26
-rw-r--r--deps/uv/src/unix/udp.c68
-rw-r--r--deps/uv/src/win/fs-event.c3
-rw-r--r--deps/uv/src/win/pipe.c6
-rw-r--r--deps/uv/src/win/tcp.c15
-rw-r--r--deps/uv/src/win/udp.c22
-rw-r--r--deps/uv/test/benchmark-list.h8
-rw-r--r--deps/uv/test/benchmark-multi-accept.c436
-rw-r--r--deps/uv/test/dns-server.c5
-rw-r--r--deps/uv/test/test-fs-event.c3
-rw-r--r--deps/uv/test/test-list.h8
-rw-r--r--deps/uv/test/test-tcp-open.c175
-rw-r--r--deps/uv/test/test-udp-open.c154
-rw-r--r--deps/uv/uv.gyp3
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',