Skip to main content

rustc_middle/query/
job.rs

1use std::fmt::Debug;
2use std::hash::Hash;
3use std::num::NonZero;
4use std::sync::Arc;
5
6use parking_lot::{Condvar, Mutex};
7use rustc_span::Span;
8
9use crate::query::Cycle;
10use crate::ty::TyCtxt;
11
12/// 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>);
15
16/// 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> {
19    pub id: QueryJobId,
20
21    /// The span corresponding to the reason for which this query was required.
22    pub span: Span,
23
24    /// The parent query job which created this job and is implicitly waiting on it.
25    pub parent: Option<QueryJobId>,
26
27    /// The latch that is used to wait on this job.
28    pub latch: Option<QueryLatch<'tcx>>,
29}
30
31impl<'tcx> QueryJob<'tcx> {
32    /// Creates a new query job.
33    #[inline]
34    pub fn new(id: QueryJobId, span: Span, parent: Option<QueryJobId>) -> Self {
35        QueryJob { id, span, parent, latch: None }
36    }
37
38    pub fn latch(&mut self) -> QueryLatch<'tcx> {
39        if self.latch.is_none() {
40            self.latch = Some(QueryLatch::new());
41        }
42        self.latch.as_ref().unwrap().clone()
43    }
44
45    /// Signals to waiters that the query is complete.
46    ///
47    /// This does nothing for single threaded rustc,
48    /// as there are no concurrent jobs which could be waiting on us
49    #[inline]
50    pub fn signal_complete(self) {
51        if let Some(latch) = self.latch {
52            latch.set();
53        }
54    }
55}
56
57#[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)]
58pub struct QueryWaiter<'tcx> {
59    pub parent: Option<QueryJobId>,
60    pub condvar: Condvar,
61    pub span: Span,
62    pub cycle: Mutex<Option<Cycle<'tcx>>>,
63}
64
65#[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)]
66pub struct QueryLatch<'tcx> {
67    /// The `Option` is `Some(..)` when the job is active, and `None` once completed.
68    pub waiters: Arc<Mutex<Option<Vec<Arc<QueryWaiter<'tcx>>>>>>,
69}
70
71impl<'tcx> QueryLatch<'tcx> {
72    fn new() -> Self {
73        QueryLatch { waiters: Arc::new(Mutex::new(Some(Vec::new()))) }
74    }
75
76    /// Awaits for the query job to complete.
77    pub fn wait_on(
78        &self,
79        tcx: TyCtxt<'tcx>,
80        query: Option<QueryJobId>,
81        span: Span,
82    ) -> Result<(), Cycle<'tcx>> {
83        let mut waiters_guard = self.waiters.lock();
84        let Some(waiters) = &mut *waiters_guard else {
85            return Ok(()); // already complete
86        };
87
88        let waiter = Arc::new(QueryWaiter {
89            parent: query,
90            span,
91            cycle: Mutex::new(None),
92            condvar: Condvar::new(),
93        });
94
95        // We push the waiter on to the `waiters` list. It can be accessed inside
96        // the `wait` call below, by 1) the `set` method or 2) by deadlock detection.
97        // Both of these will remove it from the `waiters` list before resuming
98        // this thread.
99        waiters.push(Arc::clone(&waiter));
100
101        // Awaits the caller on this latch by blocking the current thread.
102        // If this detects a deadlock and the deadlock handler wants to resume this thread
103        // we have to be in the `wait` call. This is ensured by the deadlock handler
104        // getting the self.info lock.
105        rustc_thread_pool::mark_blocked();
106        tcx.jobserver_proxy.release_thread();
107        waiter.condvar.wait(&mut waiters_guard);
108        // Release the lock before we potentially block in `acquire_thread`
109        drop(waiters_guard);
110        tcx.jobserver_proxy.acquire_thread();
111
112        // FIXME: Get rid of this lock. We have ownership of the QueryWaiter
113        // although another thread may still have a Arc reference so we cannot
114        // use Arc::get_mut
115        let mut cycle = waiter.cycle.lock();
116        match cycle.take() {
117            None => Ok(()),
118            Some(cycle) => Err(cycle),
119        }
120    }
121
122    /// Sets the latch and resumes all waiters on it
123    fn set(&self) {
124        let mut waiters_guard = self.waiters.lock();
125        let waiters = waiters_guard.take().unwrap(); // mark the latch as complete
126        let registry = rustc_thread_pool::Registry::current();
127        for waiter in waiters {
128            rustc_thread_pool::mark_unblocked(&registry);
129            waiter.condvar.notify_one();
130        }
131    }
132
133    /// Removes a single waiter from the list of waiters.
134    /// This is used to break query cycles.
135    pub fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<'tcx>> {
136        let mut waiters_guard = self.waiters.lock();
137        let waiters = waiters_guard.as_mut().expect("non-empty waiters vec");
138        // Remove the waiter from the list of waiters
139        waiters.remove(waiter)
140    }
141}