vm: fix race condition in watchdog cleanup

Previous code was calling uv_loop_delete() directly on a running loop,
which led to race condition aborts/segfaults within libuv.  This change
changes the watchdog thread to call uv_run() with UV_RUN_ONCE so that
the call exits after either the timer times out or uv_async_send() is
called from the main thread in Watchdog::Destroy().  The timer/async
handles are then closed and uv_run() with UV_RUN_DEFAULT is called so
that libuv has a chance to cleanup before the thread exits.  The main
thread meanwhile calls uv_thread_join() and then uv_loop_delete() to
complete the cleanup.

Fixes #5564.