Skip to content

[BugFix] Unschedule global RF timer in PipelineDriver destructor#76252

Open
jenwitteng wants to merge 1 commit into
StarRocks:mainfrom
jenwitteng:fix/main/pipeline-driver-dtor-unschedule-global-rf-timer
Open

[BugFix] Unschedule global RF timer in PipelineDriver destructor#76252
jenwitteng wants to merge 1 commit into
StarRocks:mainfrom
jenwitteng:fix/main/pipeline-driver-dtor-unschedule-global-rf-timer

Conversation

@jenwitteng

Copy link
Copy Markdown
Contributor

Why I'm doing:

A BE/CN aborts during shutdown with std::bad_weak_ptr:

terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr
  ... starrocks::pipeline::RunTimerTask(void*)
  ... bthread::TimerThread::Task::run_and_delete()
  ... bthread::TimerThread::run()

The global runtime-filter timer (RFScanWaitTimeout) is owned solely by
PipelineDriver::_global_rf_timer, and it is only unscheduled in
finalize(). Queued or blocked drivers that are abandoned when the driver
executor is closed at shutdown are destroyed without going through
finalize(), so the bthread TimerThread — which keeps only a raw pointer to
the task — can still fire on a task whose last owning shared_ptr is already
gone. PipelineTimerTask::doRun() then calls shared_from_this() on the freed
object and throws std::bad_weak_ptr, aborting the process.

This is the abandoned-driver counterpart to the query-context-destruction race
fixed in #73082, which unscheduled only from finalize(). The throwing
shared_from_this() in doRun() was introduced when PipelineTimerTask
became enable_shared_from_this in #72058.

What I'm doing:

  • Extract the unschedule into _unschedule_global_rf_timer() and call it from
    both finalize() and ~PipelineDriver().
  • The helper moves the shared_ptr into a local so the task stays alive across
    unschedule_and_join(), closing the window where a concurrently running
    doRun() could observe a zero use_count.
  • Add unit tests: the destructor unschedules a pending global RF timer, and the
    destructor blocks until an in-flight timer task returns.

Verified: both changed translation units compile cleanly with the project
toolchain (gcc-12, -std=gnu++23), zero diagnostics.

What type of PR is this:

  • BugFix
  • Feature
  • Enhancement
  • Refactor
  • UT
  • Doc
  • Tool

Does this PR entail a change in behavior?

  • Yes, this PR will result in a change in behavior.
  • No, this PR will not result in a change in behavior.

Checklist:

  • I have added test cases for my bug fix or my new feature
  • This pr needs user documentation (for new or modified features or behaviors)
    • I have added documentation for my new feature or new function
    • This pr needs auto generate documentation
  • This is a backport pr

Bugfix cherry-pick branch check:

  • I have checked the version labels which the pr will be auto-backported to the target branch
    • 4.1
    • 4.0
    • 3.5

@CelerData-Reviewer

Copy link
Copy Markdown

@codex review

@github-actions github-actions Bot requested review from ZiheLiu and stdpain July 13, 2026 04:17
A queued or blocked PipelineDriver that is abandoned when the driver executor
is closed during shutdown is destroyed without going through finalize(), which
was the only place the global runtime-filter timer (RFScanWaitTimeout) was
unscheduled. Because the bthread TimerThread keeps only a raw pointer to the
task, it can then fire on a task whose sole owning shared_ptr
(PipelineDriver::_global_rf_timer) is already gone. PipelineTimerTask::doRun()
calls shared_from_this() on that freed object and throws std::bad_weak_ptr,
aborting the process:

    terminate called after throwing an instance of 'std::bad_weak_ptr'
      RunTimerTask -> bthread::TimerThread::run

This is the abandoned-driver counterpart to the query-context-destruction race
fixed in StarRocks#73082, which only unscheduled from finalize(); the throwing
shared_from_this() in doRun() was introduced in StarRocks#72058.

Move the unschedule into a helper called from both finalize() and
~PipelineDriver(). The helper moves the shared_ptr into a local so the task
stays alive across unschedule_and_join(), closing the window where a
concurrently running doRun() could observe a zero use_count.

Add unit tests covering the destructor unscheduling a pending timer and the
destructor blocking until an in-flight timer task finishes.

Signed-off-by: Jenwit Amonpongitsara <jenwit.amonpongitsara@agoda.com>
@jenwitteng jenwitteng force-pushed the fix/main/pipeline-driver-dtor-unschedule-global-rf-timer branch from b94e4bb to 2c184a5 Compare July 13, 2026 04:18
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: b94e4bb620

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@stdpain stdpain left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's OK for a hot fix

@github-actions

Copy link
Copy Markdown
Contributor

[Java-Extensions Incremental Coverage Report]

pass : 0 / 0 (0%)

@github-actions

Copy link
Copy Markdown
Contributor

[FE Incremental Coverage Report]

pass : 0 / 0 (0%)

@github-actions

Copy link
Copy Markdown
Contributor

[BE Incremental Coverage Report]

pass : 7 / 7 (100.00%)

file detail

path covered_line new_line coverage not_covered_line_detail
🔵 be/src/exec/runtime/pipeline_driver.cpp 7 7 100.00% []

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants