Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
src: fix race condition in ~NodeTraceBuffer
Libuv does not guarantee that handles have their close
callbacks called in the order in which they were added
(and in fact, currently calls them in reverse order).

This patch ensures that the `flush_signal_` handle
is no longer in use (i.e. its close callback has already
been run) when we signal to the main thread that
`~NodeTraceBuffer` may be destroyed.

Credit for debugging goes to Gireesh Punathil.

Fixes: #25512
  • Loading branch information
addaleax committed Feb 2, 2019
commit 60311fe2fa8d6e0670d8b51f703ec2738fc35686
25 changes: 18 additions & 7 deletions src/tracing/node_trace_buffer.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "tracing/node_trace_buffer.h"
#include "util-inl.h"

namespace node {
namespace tracing {
Expand Down Expand Up @@ -170,15 +171,25 @@ void NodeTraceBuffer::NonBlockingFlushSignalCb(uv_async_t* signal) {

// static
void NodeTraceBuffer::ExitSignalCb(uv_async_t* signal) {
NodeTraceBuffer* buffer = reinterpret_cast<NodeTraceBuffer*>(signal->data);
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->flush_signal_), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->exit_signal_),
NodeTraceBuffer* buffer =
ContainerOf(&NodeTraceBuffer::exit_signal_, signal);

// Close both flush_signal_ and exit_signal_.
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->flush_signal_),
[](uv_handle_t* signal) {
NodeTraceBuffer* buffer =
ContainerOf(&NodeTraceBuffer::flush_signal_,
reinterpret_cast<uv_async_t*>(signal));

uv_close(reinterpret_cast<uv_handle_t*>(&buffer->exit_signal_),
[](uv_handle_t* signal) {
NodeTraceBuffer* buffer =
reinterpret_cast<NodeTraceBuffer*>(signal->data);
Mutex::ScopedLock scoped_lock(buffer->exit_mutex_);
buffer->exited_ = true;
buffer->exit_cond_.Signal(scoped_lock);
ContainerOf(&NodeTraceBuffer::exit_signal_,
reinterpret_cast<uv_async_t*>(signal));
Mutex::ScopedLock scoped_lock(buffer->exit_mutex_);
buffer->exited_ = true;
buffer->exit_cond_.Signal(scoped_lock);
});
});
}

Expand Down