This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 99653480bc8 branch-4.1: [fix](pipeline) Make PipelineTask::set_running
an unconditional exchange #67032 (#68929)
99653480bc8 is described below
commit 99653480bc8cdc0ec827e4174d95c47b9012d699
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Oct 12 18:05:20 2026 +0800
branch-4.1: [fix](pipeline) Make PipelineTask::set_running an unconditional
exchange #67032 (#68929)
Cherry-picked from #67032
Co-authored-by: Raghvendra Singh <[email protected]>
Co-authored-by: Claude Fable 5 <[email protected]>
---
be/src/exec/pipeline/pipeline_task.h | 12 ++++--
be/test/exec/pipeline/pipeline_task_test.cpp | 58 ++++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/be/src/exec/pipeline/pipeline_task.h
b/be/src/exec/pipeline/pipeline_task.h
index 5b5da9dca83..7c4b4f92057 100644
--- a/be/src/exec/pipeline/pipeline_task.h
+++ b/be/src/exec/pipeline/pipeline_task.h
@@ -153,9 +153,15 @@ public:
bool is_running() { return _running.load(); }
virtual bool set_running(bool running) {
- bool old_value = !running;
- _running.compare_exchange_weak(old_value, running);
- return old_value;
+ // This is a mutual-exclusion gate: TaskScheduler::_do_work skips a
task when
+ // set_running(true) returns true, and RevokableTask delegates here so
an
+ // in-flight revocation excludes its underlying task. It must be an
+ // unconditional exchange: compare_exchange_weak may fail spuriously
on LL/SC
+ // architectures (aarch64), and a spurious failure with _running ==
false
+ // returns "was not running" WITHOUT setting the flag — two workers
then
+ // execute the same task concurrently and race on its local state
+ // (shared hash table / _places).
+ return _running.exchange(running);
}
virtual RuntimeState* runtime_state() const { return _state; }
diff --git a/be/test/exec/pipeline/pipeline_task_test.cpp
b/be/test/exec/pipeline/pipeline_task_test.cpp
index 33b94ebfd83..104c381efb1 100644
--- a/be/test/exec/pipeline/pipeline_task_test.cpp
+++ b/be/test/exec/pipeline/pipeline_task_test.cpp
@@ -1857,4 +1857,62 @@ TEST_F(PipelineTaskTest, TEST_TERMINATE_RACE_FIX) {
config::enable_debug_points = false;
}
+TEST_F(PipelineTaskTest, TEST_SET_RUNNING_MUTUAL_EXCLUSION) {
+ auto num_instances = 1;
+ auto pip_id = 0;
+ auto task_id = 0;
+ auto pip = std::make_shared<Pipeline>(pip_id, num_instances,
num_instances);
+ {
+ OperatorPtr source_op;
+ source_op.reset(new DummyOperator());
+ EXPECT_TRUE(pip->add_operator(source_op, num_instances).ok());
+
+ int op_id = 1;
+ int node_id = 2;
+ int dest_id = 3;
+ DataSinkOperatorPtr sink_op;
+ sink_op.reset(new DummySinkOperatorX(op_id, node_id, dest_id));
+ EXPECT_TRUE(pip->set_sink(sink_op).ok());
+ }
+ auto profile = std::make_shared<RuntimeProfile>("Pipeline : " +
std::to_string(pip_id));
+ std::map<int,
+ std::pair<std::shared_ptr<BasicSharedState>,
std::vector<std::shared_ptr<Dependency>>>>
+ shared_state_map;
+ auto task = std::make_shared<PipelineTask>(pip, task_id,
_runtime_state.get(), _context,
+ profile.get(),
shared_state_map, task_id);
+
+ // set_running(true) is the scheduler's double-execute gate (and
RevokableTask's mutual
+ // exclusion with its underlying task): at most one caller may observe
"was not running"
+ // until the matching set_running(false). The former compare_exchange_weak
implementation
+ // could fail spuriously on LL/SC architectures and report acquisition
without setting
+ // the flag, admitting two concurrent holders.
+ std::atomic<int> holders {0};
+ std::atomic<int64_t> acquired {0};
+ std::atomic<bool> violation {false};
+ constexpr int kThreads = 8;
+ constexpr int kItersPerThread = 200000;
+ std::vector<std::thread> threads;
+ threads.reserve(kThreads);
+ for (int t = 0; t < kThreads; ++t) {
+ threads.emplace_back([&]() {
+ for (int i = 0; i < kItersPerThread; ++i) {
+ if (!task->set_running(true)) {
+ if (holders.fetch_add(1) != 0) {
+ violation = true;
+ }
+ acquired.fetch_add(1, std::memory_order_relaxed);
+ holders.fetch_sub(1);
+ task->set_running(false);
+ }
+ }
+ });
+ }
+ for (auto& th : threads) {
+ th.join();
+ }
+ EXPECT_FALSE(violation.load()) << "two threads acquired the running gate
concurrently";
+ EXPECT_GT(acquired.load(), 0);
+ EXPECT_FALSE(task->is_running());
+}
+
} // namespace doris
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]