Skip to content

src: fix perfetto session reader teardown race - #65611

Open
MarshallOfSound wants to merge 1 commit into
nodejs:mainfrom
MarshallOfSound:src-perfetto-reader-teardown
Open

src: fix perfetto session reader teardown race#65611
MarshallOfSound wants to merge 1 commit into
nodejs:mainfrom
MarshallOfSound:src-perfetto-reader-teardown

Conversation

@MarshallOfSound

Copy link
Copy Markdown
Member

PerfettoSessionReader::Deleter does a final ReadTrace() then Stop(). Perfetto runs the read callback and the stop callback as separate tasks on its own thread, so the stop can win: the loop thread closes the handles and deletes the reader while a ReadTraceCallback bound to the raw this is still queued, which then locks a destroyed mutex and signals a closed uv_async_t.

This defers teardown until the owner has released the reader, the session has stopped, and no read is in flight, and makes both Perfetto-thread callbacks flip their flag and signal under chunks_mutex_ so the loop thread can't free the reader in between.

Found with ASan (trace_events.createTracing().enable() + process exit, intermittently abort() in uv_mutex_lock from ReadTraceCallback).

Refs: #64565

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. labels Aug 28, 2026
@MarshallOfSound
MarshallOfSound force-pushed the src-perfetto-reader-teardown branch from f55e86d to 5848e98 Compare August 28, 2026 07:46
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: nodejs#64565
@MarshallOfSound
MarshallOfSound force-pushed the src-perfetto-reader-teardown branch from 5848e98 to aeccc1d Compare August 28, 2026 07:47
@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.06%. Comparing base (9f04fcd) to head (aeccc1d).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65611      +/-   ##
==========================================
- Coverage   90.07%   90.06%   -0.01%     
==========================================
  Files         751      751              
  Lines      254916   254916              
  Branches    48133    48121      -12     
==========================================
- Hits       229605   229586      -19     
- Misses      16496    16497       +1     
- Partials     8815     8833      +18     

see 36 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

std::list<std::vector<char>> chunks_to_write;
{
Mutex::ScopedLock lock(reader->chunks_mutex_);
std::swap(chunks_to_write, reader->pending_chunks_);

@legendecas legendecas Aug 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a simple fix could be:

  // Shutdown requested and no read outstanding.
  bool should_tear_down = false;
  {
    Mutex::ScopedLock lock(reader->chunks_mutex_);
    std::swap(chunks_to_write, reader->pending_chunks_);
    should_tear_down =
        reader->stop_requested_ && !reader->read_in_progress_;
  }

and later check it

  if (!should_tear_down || reader->handles_pending_close_ != 0) return;

  ... close handles.

With the Mutex::ScopedLock changes in both ReadTraceCallback and SessionStopCallback.

This does not need any additional mutexes or flags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants