1use std::fmt::Debug;
2use std::hash::Hash;
3use std::num::NonZero;
4use std::sync::Arc;
56use parking_lot::{Condvar, Mutex};
7use rustc_span::Span;
89use crate::query::Cycle;
10use crate::ty::TyCtxt;
1112/// A value uniquely identifying an active query job.
13#[derive(#[automatically_derived]
impl ::core::marker::Copy for QueryJobId { }Copy, #[automatically_derived]
impl ::core::clone::Clone for QueryJobId {
#[inline]
fn clone(&self) -> QueryJobId {
let _: ::core::clone::AssertParamIsClone<NonZero<u64>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::cmp::Eq for QueryJobId {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<NonZero<u64>>;
}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for QueryJobId {
#[inline]
fn eq(&self, other: &QueryJobId) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::hash::Hash for QueryJobId {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for QueryJobId {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "QueryJobId",
&&self.0)
}
}Debug)]
14pub struct QueryJobId(pub NonZero<u64>);
1516/// Represents an active query job.
17#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for QueryJob<'tcx> {
#[inline]
fn clone(&self) -> QueryJob<'tcx> {
QueryJob {
id: ::core::clone::Clone::clone(&self.id),
span: ::core::clone::Clone::clone(&self.span),
parent: ::core::clone::Clone::clone(&self.parent),
latch: ::core::clone::Clone::clone(&self.latch),
}
}
}Clone, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for QueryJob<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field4_finish(f, "QueryJob",
"id", &self.id, "span", &self.span, "parent", &self.parent,
"latch", &&self.latch)
}
}Debug)]
18pub struct QueryJob<'tcx> {
19pub id: QueryJobId,
2021/// The span corresponding to the reason for which this query was required.
22pub span: Span,
2324/// The parent query job which created this job and is implicitly waiting on it.
25pub parent: Option<QueryJobId>,
2627/// The latch that is used to wait on this job.
28pub latch: Option<QueryLatch<'tcx>>,
29}
3031impl<'tcx> QueryJob<'tcx> {
32/// Creates a new query job.
33#[inline]
34pub fn new(id: QueryJobId, span: Span, parent: Option<QueryJobId>) -> Self {
35QueryJob { id, span, parent, latch: None }
36 }
3738pub fn latch(&mut self) -> QueryLatch<'tcx> {
39self.latch.get_or_insert_with(QueryLatch::new).clone()
40 }
4142/// Signals to waiters that the query is complete.
43 ///
44 /// This does nothing for single threaded rustc,
45 /// as there are no concurrent jobs which could be waiting on us
46#[inline]
47pub fn signal_complete(self) {
48if let Some(latch) = self.latch {
49latch.set();
50 }
51 }
52}
5354#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for QueryWaiter<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field4_finish(f, "QueryWaiter",
"parent", &self.parent, "condvar", &self.condvar, "span",
&self.span, "cycle", &&self.cycle)
}
}Debug)]
55pub struct QueryWaiter<'tcx> {
56pub parent: Option<QueryJobId>,
57pub condvar: Condvar,
58pub span: Span,
59pub cycle: Mutex<Option<Cycle<'tcx>>>,
60}
6162#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for QueryLatch<'tcx> {
#[inline]
fn clone(&self) -> QueryLatch<'tcx> {
QueryLatch { waiters: ::core::clone::Clone::clone(&self.waiters) }
}
}Clone, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for QueryLatch<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f, "QueryLatch",
"waiters", &&self.waiters)
}
}Debug)]
63pub struct QueryLatch<'tcx> {
64/// The `Option` is `Some(..)` when the job is active, and `None` once completed.
65pub waiters: Arc<Mutex<Option<Vec<Arc<QueryWaiter<'tcx>>>>>>,
66}
6768impl<'tcx> QueryLatch<'tcx> {
69fn new() -> Self {
70QueryLatch { waiters: Arc::new(Mutex::new(Some(Vec::new()))) }
71 }
7273/// Awaits for the query job to complete.
74pub fn wait_on(
75&self,
76 tcx: TyCtxt<'tcx>,
77 query: Option<QueryJobId>,
78 span: Span,
79 ) -> Result<(), Cycle<'tcx>> {
80let mut waiters_guard = self.waiters.lock();
81let Some(waiters) = &mut *waiters_guardelse {
82return Ok(()); // already complete
83};
8485let waiter = Arc::new(QueryWaiter {
86 parent: query,
87span,
88 cycle: Mutex::new(None),
89 condvar: Condvar::new(),
90 });
9192// We push the waiter on to the `waiters` list. It can be accessed inside
93 // the `wait` call below, by 1) the `set` method or 2) by deadlock detection.
94 // Both of these will remove it from the `waiters` list before resuming
95 // this thread.
96waiters.push(Arc::clone(&waiter));
9798// Awaits the caller on this latch by blocking the current thread.
99 // If this detects a deadlock and the deadlock handler wants to resume this thread
100 // we have to be in the `wait` call. This is ensured by the deadlock handler
101 // getting the self.info lock.
102rustc_thread_pool::mark_blocked();
103tcx.jobserver_proxy.release_thread();
104waiter.condvar.wait(&mut waiters_guard);
105// Release the lock before we potentially block in `acquire_thread`
106drop(waiters_guard);
107tcx.jobserver_proxy.acquire_thread();
108109// FIXME: Get rid of this lock. We have ownership of the QueryWaiter
110 // although another thread may still have a Arc reference so we cannot
111 // use Arc::get_mut
112let mut cycle = waiter.cycle.lock();
113match cycle.take() {
114None => Ok(()),
115Some(cycle) => Err(cycle),
116 }
117 }
118119/// Sets the latch and resumes all waiters on it
120fn set(&self) {
121let mut waiters_guard = self.waiters.lock();
122let waiters = waiters_guard.take().unwrap(); // mark the latch as complete
123let registry = rustc_thread_pool::Registry::current();
124for waiter in waiters {
125 rustc_thread_pool::mark_unblocked(®istry);
126 waiter.condvar.notify_one();
127 }
128 }
129130/// Removes a single waiter from the list of waiters.
131 /// This is used to break query cycles.
132pub fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<'tcx>> {
133let mut waiters_guard = self.waiters.lock();
134let waiters = waiters_guard.as_mut().expect("non-empty waiters vec");
135// Remove the waiter from the list of waiters
136waiters.remove(waiter)
137 }
138}