diff options
| -rw-r--r-- | src/nvim/event/loop.c | 21 | ||||
| -rw-r--r-- | src/nvim/main.c | 7 | ||||
| -rw-r--r-- | src/nvim/os_unix.c | 4 | ||||
| -rw-r--r-- | test/README.md | 2 | 
4 files changed, 27 insertions, 7 deletions
| diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index c709ce9a1c..25701a1621 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -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) diff --git a/src/nvim/main.c b/src/nvim/main.c index 46607da6ea..19a661d7db 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -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. diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index fb648fbcf8..692bcc97f4 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -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 diff --git a/test/README.md b/test/README.md index 2857cc0ecf..01db5960cd 100644 --- a/test/README.md +++ b/test/README.md @@ -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  | 
