diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c index e2efb4db22..04d06fa9c1 100644 --- a/src/nvim/event/socket.c +++ b/src/nvim/event/socket.c @@ -79,6 +79,13 @@ int socket_watcher_init(Loop *loop, SocketWatcher *watcher, const char *endpoint return 0; } +/// Callback for closing a handle initialized by socket_connect(). +static void connect_close_cb(uv_handle_t *handle) +{ + bool *closed = handle->data; + *closed = true; +} + int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb) FUNC_ATTR_NONNULL_ALL { @@ -194,7 +201,7 @@ static void connect_cb(uv_connect_t *req, int status) *ret_status = status; uv_handle_t *handle = (uv_handle_t *)req->handle; if (status != 0 && !uv_is_closing(handle)) { - uv_close(handle, NULL); + uv_close(handle, connect_close_cb); } } @@ -202,6 +209,7 @@ bool socket_connect(Loop *loop, RStream *stream, bool is_tcp, const char *addres const char **error) { bool success = false; + bool closed; int status; uv_connect_t req; req.data = &status; @@ -243,21 +251,21 @@ tcp_retry: uv_pipe_connect(&req, pipe, address, connect_cb); uv_stream = (uv_stream_t *)pipe; } + uv_stream->data = &closed; + closed = false; status = 1; LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, timeout, status != 1); if (status == 0) { stream_init(NULL, &stream->s, -1, uv_stream); + assert(uv_stream->data != &closed); // Should have been set by stream_init(). success = true; } else { if (!uv_is_closing((uv_handle_t *)uv_stream)) { - uv_close((uv_handle_t *)uv_stream, NULL); - if (status == 1) { - // The uv_close() above will make libuv call connect_cb() with UV_ECANCELED. - // Make sure connect_cb() has been called here, as if it's called after this - // function ends it will cause a stack-use-after-scope. - LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, -1, status != 1); - } + uv_close((uv_handle_t *)uv_stream, connect_close_cb); } + // Wait for the close callback to arrive before retrying or returning, otherwise + // it may lead to a hang or stack-use-after-return. + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, -1, closed); if (is_tcp && addrinfo->ai_next) { addrinfo = addrinfo->ai_next; diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 3445dbc3b8..075b9b8ea4 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -475,7 +475,6 @@ describe('channels', function() end) it('in "tcp" mode', function() - skip(not is_os('linux'), 'FIXME: hangs on non-Linux') eq( 'Vim:connection failed: connection refused', pcall_err(fn.sockconnect, 'tcp', '127.0.0.1:0')