From 7e5aeac28f06ab17bcdfd4fd8ede6a7bcd95dbaf Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 30 Jun 2012 03:27:54 +0200 Subject: [PATCH] deps: upgrade libuv to 3b8c0da --- deps/uv/src/unix/internal.h | 5 +- deps/uv/src/unix/stream.c | 68 +--------- deps/uv/src/unix/tcp.c | 163 +++++++++++++++--------- deps/uv/test/test-gethostbyname.c | 27 ++-- deps/uv/test/test-list.h | 3 +- deps/uv/test/test-tcp-unexpected-read.c | 111 ++++++++++++++++ deps/uv/uv.gyp | 1 + 7 files changed, 233 insertions(+), 145 deletions(-) create mode 100644 deps/uv/test/test-tcp-unexpected-read.c diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 23f16f7b0ee..382c272235d 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -93,7 +93,8 @@ enum { UV_STREAM_WRITABLE = 0x40, /* The stream is writable */ UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */ UV_TCP_NODELAY = 0x100, /* Disable Nagle. */ - UV_TCP_KEEPALIVE = 0x200 /* Turn on keep-alive. */ + UV_TCP_KEEPALIVE = 0x200, /* Turn on keep-alive. */ + UV_TCP_CONNECTING = 0x400 /* Not alway set. See uv_connect() in tcp.c */ }; inline static void uv__req_init(uv_loop_t* loop, @@ -139,8 +140,6 @@ int uv__stream_open(uv_stream_t*, int fd, int flags); void uv__stream_destroy(uv_stream_t* stream); void uv__server_io(uv_loop_t* loop, uv__io_t* watcher, int events); int uv__accept(int sockfd); -int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, - socklen_t addrlen, uv_connect_cb cb); /* tcp */ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 8c33ee09c5f..bab65807beb 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -784,12 +784,15 @@ static void uv__stream_connect(uv_stream_t* stream) { if (error == EINPROGRESS) return; - if (error == 0) - uv__io_start(stream->loop, &stream->read_watcher); - stream->connect_req = NULL; uv__req_unregister(stream->loop, req); + /* Hack. See uv__connect() in tcp.c */ + if (stream->flags & UV_TCP_CONNECTING) { + assert(stream->type == UV_TCP); + uv__handle_stop(stream); + } + if (req->cb) { uv__set_sys_error(stream->loop, error); req->cb(req, error ? -1 : 0); @@ -797,65 +800,6 @@ static void uv__stream_connect(uv_stream_t* stream) { } -int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, - socklen_t addrlen, uv_connect_cb cb) { - int sockfd; - int r; - - if (stream->type != UV_TCP) - return uv__set_sys_error(stream->loop, ENOTSOCK); - - if (stream->connect_req) - return uv__set_sys_error(stream->loop, EALREADY); - - if (stream->fd <= 0) { - sockfd = uv__socket(addr->sa_family, SOCK_STREAM, 0); - - if (sockfd == -1) - return uv__set_sys_error(stream->loop, errno); - - if (uv__stream_open(stream, - sockfd, - UV_STREAM_READABLE | UV_STREAM_WRITABLE)) { - close(sockfd); - return -1; - } - } - - stream->delayed_error = 0; - - do - r = connect(stream->fd, addr, addrlen); - while (r == -1 && errno == EINTR); - - if (r == -1) { - if (errno == EINPROGRESS) - ; /* not an error */ - else if (errno == ECONNREFUSED) - /* If we get a ECONNREFUSED wait until the next tick to report the - * error. Solaris wants to report immediately--other unixes want to - * wait. - */ - stream->delayed_error = errno; - else - return uv__set_sys_error(stream->loop, errno); - } - - uv__req_init(stream->loop, req, UV_CONNECT); - req->cb = cb; - req->handle = stream; - ngx_queue_init(&req->queue); - stream->connect_req = req; - - uv__io_start(stream->loop, &stream->write_watcher); - - if (stream->delayed_error) - uv__io_feed(stream->loop, &stream->write_watcher, UV__IO_WRITE); - - return 0; -} - - int uv_write2(uv_write_t* req, uv_stream_t* stream, uv_buf_t bufs[], int bufcnt, uv_stream_t* send_handle, uv_write_cb cb) { int empty_queue; diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index 07ad2d9ecad..1aeba0efcfb 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -34,6 +34,26 @@ int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { } +static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) { + int sockfd; + + if (handle->fd != -1) + return 0; + + sockfd = uv__socket(domain, SOCK_STREAM, 0); + + if (sockfd == -1) + return uv__set_sys_error(handle->loop, errno); + + if (uv__stream_open((uv_stream_t*)handle, sockfd, flags)) { + close(sockfd); + return -1; + } + + return 0; +} + + static int uv__bind(uv_tcp_t* tcp, int domain, struct sockaddr* addr, @@ -44,23 +64,8 @@ static int uv__bind(uv_tcp_t* tcp, saved_errno = errno; status = -1; - if (tcp->fd < 0) { - if ((tcp->fd = uv__socket(domain, SOCK_STREAM, 0)) == -1) { - uv__set_sys_error(tcp->loop, errno); - goto out; - } - - if (uv__stream_open((uv_stream_t*)tcp, - tcp->fd, - UV_STREAM_READABLE | UV_STREAM_WRITABLE)) { - close(tcp->fd); - tcp->fd = -1; - status = -2; - goto out; - } - } - - assert(tcp->fd >= 0); + if (maybe_new_socket(tcp, domain, UV_STREAM_READABLE|UV_STREAM_WRITABLE)) + return -1; tcp->delayed_error = 0; if (bind(tcp->fd, addr, addrsize) == -1) { @@ -79,6 +84,67 @@ out: } +static int uv__connect(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr* addr, + socklen_t addrlen, + uv_connect_cb cb) { + int r; + + assert(handle->type == UV_TCP); + + if (handle->connect_req) + return uv__set_sys_error(handle->loop, EALREADY); + + if (maybe_new_socket(handle, + addr->sa_family, + UV_STREAM_READABLE|UV_STREAM_WRITABLE)) { + return -1; + } + + handle->delayed_error = 0; + + do + r = connect(handle->fd, addr, addrlen); + while (r == -1 && errno == EINTR); + + if (r == -1) { + if (errno == EINPROGRESS) { + /* Not an error. However, we need to keep the event loop from spinning + * while the connection is in progress. Artificially start the handle + * and stop it again in uv__stream_connect() in stream.c. Yes, it's a + * hack but there's no good alternative, the v0.8 ABI is frozen. + */ + if (!uv__is_active(handle)) { + handle->flags |= UV_TCP_CONNECTING; + uv__handle_start(handle); + } + } + else if (errno == ECONNREFUSED) + /* If we get a ECONNREFUSED wait until the next tick to report the + * error. Solaris wants to report immediately--other unixes want to + * wait. + */ + handle->delayed_error = errno; + else + return uv__set_sys_error(handle->loop, errno); + } + + uv__req_init(handle->loop, req, UV_CONNECT); + req->cb = cb; + req->handle = (uv_stream_t*) handle; + ngx_queue_init(&req->queue); + handle->connect_req = req; + + uv__io_start(handle->loop, &handle->write_watcher); + + if (handle->delayed_error) + uv__io_feed(handle->loop, &handle->write_watcher, UV__IO_WRITE); + + return 0; +} + + int uv__tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { return uv__bind(handle, AF_INET, @@ -170,33 +236,14 @@ out: int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { - int r; + if (tcp->delayed_error) + return uv__set_sys_error(tcp->loop, tcp->delayed_error); - if (tcp->delayed_error) { - uv__set_sys_error(tcp->loop, tcp->delayed_error); + if (maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE)) return -1; - } - if (tcp->fd < 0) { - if ((tcp->fd = uv__socket(AF_INET, SOCK_STREAM, 0)) == -1) { - uv__set_sys_error(tcp->loop, errno); - return -1; - } - - if (uv__stream_open((uv_stream_t*)tcp, tcp->fd, UV_STREAM_READABLE)) { - close(tcp->fd); - tcp->fd = -1; - return -1; - } - } - - assert(tcp->fd >= 0); - - r = listen(tcp->fd, backlog); - if (r < 0) { - uv__set_sys_error(tcp->loop, errno); - return -1; - } + if (listen(tcp->fd, backlog)) + return uv__set_sys_error(tcp->loop, errno); tcp->connection_cb = cb; @@ -209,37 +256,31 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { int uv__tcp_connect(uv_connect_t* req, - uv_tcp_t* handle, - struct sockaddr_in address, - uv_connect_cb cb) { - int saved_errno = errno; + uv_tcp_t* handle, + struct sockaddr_in addr, + uv_connect_cb cb) { + int saved_errno; int status; - status = uv__connect(req, - (uv_stream_t*)handle, - (struct sockaddr*)&address, - sizeof address, - cb); - + saved_errno = errno; + status = uv__connect(req, handle, (struct sockaddr*)&addr, sizeof addr, cb); errno = saved_errno; + return status; } int uv__tcp_connect6(uv_connect_t* req, - uv_tcp_t* handle, - struct sockaddr_in6 address, - uv_connect_cb cb) { - int saved_errno = errno; + uv_tcp_t* handle, + struct sockaddr_in6 addr, + uv_connect_cb cb) { + int saved_errno; int status; - status = uv__connect(req, - (uv_stream_t*)handle, - (struct sockaddr*)&address, - sizeof address, - cb); - + saved_errno = errno; + status = uv__connect(req, handle, (struct sockaddr*)&addr, sizeof addr, cb); errno = saved_errno; + return status; } diff --git a/deps/uv/test/test-gethostbyname.c b/deps/uv/test/test-gethostbyname.c index e49c69c41cd..1df2aaac309 100644 --- a/deps/uv/test/test-gethostbyname.c +++ b/deps/uv/test/test-gethostbyname.c @@ -27,8 +27,6 @@ #include /* strlen */ static ares_channel channel; -static struct ares_options options; -static int optmask; static int ares_bynamecallbacks; static int bynamecallbacksig; @@ -63,19 +61,12 @@ static void aresbyaddrcallback( void *arg, } -static void prep_tcploopback() { - /* for test, use echo server - TCP port TEST_PORT on loopback */ - struct sockaddr_in test_server = uv_ip4_addr("127.0.0.1", 0); - int rc; - - optmask = ARES_OPT_SERVERS | ARES_OPT_TCP_PORT | ARES_OPT_FLAGS; - options.servers = &test_server.sin_addr; - options.nservers = 1; - options.tcp_port = htons(TEST_PORT); - options.flags = ARES_FLAG_USEVC; - - rc = uv_ares_init_options(uv_default_loop(), &channel, &options, optmask); - ASSERT(rc == ARES_SUCCESS); +static void setup_cares() { + int r; + struct ares_options options; + memset(&options, 0, sizeof options); + r = uv_ares_init_options(uv_default_loop(), &channel, &options, 0); + ASSERT(r == ARES_SUCCESS); } @@ -91,7 +82,7 @@ TEST_IMPL(gethostbyname) { } printf("Start basic gethostbyname test\n"); - prep_tcploopback(); + setup_cares(); ares_bynamecallbacks = 0; bynamecallbacksig = 7; @@ -112,7 +103,7 @@ TEST_IMPL(gethostbyname) { /* two sequential call on new channel */ printf("Start gethostbyname and gethostbyaddr sequential test\n"); - prep_tcploopback(); + setup_cares(); ares_bynamecallbacks = 0; bynamecallbacksig = 7; @@ -151,7 +142,7 @@ TEST_IMPL(gethostbyname) { /* two simultaneous calls on new channel */ printf("Start gethostbyname and gethostbyaddr concurrent test\n"); - prep_tcploopback(); + setup_cares(); ares_bynamecallbacks = 0; bynamecallbacksig = 7; diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index cf9a02edb19..76651499646 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -56,6 +56,7 @@ TEST_DECLARE (tcp_close) TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_error) TEST_DECLARE (tcp_write_to_half_open_connection) +TEST_DECLARE (tcp_unexpected_read) TEST_DECLARE (tcp_bind6_error_addrinuse) TEST_DECLARE (tcp_bind6_error_addrnotavail) TEST_DECLARE (tcp_bind6_error_fault) @@ -251,6 +252,7 @@ TASK_LIST_START TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_error) TEST_ENTRY (tcp_write_to_half_open_connection) + TEST_ENTRY (tcp_unexpected_read) TEST_ENTRY (tcp_bind6_error_addrinuse) TEST_ENTRY (tcp_bind6_error_addrnotavail) @@ -342,7 +344,6 @@ TASK_LIST_START TEST_ENTRY (getaddrinfo_concurrent) TEST_ENTRY (gethostbyname) - TEST_HELPER (gethostbyname, tcp4_echo_server) TEST_ENTRY (getsockname_tcp) TEST_ENTRY (getsockname_udp) diff --git a/deps/uv/test/test-tcp-unexpected-read.c b/deps/uv/test/test-tcp-unexpected-read.c new file mode 100644 index 00000000000..7adb4dae2c0 --- /dev/null +++ b/deps/uv/test/test-tcp-unexpected-read.c @@ -0,0 +1,111 @@ +/* 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" + +static uv_check_t check_handle; +static uv_timer_t timer_handle; +static uv_tcp_t server_handle; +static uv_tcp_t client_handle; +static uv_tcp_t peer_handle; +static uv_write_t write_req; +static uv_connect_t connect_req; + +static unsigned long ticks; /* event loop ticks */ + + +static void check_cb(uv_check_t* handle, int status) { + ticks++; +} + + +static void timer_cb(uv_timer_t* handle, int status) { + uv_close((uv_handle_t*) &check_handle, NULL); + uv_close((uv_handle_t*) &timer_handle, NULL); + uv_close((uv_handle_t*) &server_handle, NULL); + uv_close((uv_handle_t*) &client_handle, NULL); + uv_close((uv_handle_t*) &peer_handle, NULL); +} + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + ASSERT(0 && "alloc_cb should not have been called"); +} + + +static void read_cb(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { + ASSERT(0 && "read_cb should not have been called"); +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &client_handle); + ASSERT(0 == status); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &peer_handle); + ASSERT(0 == status); +} + + +static void connection_cb(uv_stream_t* handle, int status) { + uv_buf_t buf; + + buf = uv_buf_init("PING", 4); + + ASSERT(0 == status); + ASSERT(0 == uv_tcp_init(uv_default_loop(), &peer_handle)); + ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &peer_handle, + &buf, 1, write_cb)); +} + + +TEST_IMPL(tcp_unexpected_read) { + struct sockaddr_in addr; + uv_loop_t* loop; + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + loop = uv_default_loop(); + + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1000, 0)); + ASSERT(0 == uv_check_init(loop, &check_handle)); + ASSERT(0 == uv_check_start(&check_handle, check_cb)); + ASSERT(0 == uv_tcp_init(loop, &server_handle)); + ASSERT(0 == uv_tcp_init(loop, &client_handle)); + ASSERT(0 == uv_tcp_bind(&server_handle, addr)); + ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + ASSERT(0 == uv_tcp_connect(&connect_req, &client_handle, addr, connect_cb)); + ASSERT(0 == uv_run(loop)); + + /* This is somewhat inexact but the idea is that the event loop should not + * start busy looping when the server sends a message and the client isn't + * reading. + */ + ASSERT(ticks <= 10); + + return 0; +} diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 5b9273abb5b..0fa50338c61 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -355,6 +355,7 @@ 'test/test-tcp-write-error.c', 'test/test-tcp-write-to-half-open-connection.c', 'test/test-tcp-writealot.c', + 'test/test-tcp-unexpected-read.c', 'test/test-threadpool.c', 'test/test-mutexes.c', 'test/test-thread.c',