unix: fix EINPROGRESS busy loop
Don't make the event loop spin when connect() returns EINPROGRESS.
Test case:
#include "uv.h"
static void connect_cb(uv_connect_t* req, int status) {
// ...
}
int main() {
uv_tcp_t handle;
uv_connect_t req;
struct sockaddr_in addr;
addr = uv_ip4_addr("8.8.8.8", 1234); // unreachable
uv_tcp_init(uv_default_loop(), &handle);
uv_tcp_connect(&req, (uv_stream_t*)&handle, addr, connect_cb);
uv_run(uv_default_loop()); // busy loops until connection times out
return 0;
}
After EINPROGRESS, there are zero active handles and one active request. That
in turn makes uv__poll_timeout() believe that it should merely poll, not block,
in epoll() / kqueue() / port_getn().
Sidestep that by artificially starting the handle on connect() and stopping it
again once the TCP handshake completes / is rejected / times out.
It's a slightly hacky approach because I don't want to change the ABI of the
stable branch. I'll address it properly in the master branch.
This commit is contained in:
parent
1a6b6b781c
commit
0971598d02
@ -93,7 +93,8 @@ enum {
|
|||||||
UV_STREAM_WRITABLE = 0x40, /* The stream is writable */
|
UV_STREAM_WRITABLE = 0x40, /* The stream is writable */
|
||||||
UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */
|
UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */
|
||||||
UV_TCP_NODELAY = 0x100, /* Disable Nagle. */
|
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,
|
inline static void uv__req_init(uv_loop_t* loop,
|
||||||
|
|||||||
@ -790,6 +790,12 @@ static void uv__stream_connect(uv_stream_t* stream) {
|
|||||||
stream->connect_req = NULL;
|
stream->connect_req = NULL;
|
||||||
uv__req_unregister(stream->loop, req);
|
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) {
|
if (req->cb) {
|
||||||
uv__set_sys_error(stream->loop, error);
|
uv__set_sys_error(stream->loop, error);
|
||||||
req->cb(req, error ? -1 : 0);
|
req->cb(req, error ? -1 : 0);
|
||||||
|
|||||||
@ -109,8 +109,17 @@ static int uv__connect(uv_connect_t* req,
|
|||||||
while (r == -1 && errno == EINTR);
|
while (r == -1 && errno == EINTR);
|
||||||
|
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
if (errno == EINPROGRESS)
|
if (errno == EINPROGRESS) {
|
||||||
; /* not an error */
|
/* 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)
|
else if (errno == ECONNREFUSED)
|
||||||
/* If we get a ECONNREFUSED wait until the next tick to report the
|
/* If we get a ECONNREFUSED wait until the next tick to report the
|
||||||
* error. Solaris wants to report immediately--other unixes want to
|
* error. Solaris wants to report immediately--other unixes want to
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user