Skip to content

Simplify break_query_cycles#153185

Open
zetanumbers wants to merge 9 commits intorust-lang:mainfrom
zetanumbers:simplify_cycle_breaking
Open

Simplify break_query_cycles#153185
zetanumbers wants to merge 9 commits intorust-lang:mainfrom
zetanumbers:simplify_cycle_breaking

Conversation

@zetanumbers
Copy link
Contributor

@zetanumbers zetanumbers commented Feb 27, 2026

This is break_query_cycles implementation from #149849 (f466828) modified to be non-deterministic to match its current implementation and remove TreeNodeIndex plumbing code.

I thought I could be a bit more brave with changes.

@rustbot rustbot added A-query-system Area: The rustc query system (https://rustc-dev-guide.rust-lang.org/query.html) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 27, 2026
@rustbot
Copy link
Collaborator

rustbot commented Feb 27, 2026

r? @madsmtm

rustbot has assigned @madsmtm.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler, query-system
  • compiler, query-system expanded to 68 candidates
  • Random selection from 15 candidates

@zetanumbers
Copy link
Contributor Author

r? @nnethercote

Since you have already looked at #149849 I thought you could also take a look at this.

@rustbot rustbot assigned nnethercote and unassigned madsmtm Feb 27, 2026
@zetanumbers zetanumbers changed the title Simplify break_query_cycles Simplify break_query_cycles into a single function Feb 27, 2026
@zetanumbers
Copy link
Contributor Author

cc @Zoxc

@rust-log-analyzer

This comment has been minimized.

pub struct QueryWaiter<'tcx> {
pub query: Option<QueryJobId>,
pub condvar: Condvar,
pub span: Span,
Copy link
Contributor

Choose a reason for hiding this comment

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

The removal of spans here doesn't seem great.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leaving unused struct fields doesn't seem like a good long-term approach either. I can split its removal into a separate commit for a fast revert if necessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean it should still be used. Each query call has an associated span and that should be propagated to the cycle error.

Copy link
Contributor

Choose a reason for hiding this comment

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

But the span is currently unused, correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 6c56188

let mut root_query = None;
for (&query, info) in &query_map.map {
if info.job.parent.is_none() {
assert!(root_query.is_none(), "found multiple threads without start");
Copy link
Contributor

Choose a reason for hiding this comment

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

There can be multiple roots if queries are called in parallel from outside a query.

Copy link
Contributor Author

@zetanumbers zetanumbers Feb 27, 2026

Choose a reason for hiding this comment

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

How would queries ever be called in parallel from outside a query? If I would relax this requirement I would also like to know the context.

Copy link
Contributor

Choose a reason for hiding this comment

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

Say a program join(|| a(), || b()) where a() and b() are queries. It's trivial to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, this is not what I've meant. But I see you are saying I shouldn't assume anything more than that.

Done in 2e0fbb9.

@Zoxc
Copy link
Contributor

Zoxc commented Feb 28, 2026

Under the assumption that #149849 doesn't land, what's the motivation for these changes? Code size?

@nnethercote
Copy link
Contributor

I don't know much about the cycle breaking / waiter / latch stuff, so I will need to spend some time reading closely over that code before I can do a sensible review here. @zetanumbers, can you explain in some more detail what this PR is doing and why? Feel free to assume I don't know anything and make your explanation as detailed as you like :) Thank you.

@zetanumbers
Copy link
Contributor Author

Under the assumption that #149849 doesn't land, what's the motivation for these changes? Code size?

Reduction of code complexity. We don't need 8 functions do to this after considering two theorems I've written down mid break_query_cycles.

@zetanumbers
Copy link
Contributor Author

And I had draft of a code removing "rustc query cycle handler" thread from deadlock_handler for this break_query_cycles implementation. I wrote it for reasons described in #149849 (comment) but later opted for a minimal change from try_lock to lock there instead:

// Latch mutexes should be at least about to unlock as we do not hold it anywhere too long
let lock = latch.info.lock();

@nnethercote
Copy link
Contributor

I started looking over the existing break_query_cycles implementation today and my head is spinning. This PR removes 150 lines of code and multiple functions to I'm positively inclined towards it based on that alone.

@zetanumbers: you said this matches the current implementation. Is it exactly the same? You haven't given the expanded explanation I requested yet so I'm still guessing. As a newcomer to this code both the old code and the new code in this PR are very difficult to follow.

#[allow(rustc::potential_query_instability)]
fn find_cycle_in_graph<'tcx>(
query_map: &QueryJobMap<'tcx>,
) -> (QueryJobId, usize, CycleError<QueryStackDeferred<'tcx>>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The function comment should explain the return type, especially the usize. Alternatively, you could introduce a new struct and use that instead of the tuple.

.map
.iter()
.find(|(_, info)| info.job.parent.is_none())
.expect("no root query was found");
Copy link
Contributor

Choose a reason for hiding this comment

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

If you use values() instead of iter() you won't need to filter out the keys.

id: QueryJobId,
span: Span,
/// Waiter index or `usize::MAX` if subquery was executed
waiter_idx: usize,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you change to Option<usize> instead of using MAX for the exceptional case?


true
// Per statement above we should have wait at either of two occurrences of the duplicate query
let waiter_idx = if last.waiter_idx != usize::MAX {
Copy link
Contributor

Choose a reason for hiding this comment

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

If last.waiter_idx is an Option<usize> you can use unwrap_or_else here.

for _ in 0..wakelist.len() {
rustc_thread_pool::mark_unblocked(registry);
}
// And so this `Vec::remove` shouldn't cause a panic
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment reads strangely, like it's referring to an earlier comment.

/// uses a query latch and then resuming that waiter.
/// There may be multiple cycles involved in a deadlock, so this searches
/// all active queries for cycles before finally resuming all the waiters at once.
pub fn break_query_cycles<'tcx>(
Copy link
Contributor

Choose a reason for hiding this comment

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

This function should have at least a short doc comment.

@nnethercote
Copy link
Contributor

A few minor comments above but I think this generally looks good. I don't understand every detail but it's a lot shorter and easier to follow that the existing code. I would still appreciate it if you modified the PR description to include some more details about what this PR does, and why.

@nnethercote nnethercote added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 3, 2026
@nnethercote
Copy link
Contributor

Also, I guess the PR title should now say "two functions"?

@zetanumbers zetanumbers changed the title Simplify break_query_cycles into a single function Simplify break_query_cycles Mar 3, 2026
@Zoxc
Copy link
Contributor

Zoxc commented Mar 3, 2026

This PR seems highly questionable to me. My initial concerns suggest @zetanumbers does not fully understand the problem, so I question the correctness of the rest of the code too.

We're trying to find cycles in the directed graph of active query jobs. The straightforward solution is a DFS search for cycles in that graph, which is what my code does. I have no clue what this PR is doing.

@zetanumbers
Copy link
Contributor Author

We're trying to find cycles in the directed graph of active query jobs. The straightforward solution is a DFS search for cycles in that graph, which is what my code does. I have no clue what this PR is doing.

I will write a detailed description later, but this is also a DFS search for cycles in that graph.

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

Labels

A-query-system Area: The rustc query system (https://rustc-dev-guide.rust-lang.org/query.html) S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants