Skip to content

Commit e28d891

Browse files
committed
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. The same applies for `~NodeTraceWriter`. Credit for debugging goes to Gireesh Punathil. Fixes: #25512 Co-authored-by: Gireesh Punathil <gpunathi@in.ibm.com> PR-URL: #25896 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Eugene Ostroukhov <eostroukhov@google.com>
1 parent 025a7c3 commit e28d891

File tree

2 files changed

+33
-17
lines changed

2 files changed

+33
-17
lines changed

src/tracing/node_trace_buffer.cc

+18-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "tracing/node_trace_buffer.h"
2+
#include "util-inl.h"
23

34
namespace node {
45
namespace tracing {
@@ -170,15 +171,25 @@ void NodeTraceBuffer::NonBlockingFlushSignalCb(uv_async_t* signal) {
170171

171172
// static
172173
void NodeTraceBuffer::ExitSignalCb(uv_async_t* signal) {
173-
NodeTraceBuffer* buffer = reinterpret_cast<NodeTraceBuffer*>(signal->data);
174-
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->flush_signal_), nullptr);
175-
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->exit_signal_),
174+
NodeTraceBuffer* buffer =
175+
ContainerOf(&NodeTraceBuffer::exit_signal_, signal);
176+
177+
// Close both flush_signal_ and exit_signal_.
178+
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->flush_signal_),
176179
[](uv_handle_t* signal) {
180+
NodeTraceBuffer* buffer =
181+
ContainerOf(&NodeTraceBuffer::flush_signal_,
182+
reinterpret_cast<uv_async_t*>(signal));
183+
184+
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->exit_signal_),
185+
[](uv_handle_t* signal) {
177186
NodeTraceBuffer* buffer =
178-
reinterpret_cast<NodeTraceBuffer*>(signal->data);
179-
Mutex::ScopedLock scoped_lock(buffer->exit_mutex_);
180-
buffer->exited_ = true;
181-
buffer->exit_cond_.Signal(scoped_lock);
187+
ContainerOf(&NodeTraceBuffer::exit_signal_,
188+
reinterpret_cast<uv_async_t*>(signal));
189+
Mutex::ScopedLock scoped_lock(buffer->exit_mutex_);
190+
buffer->exited_ = true;
191+
buffer->exit_cond_.Signal(scoped_lock);
192+
});
182193
});
183194
}
184195

src/tracing/node_trace_writer.cc

+15-10
Original file line numberDiff line numberDiff line change
@@ -218,18 +218,23 @@ void NodeTraceWriter::AfterWrite() {
218218
void NodeTraceWriter::ExitSignalCb(uv_async_t* signal) {
219219
NodeTraceWriter* trace_writer =
220220
ContainerOf(&NodeTraceWriter::exit_signal_, signal);
221+
// Close both flush_signal_ and exit_signal_.
221222
uv_close(reinterpret_cast<uv_handle_t*>(&trace_writer->flush_signal_),
222-
nullptr);
223-
uv_close(reinterpret_cast<uv_handle_t*>(&trace_writer->exit_signal_),
224223
[](uv_handle_t* signal) {
225-
NodeTraceWriter* trace_writer =
226-
ContainerOf(&NodeTraceWriter::exit_signal_,
227-
reinterpret_cast<uv_async_t*>(signal));
228-
Mutex::ScopedLock scoped_lock(trace_writer->request_mutex_);
229-
trace_writer->exited_ = true;
230-
trace_writer->exit_cond_.Signal(scoped_lock);
231-
});
224+
NodeTraceWriter* trace_writer =
225+
ContainerOf(&NodeTraceWriter::flush_signal_,
226+
reinterpret_cast<uv_async_t*>(signal));
227+
uv_close(
228+
reinterpret_cast<uv_handle_t*>(&trace_writer->exit_signal_),
229+
[](uv_handle_t* signal) {
230+
NodeTraceWriter* trace_writer =
231+
ContainerOf(&NodeTraceWriter::exit_signal_,
232+
reinterpret_cast<uv_async_t*>(signal));
233+
Mutex::ScopedLock scoped_lock(trace_writer->request_mutex_);
234+
trace_writer->exited_ = true;
235+
trace_writer->exit_cond_.Signal(scoped_lock);
236+
});
237+
});
232238
}
233-
234239
} // namespace tracing
235240
} // namespace node

0 commit comments

Comments
 (0)