Skip to content

Commit

Permalink
Merge pull request #48076 from ClickHouse/backport/22.3/47953
Browse files Browse the repository at this point in the history
Backport #47953 to 22.3: Fix tsan error lock-order-inversion
  • Loading branch information
Avogar authored Mar 28, 2023
2 parents 52a387a + baeec17 commit 297b4dd
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 11 deletions.
27 changes: 18 additions & 9 deletions src/Interpreters/ProcessList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ QueryStatus::~QueryStatus()
{
#if !defined(NDEBUG)
/// Check that all executors were invalidated.
for (const auto & e : executors)
for (const auto & [_, e] : executors)
assert(!e->executor);
#endif

Expand Down Expand Up @@ -379,7 +379,9 @@ CancellationCode QueryStatus::cancelQuery(bool)
{
/// Create a snapshot of executors under a mutex.
std::lock_guard lock(executors_mutex);
executors_snapshot = executors;
executors_snapshot.reserve(executors.size());
for (const auto & [_, e] : executors)
executors_snapshot.push_back(e);
}

/// We should call cancel() for each executor with unlocked executors_mutex, because
Expand All @@ -401,17 +403,24 @@ CancellationCode QueryStatus::cancelQuery(bool)
void QueryStatus::addPipelineExecutor(PipelineExecutor * e)
{
std::lock_guard lock(executors_mutex);
assert(std::find_if(executors.begin(), executors.end(), [e](const ExecutorHolderPtr & x){ return x->executor == e; }) == executors.end());
executors.push_back(std::make_shared<ExecutorHolder>(e));
assert(!executors.contains(e));
executors[e] = std::make_shared<ExecutorHolder>(e);
}

void QueryStatus::removePipelineExecutor(PipelineExecutor * e)
{
std::lock_guard lock(executors_mutex);
auto it = std::find_if(executors.begin(), executors.end(), [e](const ExecutorHolderPtr & x){ return x->executor == e; });
assert(it != executors.end());
/// Invalidate executor pointer inside holder, but don't remove holder from the executors (to avoid race with cancelQuery)
(*it)->remove();
ExecutorHolderPtr executor_holder;

{
std::lock_guard lock(executors_mutex);
assert(executors.contains(e));
executor_holder = executors[e];
executors.erase(e);
}

/// Invalidate executor pointer inside holder.
/// We should do it with released executors_mutex to avoid possible lock order inversion.
executor_holder->remove();
}

bool QueryStatus::checkTimeLimit()
Expand Down
4 changes: 2 additions & 2 deletions src/Interpreters/ProcessList.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ class QueryStatus : public WithContext

using ExecutorHolderPtr = std::shared_ptr<ExecutorHolder>;

/// Array of PipelineExecutors to be cancelled when a cancelQuery is received
std::vector<ExecutorHolderPtr> executors;
/// Container of PipelineExecutors to be cancelled when a cancelQuery is received
std::unordered_map<PipelineExecutor *, ExecutorHolderPtr> executors;

enum QueryStreamsStatus
{
Expand Down

0 comments on commit 297b4dd

Please sign in to comment.