Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
unix: fix EINPROGRESS busy loop
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bnoordhuis committed Jun 29, 2012
1 parent 1a6b6b7 commit 0971598
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/unix/internal.h
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions src/unix/stream.c
Expand Up @@ -790,6 +790,12 @@ static void uv__stream_connect(uv_stream_t* stream) {
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);
Expand Down
13 changes: 11 additions & 2 deletions src/unix/tcp.c
Expand Up @@ -109,8 +109,17 @@ static int uv__connect(uv_connect_t* req,
while (r == -1 && errno == EINTR);

if (r == -1) {
if (errno == EINPROGRESS)
; /* not an error */
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
Expand Down

0 comments on commit 0971598

Please sign in to comment.