vm: fix race condition in timeout

Eliminate a race condition between uv_async_send and the closing of the
corresponding handle.

Also made errors in Watchdog constructor call abort()

Fixes #6088
This commit is contained in:
Alexis Campailla 2013-12-17 07:43:38 -08:00 committed by Timothy J Fontaine
parent 95ee84fabe
commit cdc038ceb6
2 changed files with 16 additions and 24 deletions

View File

@ -20,6 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE. // USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "node_watchdog.h" #include "node_watchdog.h"
#include "util.h"
#include <assert.h> #include <assert.h>
namespace node { namespace node {
@ -27,27 +28,21 @@ namespace node {
using v8::V8; using v8::V8;
Watchdog::Watchdog(uint64_t ms) : thread_created_(false), destroyed_(false) { Watchdog::Watchdog(uint64_t ms) : destroyed_(false) {
loop_ = uv_loop_new(); loop_ = uv_loop_new();
if (!loop_) CHECK(loop_);
return;
int rc = uv_async_init(loop_, &async_, &Watchdog::Async); int rc = uv_async_init(loop_, &async_, &Watchdog::Async);
assert(rc == 0); CHECK(0 == rc); // NOLINT(readability/check)
rc = uv_timer_init(loop_, &timer_); rc = uv_timer_init(loop_, &timer_);
assert(rc == 0); CHECK(0 == rc); // NOLINT(readability/check)
rc = uv_timer_start(&timer_, &Watchdog::Timer, ms, 0); rc = uv_timer_start(&timer_, &Watchdog::Timer, ms, 0);
if (rc) { CHECK(0 == rc); // NOLINT(readability/check)
return;
}
rc = uv_thread_create(&thread_, &Watchdog::Run, this); rc = uv_thread_create(&thread_, &Watchdog::Run, this);
if (rc) { CHECK(0 == rc); // NOLINT(readability/check)
return;
}
thread_created_ = true;
} }
@ -66,14 +61,15 @@ void Watchdog::Destroy() {
return; return;
} }
if (thread_created_) { uv_async_send(&async_);
uv_async_send(&async_); uv_thread_join(&thread_);
uv_thread_join(&thread_);
}
if (loop_) { uv_close(reinterpret_cast<uv_handle_t*>(&async_), NULL);
uv_loop_delete(loop_);
} // UV_RUN_DEFAULT so that libuv has a chance to clean up.
uv_run(loop_, UV_RUN_DEFAULT);
uv_loop_delete(loop_);
destroyed_ = true; destroyed_ = true;
} }
@ -86,11 +82,8 @@ void Watchdog::Run(void* arg) {
uv_run(wd->loop_, UV_RUN_ONCE); uv_run(wd->loop_, UV_RUN_ONCE);
// Loop ref count reaches zero when both handles are closed. // Loop ref count reaches zero when both handles are closed.
uv_close(reinterpret_cast<uv_handle_t*>(&wd->async_), NULL); // Close the timer handle on this side and let Destroy() close async_
uv_close(reinterpret_cast<uv_handle_t*>(&wd->timer_), NULL); uv_close(reinterpret_cast<uv_handle_t*>(&wd->timer_), NULL);
// UV_RUN_DEFAULT so that libuv has a chance to clean up.
uv_run(wd->loop_, UV_RUN_DEFAULT);
} }

View File

@ -45,7 +45,6 @@ class Watchdog {
uv_loop_t* loop_; uv_loop_t* loop_;
uv_async_t async_; uv_async_t async_;
uv_timer_t timer_; uv_timer_t timer_;
bool thread_created_;
bool destroyed_; bool destroyed_;
}; };