Skip to content

Commit 18ade84

Browse files
committed
Streamline ActiveJobGuard completion.
`ActiveJobGuard::complete` and `ActiveJobGuard::drop` have very similar code, though non-essential structural differences obscure this a little. This commit factors out the common code into a new method `ActiveJobGuard::complete_inner`. It also inlines and remove `expect_job`, which ends up having a single call site.
1 parent e41bf75 commit 18ade84

1 file changed

Lines changed: 33 additions & 46 deletions

File tree

compiler/rustc_query_impl/src/execution.rs

Lines changed: 33 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::hash::Hash;
2-
use std::mem;
2+
use std::mem::ManuallyDrop;
33

44
use rustc_data_structures::hash_table::{Entry, HashTable};
55
use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -26,17 +26,6 @@ fn equivalent_key<K: Eq, V>(k: K) -> impl Fn(&(K, V)) -> bool {
2626
move |x| x.0 == k
2727
}
2828

29-
/// Obtains the enclosed [`QueryJob`], or panics if this query evaluation
30-
/// was poisoned by a panic.
31-
fn expect_job<'tcx>(status: ActiveKeyStatus<'tcx>) -> QueryJob<'tcx> {
32-
match status {
33-
ActiveKeyStatus::Started(job) => job,
34-
ActiveKeyStatus::Poisoned => {
35-
panic!("job for query failed to start and was poisoned")
36-
}
37-
}
38-
}
39-
4029
pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool {
4130
state.active.lock_shards().all(|shard| shard.is_empty())
4231
}
@@ -154,33 +143,45 @@ where
154143
{
155144
/// Completes the query by updating the query cache with the `result`,
156145
/// signals the waiter, and forgets the guard so it won't poison the query.
157-
fn complete<C>(self, cache: &C, result: C::Value, dep_node_index: DepNodeIndex)
146+
fn complete<C>(self, cache: &C, value: C::Value, dep_node_index: DepNodeIndex)
158147
where
159148
C: QueryCache<Key = K>,
160149
{
161-
// Forget ourself so our destructor won't poison the query.
162-
// (Extract fields by value first to make sure we don't leak anything.)
163-
let Self { state, key, key_hash }: Self = self;
164-
mem::forget(self);
165-
166150
// Mark as complete before we remove the job from the active state
167151
// so no other thread can re-execute this query.
168-
cache.complete(key, result, dep_node_index);
169-
170-
let job = {
171-
// don't keep the lock during the `unwrap()` of the retrieved value, or we taint the
172-
// underlying shard.
173-
// since unwinding also wants to look at this map, this can also prevent a double
174-
// panic.
175-
let mut shard = state.active.lock_shard_by_hash(key_hash);
176-
match shard.find_entry(key_hash, equivalent_key(key)) {
177-
Err(_) => None,
178-
Ok(occupied) => Some(occupied.remove().0.1),
152+
cache.complete(self.key, value, dep_node_index);
153+
154+
let mut this = ManuallyDrop::new(self);
155+
156+
// Drop everything without poisoning the query.
157+
this.drop_and_maybe_poison(/* poison */ false);
158+
}
159+
160+
fn drop_and_maybe_poison(&mut self, poison: bool) {
161+
let status = {
162+
let mut shard = self.state.active.lock_shard_by_hash(self.key_hash);
163+
match shard.find_entry(self.key_hash, equivalent_key(self.key)) {
164+
Err(_) => {
165+
// Note: we must not panic while holding the lock, because unwinding also looks
166+
// at this map, which can result in a double panic. So drop it first.
167+
drop(shard);
168+
panic!();
169+
}
170+
Ok(occupied) => {
171+
let ((key, status), vacant) = occupied.remove();
172+
if poison {
173+
vacant.insert((key, ActiveKeyStatus::Poisoned));
174+
}
175+
status
176+
}
179177
}
180178
};
181-
let job = expect_job(job.expect("active query job entry"));
182179

183-
job.signal_complete();
180+
// Also signal the completion of the job, so waiters will continue execution.
181+
match status {
182+
ActiveKeyStatus::Started(job) => job.signal_complete(),
183+
ActiveKeyStatus::Poisoned => panic!(),
184+
}
184185
}
185186
}
186187

@@ -192,21 +193,7 @@ where
192193
#[cold]
193194
fn drop(&mut self) {
194195
// Poison the query so jobs waiting on it panic.
195-
let Self { state, key, key_hash } = *self;
196-
let job = {
197-
let mut shard = state.active.lock_shard_by_hash(key_hash);
198-
match shard.find_entry(key_hash, equivalent_key(key)) {
199-
Err(_) => panic!(),
200-
Ok(occupied) => {
201-
let ((key, value), vacant) = occupied.remove();
202-
vacant.insert((key, ActiveKeyStatus::Poisoned));
203-
expect_job(value)
204-
}
205-
}
206-
};
207-
// Also signal the completion of the job, so waiters
208-
// will continue execution.
209-
job.signal_complete();
196+
self.drop_and_maybe_poison(/* poison */ true);
210197
}
211198
}
212199

0 commit comments

Comments
 (0)