Skip to content

Commit 5848e98

Browse files
src: fix perfetto session reader teardown race
PerfettoSessionReader::Deleter issues a final ReadTrace() and then Stop()s the session. Perfetto delivers the read data and the stop notification as independent tasks on its own thread, so the stop could win, close the uv handles and delete the reader while a ReadTraceCallback bound to the raw pointer was still queued. That callback then locked a destroyed mutex and signalled a closed uv_async_t. Only tear the reader down once the owner has released it, the session has stopped and no read is in flight, and have both Perfetto-thread callbacks update their flag and signal under chunks_mutex_ so the loop thread cannot free the reader in between. Refs: #64565 🏠 Remote-Dev: homespace
1 parent 9f04fcd commit 5848e98

2 files changed

Lines changed: 26 additions & 14 deletions

File tree

‎src/tracing/agent_perfetto.cc‎

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ void PerfettoSessionReader::Deleter::operator()(
155155
ptr->tracing_session_->FlushBlocking();
156156
ptr->Read();
157157
ptr->tracing_session_->Stop();
158+
Mutex::ScopedLock lock(ptr->chunks_mutex_);
159+
ptr->owner_released_ = true;
160+
uv_async_send(&ptr->read_async_);
158161
}
159162

160163
PerfettoSessionReader::PerfettoSessionReader(
@@ -201,12 +204,11 @@ void PerfettoSessionReader::Read() {
201204

202205
void PerfettoSessionReader::ReadTraceCallback(
203206
perfetto::TracingSession::ReadTraceCallbackArgs args) {
204-
// On Perfetto internal thread.
205-
{
206-
Mutex::ScopedLock lock(chunks_mutex_);
207-
if (args.size > 0)
208-
pending_chunks_.emplace_back(args.data, args.data + args.size);
209-
}
207+
// On Perfetto internal thread. Signal under the lock so MaybeStartTeardown()
208+
// cannot free |this| while a callback is still running.
209+
Mutex::ScopedLock lock(chunks_mutex_);
210+
if (args.size > 0)
211+
pending_chunks_.emplace_back(args.data, args.data + args.size);
210212
// A single ReadTrace() cycle can yield multiple callbacks; the last one has
211213
// has_more == false, which clears read_in_progress_ so the next timer tick
212214
// can start a new read.
@@ -215,6 +217,8 @@ void PerfettoSessionReader::ReadTraceCallback(
215217
}
216218

217219
void PerfettoSessionReader::SessionStopCallback() {
220+
// On Perfetto internal thread.
221+
Mutex::ScopedLock lock(chunks_mutex_);
218222
stop_requested_ = true;
219223
uv_async_send(&read_async_);
220224
}
@@ -235,16 +239,22 @@ void PerfettoSessionReader::OnReadAsync(uv_async_t* async) {
235239
chunks_to_write.pop_front();
236240
}
237241

238-
if (reader->stop_requested_ && reader->handles_pending_close_ == 0) {
239-
reader->writer_->Flush(true);
242+
reader->MaybeStartTeardown();
243+
}
240244

241-
reader->handles_pending_close_ = 2;
242-
uv_timer_stop(&reader->read_timer_);
243-
uv_close(reinterpret_cast<uv_handle_t*>(&reader->read_async_),
244-
OnHandleClose);
245-
uv_close(reinterpret_cast<uv_handle_t*>(&reader->read_timer_),
246-
OnHandleClose);
245+
void PerfettoSessionReader::MaybeStartTeardown() {
246+
if (handles_pending_close_ != 0) return;
247+
{
248+
Mutex::ScopedLock lock(chunks_mutex_);
249+
if (!owner_released_ || !stop_requested_ || read_in_progress_) return;
247250
}
251+
252+
writer_->Flush(true);
253+
254+
handles_pending_close_ = 2;
255+
uv_timer_stop(&read_timer_);
256+
uv_close(reinterpret_cast<uv_handle_t*>(&read_async_), OnHandleClose);
257+
uv_close(reinterpret_cast<uv_handle_t*>(&read_timer_), OnHandleClose);
248258
}
249259

250260
// static

‎src/tracing/agent_perfetto.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class PerfettoSessionReader final {
6767
void ReadTraceCallback(perfetto::TracingSession::ReadTraceCallbackArgs args);
6868
void SessionStopCallback();
6969
void Read();
70+
void MaybeStartTeardown();
7071

7172
static void OnReadAsync(uv_async_t* async);
7273
static void OnReadTimer(uv_timer_t* timer);
@@ -78,6 +79,7 @@ class PerfettoSessionReader final {
7879
int handles_pending_close_ = 0;
7980
std::atomic<bool> stop_requested_ = false;
8081
std::atomic<bool> read_in_progress_ = false;
82+
bool owner_released_ = false; // Guarded by chunks_mutex_.
8183

8284
Mutex chunks_mutex_;
8385
std::list<std::vector<char>> pending_chunks_;

0 commit comments

Comments
 (0)