From 9983a569ca7c2f445b88f3189ace677b22e8563e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 9 Mar 2026 16:38:57 +1100 Subject: [PATCH 1/2] Inline and remove `QueryLatch::wait_on_inner`. It has a single call site. --- compiler/rustc_middle/src/query/job.rs | 46 +++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_middle/src/query/job.rs b/compiler/rustc_middle/src/query/job.rs index f1a2b3a34d0e8..afa1d9a5ec20c 100644 --- a/compiler/rustc_middle/src/query/job.rs +++ b/compiler/rustc_middle/src/query/job.rs @@ -104,7 +104,29 @@ impl<'tcx> QueryLatch<'tcx> { ) -> Result<(), CycleError>> { let waiter = Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() }); - self.wait_on_inner(tcx, &waiter); + + // Awaits the caller on this latch by blocking the current thread. + { + let mut info = self.info.lock(); + if !info.complete { + // We push the waiter on to the `waiters` list. It can be accessed inside + // the `wait` call below, by 1) the `set` method or 2) by deadlock detection. + // Both of these will remove it from the `waiters` list before resuming + // this thread. + info.waiters.push(Arc::clone(&waiter)); + + // If this detects a deadlock and the deadlock handler wants to resume this thread + // we have to be in the `wait` call. This is ensured by the deadlock handler + // getting the self.info lock. + rustc_thread_pool::mark_blocked(); + tcx.jobserver_proxy.release_thread(); + waiter.condvar.wait(&mut info); + // Release the lock before we potentially block in `acquire_thread` + drop(info); + tcx.jobserver_proxy.acquire_thread(); + } + } + // FIXME: Get rid of this lock. We have ownership of the QueryWaiter // although another thread may still have a Arc reference so we cannot // use Arc::get_mut @@ -115,28 +137,6 @@ impl<'tcx> QueryLatch<'tcx> { } } - /// Awaits the caller on this latch by blocking the current thread. - fn wait_on_inner(&self, tcx: TyCtxt<'tcx>, waiter: &Arc>) { - let mut info = self.info.lock(); - if !info.complete { - // We push the waiter on to the `waiters` list. It can be accessed inside - // the `wait` call below, by 1) the `set` method or 2) by deadlock detection. - // Both of these will remove it from the `waiters` list before resuming - // this thread. - info.waiters.push(Arc::clone(waiter)); - - // If this detects a deadlock and the deadlock handler wants to resume this thread - // we have to be in the `wait` call. This is ensured by the deadlock handler - // getting the self.info lock. - rustc_thread_pool::mark_blocked(); - tcx.jobserver_proxy.release_thread(); - waiter.condvar.wait(&mut info); - // Release the lock before we potentially block in `acquire_thread` - drop(info); - tcx.jobserver_proxy.acquire_thread(); - } - } - /// Sets the latch and resumes all waiters on it fn set(&self) { let mut info = self.info.lock(); From f2cf26f3f08a87eb7fd999e5c582aeb0c3506902 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 9 Mar 2026 16:43:52 +1100 Subject: [PATCH 2/2] Streamline `QueryLatch::wait_on`. This commit moves the handling of the trivial "complete" case to the top of the method. This avoids uselessly constructing a waiter in that case. It also avoids the need for a separate block scope for `info`. --- compiler/rustc_middle/src/query/job.rs | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_middle/src/query/job.rs b/compiler/rustc_middle/src/query/job.rs index afa1d9a5ec20c..23ca15d3f8a12 100644 --- a/compiler/rustc_middle/src/query/job.rs +++ b/compiler/rustc_middle/src/query/job.rs @@ -102,30 +102,30 @@ impl<'tcx> QueryLatch<'tcx> { query: Option, span: Span, ) -> Result<(), CycleError>> { + let mut info = self.info.lock(); + if info.complete { + return Ok(()); + } + let waiter = Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() }); + // We push the waiter on to the `waiters` list. It can be accessed inside + // the `wait` call below, by 1) the `set` method or 2) by deadlock detection. + // Both of these will remove it from the `waiters` list before resuming + // this thread. + info.waiters.push(Arc::clone(&waiter)); + // Awaits the caller on this latch by blocking the current thread. - { - let mut info = self.info.lock(); - if !info.complete { - // We push the waiter on to the `waiters` list. It can be accessed inside - // the `wait` call below, by 1) the `set` method or 2) by deadlock detection. - // Both of these will remove it from the `waiters` list before resuming - // this thread. - info.waiters.push(Arc::clone(&waiter)); - - // If this detects a deadlock and the deadlock handler wants to resume this thread - // we have to be in the `wait` call. This is ensured by the deadlock handler - // getting the self.info lock. - rustc_thread_pool::mark_blocked(); - tcx.jobserver_proxy.release_thread(); - waiter.condvar.wait(&mut info); - // Release the lock before we potentially block in `acquire_thread` - drop(info); - tcx.jobserver_proxy.acquire_thread(); - } - } + // If this detects a deadlock and the deadlock handler wants to resume this thread + // we have to be in the `wait` call. This is ensured by the deadlock handler + // getting the self.info lock. + rustc_thread_pool::mark_blocked(); + tcx.jobserver_proxy.release_thread(); + waiter.condvar.wait(&mut info); + // Release the lock before we potentially block in `acquire_thread` + drop(info); + tcx.jobserver_proxy.acquire_thread(); // FIXME: Get rid of this lock. We have ownership of the QueryWaiter // although another thread may still have a Arc reference so we cannot