loop_close: Avoid infinite loop, and log it.

Avoids a hang, and also helps diagnose issues like:

https://github.com/neovim/neovim/pull/6594#issuecomment-298321826
This commit is contained in:
Justin M. Keyes
2017-05-30 01:25:25 +02:00
parent f83d733318
commit 698ec9eb6e
4 changed files with 27 additions and 7 deletions

View File

@@ -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"
@@ -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)

View File

@@ -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);
@@ -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.

View File

@@ -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