Skip to content

Commit 3a558f6

Browse files
committed
Eliminate QueryLatchInfo.
The boolean `complete` flag indicates whether the `waiters` vec is still in use, which means the `waiters` vec must be empty when `complete` is true. This is achieved by using `drain` on the waiters just after `complete` is set. We can do better by using the type system to make invalid combinations impossible. This commit removes `complete` and puts the waiters inside an `Option`. `Some` means the query job is still active, and `None` once it is complete. And `QueryLatchInfo` is eliminated. (The `Arc<Mutex<Option<Vec<Arc<QueryWaiter<'tcx>>>>>>` type is a mouthful, but when it's all in one place I find it easier to understand than when it's split across two types. And we can use `Option` methods like `as_ref`, `as_mut`, and `take`, which is nice.)
1 parent b49ecc9 commit 3a558f6

2 files changed

Lines changed: 17 additions & 25 deletions

File tree

  • compiler

compiler/rustc_middle/src/query/job.rs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -77,22 +77,15 @@ pub struct QueryWaiter<'tcx> {
7777
pub cycle: Mutex<Option<CycleError<QueryStackDeferred<'tcx>>>>,
7878
}
7979

80-
#[derive(Debug)]
81-
pub struct QueryLatchInfo<'tcx> {
82-
pub complete: bool,
83-
pub waiters: Vec<Arc<QueryWaiter<'tcx>>>,
84-
}
85-
8680
#[derive(Clone, Debug)]
8781
pub struct QueryLatch<'tcx> {
88-
pub info: Arc<Mutex<QueryLatchInfo<'tcx>>>,
82+
/// The `Option` is `Some(..)` when the job is active, and `None` once completed.
83+
pub waiters: Arc<Mutex<Option<Vec<Arc<QueryWaiter<'tcx>>>>>>,
8984
}
9085

9186
impl<'tcx> QueryLatch<'tcx> {
9287
fn new() -> Self {
93-
QueryLatch {
94-
info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
95-
}
88+
QueryLatch { waiters: Arc::new(Mutex::new(Some(Vec::new()))) }
9689
}
9790

9891
/// Awaits for the query job to complete.
@@ -102,10 +95,10 @@ impl<'tcx> QueryLatch<'tcx> {
10295
query: Option<QueryJobId>,
10396
span: Span,
10497
) -> Result<(), CycleError<QueryStackDeferred<'tcx>>> {
105-
let mut info = self.info.lock();
106-
if info.complete {
107-
return Ok(());
108-
}
98+
let mut waiters_guard = self.waiters.lock();
99+
let Some(waiters) = &mut *waiters_guard else {
100+
return Ok(()); // already complete
101+
};
109102

110103
let waiter =
111104
Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
@@ -114,17 +107,17 @@ impl<'tcx> QueryLatch<'tcx> {
114107
// the `wait` call below, by 1) the `set` method or 2) by deadlock detection.
115108
// Both of these will remove it from the `waiters` list before resuming
116109
// this thread.
117-
info.waiters.push(Arc::clone(&waiter));
110+
waiters.push(Arc::clone(&waiter));
118111

119112
// Awaits the caller on this latch by blocking the current thread.
120113
// If this detects a deadlock and the deadlock handler wants to resume this thread
121114
// we have to be in the `wait` call. This is ensured by the deadlock handler
122115
// getting the self.info lock.
123116
rustc_thread_pool::mark_blocked();
124117
tcx.jobserver_proxy.release_thread();
125-
waiter.condvar.wait(&mut info);
118+
waiter.condvar.wait(&mut waiters_guard);
126119
// Release the lock before we potentially block in `acquire_thread`
127-
drop(info);
120+
drop(waiters_guard);
128121
tcx.jobserver_proxy.acquire_thread();
129122

130123
// FIXME: Get rid of this lock. We have ownership of the QueryWaiter
@@ -139,11 +132,10 @@ impl<'tcx> QueryLatch<'tcx> {
139132

140133
/// Sets the latch and resumes all waiters on it
141134
fn set(&self) {
142-
let mut info = self.info.lock();
143-
debug_assert!(!info.complete);
144-
info.complete = true;
135+
let mut waiters_guard = self.waiters.lock();
136+
let waiters = waiters_guard.take().unwrap(); // mark the latch as complete
145137
let registry = rustc_thread_pool::Registry::current();
146-
for waiter in info.waiters.drain(..) {
138+
for waiter in waiters {
147139
rustc_thread_pool::mark_unblocked(&registry);
148140
waiter.condvar.notify_one();
149141
}
@@ -152,9 +144,9 @@ impl<'tcx> QueryLatch<'tcx> {
152144
/// Removes a single waiter from the list of waiters.
153145
/// This is used to break query cycles.
154146
pub fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<'tcx>> {
155-
let mut info = self.info.lock();
156-
debug_assert!(!info.complete);
147+
let mut waiters_guard = self.waiters.lock();
148+
let waiters = waiters_guard.as_mut().expect("non-empty waiters vec");
157149
// Remove the waiter from the list of waiters
158-
info.waiters.remove(waiter)
150+
waiters.remove(waiter)
159151
}
160152
}

compiler/rustc_query_impl/src/job.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ fn visit_waiters<'tcx>(
136136

137137
// Visit the explicit waiters which use condvars and are resumable
138138
if let Some(latch) = job_map.latch_of(query) {
139-
for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
139+
for (i, waiter) in latch.waiters.lock().as_ref().unwrap().iter().enumerate() {
140140
if let Some(waiter_query) = waiter.query {
141141
// Return a value which indicates that this waiter can be resumed
142142
visit(waiter.span, waiter_query).map_break(|_| Some((query, i)))?;

0 commit comments

Comments
 (0)