mrhhsg opened a new pull request, #69270:
URL: https://github.com/apache/doris/pull/69270

   ### What problem does this PR solve?
   
   Issue Number: None
   
   Related PR: #69208
   
   Problem Summary: `PipelineTask::stop_if_finished()` is called by 
`FragmentMgr::cancel_worker()` (through 
`PipelineFragmentContext::clear_finished_tasks()`) on the background cancel 
thread. It copied the `_sink` shared_ptr without any lock. At the same time a 
scheduler worker can run `PipelineTask::finalize()` for the same task, which 
resets `_sink` while holding `_dependency_lock`. Copying and resetting the same 
shared_ptr instance from two threads without synchronization is a data race 
(undefined behavior): the cancel thread can observe a torn pointer/control 
block and then call `is_finished()` on an object that is being released. Every 
other cross-thread reader of `_sink` (`debug_string()`, the hybrid scheduler 
submit path) is already synchronized; `stop_if_finished()` was the only one 
left.
   
   Fix: take `_dependency_lock` in `stop_if_finished()`, return if the task is 
already finalized, and copy `_sink` under the lock, following the pattern of 
`debug_string()`. `is_finished()` and `terminate()` are called after the lock 
is released, because `terminate()` acquires the same non-recursive mutex. A 
debug point inside the critical section lets the unit test hold the lock 
deterministically.
   
   This is the branch-4.2 backport of #69208. branch-4.2 still has a single 
`_dependency_lock` instead of the `_dependency_lifecycle_lock` used on master, 
and `stop_if_finished()` calls `terminate()` instead of 
`unblock_all_dependencies()`. Only those two branch-specific call sites are 
adapted; the fix intent and the test are unchanged.
   
   ### Release note
   
   None
   
   ### Check List (For Author)
   
   - Test: Unit Test
       - Added `PipelineTaskTest.TEST_STOP_IF_FINISHED_EXCLUDES_FINALIZE`, 
adapted from the original PR: it holds `stop_if_finished()` inside the critical 
section, checks that a concurrent `finalize()` is blocked and cannot reset 
`_sink`, and verifies that a finalized task is skipped without touching the 
cleared `_sink`.
       - All `PipelineTaskTest` cases pass: 23/23.
       - `clang-format --dry-run --Werror` passes on the two changed files.
       - No regression test: the race window is a few instructions wide and 
cannot be triggered reliably from SQL.
   - Behavior changed: No
   - Does this need documentation: No


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to