Skip to content

Commit

Permalink
loop_close: Avoid infinite loop, and log it.
Browse files Browse the repository at this point in the history
Avoids a hang, and also helps diagnose issues like:

neovim#6594 (comment)
  • Loading branch information
justinmk committed Jun 6, 2017
1 parent f83d733 commit 698ec9e
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 7 deletions.
21 changes: 18 additions & 3 deletions src/nvim/event/loop.c
Expand Up @@ -8,6 +8,7 @@

#include "nvim/event/loop.h"
#include "nvim/event/process.h"
#include "nvim/log.h"

#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/loop.c.generated.h"
Expand Down Expand Up @@ -78,20 +79,34 @@ void loop_on_put(MultiQueue *queue, void *data)
uv_stop(&loop->uv);
}

void loop_close(Loop *loop, bool wait)
/// @returns false if the loop could not be closed gracefully
bool loop_close(Loop *loop, bool wait)
{
bool rv = true;
uv_mutex_destroy(&loop->mutex);
uv_close((uv_handle_t *)&loop->children_watcher, NULL);
uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
uv_close((uv_handle_t *)&loop->poll_timer, NULL);
uv_close((uv_handle_t *)&loop->async, NULL);
do {
uint64_t start = wait ? os_hrtime() : 0;
while (true) {
uv_run(&loop->uv, wait ? UV_RUN_DEFAULT : UV_RUN_NOWAIT);
} while (uv_loop_close(&loop->uv) && wait);
if (!uv_loop_close(&loop->uv) || !wait) {
break;
}
if (os_hrtime() - start >= 2 * 1000000000) {
// Some libuv resource was not correctly deref'd. Log and bail.
rv = false;
ELOG("uv_loop_close() hang?");
log_uv_handles(&loop->uv);
break;
}
}
multiqueue_free(loop->fast_events);
multiqueue_free(loop->thread_events);
multiqueue_free(loop->events);
kl_destroy(WatcherPtr, loop->children);
return rv;
}

void loop_purge(Loop *loop)
Expand Down
7 changes: 4 additions & 3 deletions src/nvim/main.c
Expand Up @@ -153,10 +153,11 @@ void event_init(void)
terminal_init();
}

void event_teardown(void)
/// @returns false if main_loop could not be closed gracefully
bool event_teardown(void)
{
if (!main_loop.events) {
return;
return true;
}

multiqueue_process_events(main_loop.events);
Expand All @@ -168,7 +169,7 @@ void event_teardown(void)
signal_teardown();
terminal_teardown();

loop_close(&main_loop, true);
return loop_close(&main_loop, true);
}

/// Performs early initialization.
Expand Down
4 changes: 3 additions & 1 deletion src/nvim/os_unix.c
Expand Up @@ -141,7 +141,9 @@ void mch_exit(int r) FUNC_ATTR_NORETURN
ui_flush();
ml_close_all(true); // remove all memfiles

event_teardown();
if (!event_teardown() && r == 0) {
r = 1; // Exit with error if main_loop did not teardown gracefully.
}
stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)

#ifdef EXITFREE
Expand Down
2 changes: 2 additions & 0 deletions test/README.md
Expand Up @@ -2,6 +2,8 @@

Tests are run by `/cmake/RunTests.cmake` file, using busted.

For some failures, `.nvimlog` (or `$NVIM_LOG_FILE`) may provide insight.

## Directory structure

Directories with tests: `/test/benchmark` for benchmarks, `/test/functional` for
Expand Down

0 comments on commit 698ec9e

Please sign in to comment.