Rollup merge of #138672 - Zoxc:deferred-queries-in-deadlock-handler, r=oli-obk
Avoiding calling queries when collecting active queries This PR changes active query collection to no longer call queries. Instead the fields needing queries have their computation delayed to when an cycle error is emitted or when printing the query backtrace in a panic. This is done by splitting the fields in `QueryStackFrame` needing queries into a new `QueryStackFrameExtra` type. When collecting queries `QueryStackFrame` will contain a closure that can create `QueryStackFrameExtra`, which does make use of queries. Calling `lift` on a `QueryStackFrame` or `CycleError` will convert it to a variant containing `QueryStackFrameExtra` using those closures. This also only calls queries needed to collect information on a cycle errors, instead of information on all active queries. Calling queries when collecting active queries is a bit odd. Calling queries should not be done in the deadlock handler at all. This avoids the out of memory scenario in https://github.com/rust-lang/rust/issues/124901.
This commit is contained in:
commit
7853b88423
10 changed files with 311 additions and 148 deletions
|
@ -6,6 +6,7 @@ use std::hash::Hash;
|
|||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
|
||||
use super::QueryStackFrameExtra;
|
||||
use crate::dep_graph::{DepKind, DepNode, DepNodeParams, SerializedDepNodeIndex};
|
||||
use crate::error::HandleCycleError;
|
||||
use crate::ich::StableHashingContext;
|
||||
|
@ -27,7 +28,7 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
|
|||
fn format_value(self) -> fn(&Self::Value) -> String;
|
||||
|
||||
// Don't use this method to access query results, instead use the methods on TyCtxt
|
||||
fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState<Self::Key>
|
||||
fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState<Self::Key, Qcx::QueryInfo>
|
||||
where
|
||||
Qcx: 'a;
|
||||
|
||||
|
@ -57,7 +58,7 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
|
|||
fn value_from_cycle_error(
|
||||
self,
|
||||
tcx: Qcx::DepContext,
|
||||
cycle_error: &CycleError,
|
||||
cycle_error: &CycleError<QueryStackFrameExtra>,
|
||||
guar: ErrorGuaranteed,
|
||||
) -> Self::Value;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::io::Write;
|
||||
use std::iter;
|
||||
|
@ -12,6 +13,7 @@ use rustc_hir::def::DefKind;
|
|||
use rustc_session::Session;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
|
||||
use super::QueryStackFrameExtra;
|
||||
use crate::dep_graph::DepContext;
|
||||
use crate::error::CycleStack;
|
||||
use crate::query::plumbing::CycleError;
|
||||
|
@ -19,45 +21,54 @@ use crate::query::{QueryContext, QueryStackFrame};
|
|||
|
||||
/// Represents a span and a query key.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QueryInfo {
|
||||
pub struct QueryInfo<I> {
|
||||
/// The span corresponding to the reason for which this query was required.
|
||||
pub span: Span,
|
||||
pub query: QueryStackFrame,
|
||||
pub query: QueryStackFrame<I>,
|
||||
}
|
||||
|
||||
pub type QueryMap = FxHashMap<QueryJobId, QueryJobInfo>;
|
||||
impl<I> QueryInfo<I> {
|
||||
pub(crate) fn lift<Qcx: QueryContext<QueryInfo = I>>(
|
||||
&self,
|
||||
qcx: Qcx,
|
||||
) -> QueryInfo<QueryStackFrameExtra> {
|
||||
QueryInfo { span: self.span, query: self.query.lift(qcx) }
|
||||
}
|
||||
}
|
||||
|
||||
pub type QueryMap<I> = FxHashMap<QueryJobId, QueryJobInfo<I>>;
|
||||
|
||||
/// A value uniquely identifying an active query job.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct QueryJobId(pub NonZero<u64>);
|
||||
|
||||
impl QueryJobId {
|
||||
fn query(self, map: &QueryMap) -> QueryStackFrame {
|
||||
fn query<I: Clone>(self, map: &QueryMap<I>) -> QueryStackFrame<I> {
|
||||
map.get(&self).unwrap().query.clone()
|
||||
}
|
||||
|
||||
fn span(self, map: &QueryMap) -> Span {
|
||||
fn span<I>(self, map: &QueryMap<I>) -> Span {
|
||||
map.get(&self).unwrap().job.span
|
||||
}
|
||||
|
||||
fn parent(self, map: &QueryMap) -> Option<QueryJobId> {
|
||||
fn parent<I>(self, map: &QueryMap<I>) -> Option<QueryJobId> {
|
||||
map.get(&self).unwrap().job.parent
|
||||
}
|
||||
|
||||
fn latch(self, map: &QueryMap) -> Option<&QueryLatch> {
|
||||
fn latch<I>(self, map: &QueryMap<I>) -> Option<&QueryLatch<I>> {
|
||||
map.get(&self).unwrap().job.latch.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QueryJobInfo {
|
||||
pub query: QueryStackFrame,
|
||||
pub job: QueryJob,
|
||||
pub struct QueryJobInfo<I> {
|
||||
pub query: QueryStackFrame<I>,
|
||||
pub job: QueryJob<I>,
|
||||
}
|
||||
|
||||
/// Represents an active query job.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QueryJob {
|
||||
#[derive(Debug)]
|
||||
pub struct QueryJob<I> {
|
||||
pub id: QueryJobId,
|
||||
|
||||
/// The span corresponding to the reason for which this query was required.
|
||||
|
@ -67,17 +78,23 @@ pub struct QueryJob {
|
|||
pub parent: Option<QueryJobId>,
|
||||
|
||||
/// The latch that is used to wait on this job.
|
||||
latch: Option<QueryLatch>,
|
||||
latch: Option<QueryLatch<I>>,
|
||||
}
|
||||
|
||||
impl QueryJob {
|
||||
impl<I> Clone for QueryJob<I> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { id: self.id, span: self.span, parent: self.parent, latch: self.latch.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> QueryJob<I> {
|
||||
/// Creates a new query job.
|
||||
#[inline]
|
||||
pub fn new(id: QueryJobId, span: Span, parent: Option<QueryJobId>) -> Self {
|
||||
QueryJob { id, span, parent, latch: None }
|
||||
}
|
||||
|
||||
pub(super) fn latch(&mut self) -> QueryLatch {
|
||||
pub(super) fn latch(&mut self) -> QueryLatch<I> {
|
||||
if self.latch.is_none() {
|
||||
self.latch = Some(QueryLatch::new());
|
||||
}
|
||||
|
@ -97,12 +114,12 @@ impl QueryJob {
|
|||
}
|
||||
|
||||
impl QueryJobId {
|
||||
pub(super) fn find_cycle_in_stack(
|
||||
pub(super) fn find_cycle_in_stack<I: Clone>(
|
||||
&self,
|
||||
query_map: QueryMap,
|
||||
query_map: QueryMap<I>,
|
||||
current_job: &Option<QueryJobId>,
|
||||
span: Span,
|
||||
) -> CycleError {
|
||||
) -> CycleError<I> {
|
||||
// Find the waitee amongst `current_job` parents
|
||||
let mut cycle = Vec::new();
|
||||
let mut current_job = Option::clone(current_job);
|
||||
|
@ -136,7 +153,7 @@ impl QueryJobId {
|
|||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn find_dep_kind_root(&self, query_map: QueryMap) -> (QueryJobInfo, usize) {
|
||||
pub fn find_dep_kind_root<I: Clone>(&self, query_map: QueryMap<I>) -> (QueryJobInfo<I>, usize) {
|
||||
let mut depth = 1;
|
||||
let info = query_map.get(&self).unwrap();
|
||||
let dep_kind = info.query.dep_kind;
|
||||
|
@ -156,25 +173,31 @@ impl QueryJobId {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct QueryWaiter {
|
||||
struct QueryWaiter<I> {
|
||||
query: Option<QueryJobId>,
|
||||
condvar: Condvar,
|
||||
span: Span,
|
||||
cycle: Mutex<Option<CycleError>>,
|
||||
cycle: Mutex<Option<CycleError<I>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct QueryLatchInfo {
|
||||
struct QueryLatchInfo<I> {
|
||||
complete: bool,
|
||||
waiters: Vec<Arc<QueryWaiter>>,
|
||||
waiters: Vec<Arc<QueryWaiter<I>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct QueryLatch {
|
||||
info: Arc<Mutex<QueryLatchInfo>>,
|
||||
#[derive(Debug)]
|
||||
pub(super) struct QueryLatch<I> {
|
||||
info: Arc<Mutex<QueryLatchInfo<I>>>,
|
||||
}
|
||||
|
||||
impl QueryLatch {
|
||||
impl<I> Clone for QueryLatch<I> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { info: Arc::clone(&self.info) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> QueryLatch<I> {
|
||||
fn new() -> Self {
|
||||
QueryLatch {
|
||||
info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
|
||||
|
@ -182,7 +205,11 @@ impl QueryLatch {
|
|||
}
|
||||
|
||||
/// Awaits for the query job to complete.
|
||||
pub(super) fn wait_on(&self, query: Option<QueryJobId>, span: Span) -> Result<(), CycleError> {
|
||||
pub(super) fn wait_on(
|
||||
&self,
|
||||
query: Option<QueryJobId>,
|
||||
span: Span,
|
||||
) -> Result<(), CycleError<I>> {
|
||||
let waiter =
|
||||
Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
|
||||
self.wait_on_inner(&waiter);
|
||||
|
@ -197,7 +224,7 @@ impl QueryLatch {
|
|||
}
|
||||
|
||||
/// Awaits the caller on this latch by blocking the current thread.
|
||||
fn wait_on_inner(&self, waiter: &Arc<QueryWaiter>) {
|
||||
fn wait_on_inner(&self, waiter: &Arc<QueryWaiter<I>>) {
|
||||
let mut info = self.info.lock();
|
||||
if !info.complete {
|
||||
// We push the waiter on to the `waiters` list. It can be accessed inside
|
||||
|
@ -232,7 +259,7 @@ impl QueryLatch {
|
|||
|
||||
/// Removes a single waiter from the list of waiters.
|
||||
/// This is used to break query cycles.
|
||||
fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter> {
|
||||
fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<I>> {
|
||||
let mut info = self.info.lock();
|
||||
debug_assert!(!info.complete);
|
||||
// Remove the waiter from the list of waiters
|
||||
|
@ -252,7 +279,11 @@ type Waiter = (QueryJobId, usize);
|
|||
/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the
|
||||
/// required information to resume the waiter.
|
||||
/// If all `visit` calls returns None, this function also returns None.
|
||||
fn visit_waiters<F>(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option<Option<Waiter>>
|
||||
fn visit_waiters<I, F>(
|
||||
query_map: &QueryMap<I>,
|
||||
query: QueryJobId,
|
||||
mut visit: F,
|
||||
) -> Option<Option<Waiter>>
|
||||
where
|
||||
F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
|
||||
{
|
||||
|
@ -282,8 +313,8 @@ where
|
|||
/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP.
|
||||
/// If a cycle is detected, this initial value is replaced with the span causing
|
||||
/// the cycle.
|
||||
fn cycle_check(
|
||||
query_map: &QueryMap,
|
||||
fn cycle_check<I>(
|
||||
query_map: &QueryMap<I>,
|
||||
query: QueryJobId,
|
||||
span: Span,
|
||||
stack: &mut Vec<(Span, QueryJobId)>,
|
||||
|
@ -322,8 +353,8 @@ fn cycle_check(
|
|||
/// Finds out if there's a path to the compiler root (aka. code which isn't in a query)
|
||||
/// from `query` without going through any of the queries in `visited`.
|
||||
/// This is achieved with a depth first search.
|
||||
fn connected_to_root(
|
||||
query_map: &QueryMap,
|
||||
fn connected_to_root<I>(
|
||||
query_map: &QueryMap<I>,
|
||||
query: QueryJobId,
|
||||
visited: &mut FxHashSet<QueryJobId>,
|
||||
) -> bool {
|
||||
|
@ -344,7 +375,7 @@ fn connected_to_root(
|
|||
}
|
||||
|
||||
// Deterministically pick an query from a list
|
||||
fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T
|
||||
fn pick_query<'a, I: Clone, T, F>(query_map: &QueryMap<I>, queries: &'a [T], f: F) -> &'a T
|
||||
where
|
||||
F: Fn(&T) -> (Span, QueryJobId),
|
||||
{
|
||||
|
@ -369,10 +400,10 @@ where
|
|||
/// the function return true.
|
||||
/// If a cycle was not found, the starting query is removed from `jobs` and
|
||||
/// the function returns false.
|
||||
fn remove_cycle(
|
||||
query_map: &QueryMap,
|
||||
fn remove_cycle<I: Clone>(
|
||||
query_map: &QueryMap<I>,
|
||||
jobs: &mut Vec<QueryJobId>,
|
||||
wakelist: &mut Vec<Arc<QueryWaiter>>,
|
||||
wakelist: &mut Vec<Arc<QueryWaiter<I>>>,
|
||||
) -> bool {
|
||||
let mut visited = FxHashSet::default();
|
||||
let mut stack = Vec::new();
|
||||
|
@ -473,7 +504,10 @@ fn remove_cycle(
|
|||
/// uses a query latch and then resuming that waiter.
|
||||
/// There may be multiple cycles involved in a deadlock, so this searches
|
||||
/// all active queries for cycles before finally resuming all the waiters at once.
|
||||
pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) {
|
||||
pub fn break_query_cycles<I: Clone + Debug>(
|
||||
query_map: QueryMap<I>,
|
||||
registry: &rayon_core::Registry,
|
||||
) {
|
||||
let mut wakelist = Vec::new();
|
||||
let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();
|
||||
|
||||
|
@ -520,7 +554,7 @@ pub fn report_cycle<'a>(
|
|||
) -> Diag<'a> {
|
||||
assert!(!stack.is_empty());
|
||||
|
||||
let span = stack[0].query.default_span(stack[1 % stack.len()].span);
|
||||
let span = stack[0].query.info.default_span(stack[1 % stack.len()].span);
|
||||
|
||||
let mut cycle_stack = Vec::new();
|
||||
|
||||
|
@ -529,31 +563,31 @@ pub fn report_cycle<'a>(
|
|||
|
||||
for i in 1..stack.len() {
|
||||
let query = &stack[i].query;
|
||||
let span = query.default_span(stack[(i + 1) % stack.len()].span);
|
||||
cycle_stack.push(CycleStack { span, desc: query.description.to_owned() });
|
||||
let span = query.info.default_span(stack[(i + 1) % stack.len()].span);
|
||||
cycle_stack.push(CycleStack { span, desc: query.info.description.to_owned() });
|
||||
}
|
||||
|
||||
let mut cycle_usage = None;
|
||||
if let Some((span, ref query)) = *usage {
|
||||
cycle_usage = Some(crate::error::CycleUsage {
|
||||
span: query.default_span(span),
|
||||
usage: query.description.to_string(),
|
||||
span: query.info.default_span(span),
|
||||
usage: query.info.description.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let alias = if stack.iter().all(|entry| matches!(entry.query.def_kind, Some(DefKind::TyAlias)))
|
||||
{
|
||||
Some(crate::error::Alias::Ty)
|
||||
} else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) {
|
||||
Some(crate::error::Alias::Trait)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let alias =
|
||||
if stack.iter().all(|entry| matches!(entry.query.info.def_kind, Some(DefKind::TyAlias))) {
|
||||
Some(crate::error::Alias::Ty)
|
||||
} else if stack.iter().all(|entry| entry.query.info.def_kind == Some(DefKind::TraitAlias)) {
|
||||
Some(crate::error::Alias::Trait)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cycle_diag = crate::error::Cycle {
|
||||
span,
|
||||
cycle_stack,
|
||||
stack_bottom: stack[0].query.description.to_owned(),
|
||||
stack_bottom: stack[0].query.info.description.to_owned(),
|
||||
alias,
|
||||
cycle_usage,
|
||||
stack_count,
|
||||
|
@ -589,6 +623,7 @@ pub fn print_query_stack<Qcx: QueryContext>(
|
|||
let Some(query_info) = query_map.get(&query) else {
|
||||
break;
|
||||
};
|
||||
let query_extra = qcx.lift_query_info(&query_info.query.info);
|
||||
if Some(count_printed) < limit_frames || limit_frames.is_none() {
|
||||
// Only print to stderr as many stack frames as `num_frames` when present.
|
||||
// FIXME: needs translation
|
||||
|
@ -596,7 +631,7 @@ pub fn print_query_stack<Qcx: QueryContext>(
|
|||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
dcx.struct_failure_note(format!(
|
||||
"#{} [{:?}] {}",
|
||||
count_printed, query_info.query.dep_kind, query_info.query.description
|
||||
count_printed, query_info.query.dep_kind, query_extra.description
|
||||
))
|
||||
.with_span(query_info.job.span)
|
||||
.emit();
|
||||
|
@ -609,7 +644,7 @@ pub fn print_query_stack<Qcx: QueryContext>(
|
|||
"#{} [{}] {}",
|
||||
count_total,
|
||||
qcx.dep_context().dep_kind_info(query_info.query.dep_kind).name,
|
||||
query_info.query.description
|
||||
query_extra.description
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
mod plumbing;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::transmute;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use self::plumbing::*;
|
||||
|
||||
mod job;
|
||||
|
@ -11,6 +16,7 @@ mod caches;
|
|||
pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache};
|
||||
|
||||
mod config;
|
||||
use rustc_data_structures::sync::{DynSend, DynSync};
|
||||
use rustc_errors::DiagInner;
|
||||
use rustc_hashes::Hash64;
|
||||
use rustc_hir::def::DefKind;
|
||||
|
@ -25,31 +31,59 @@ use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIn
|
|||
///
|
||||
/// This is mostly used in case of cycles for error reporting.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QueryStackFrame {
|
||||
pub description: String,
|
||||
span: Option<Span>,
|
||||
pub def_id: Option<DefId>,
|
||||
pub def_kind: Option<DefKind>,
|
||||
/// A def-id that is extracted from a `Ty` in a query key
|
||||
pub def_id_for_ty_in_cycle: Option<DefId>,
|
||||
pub struct QueryStackFrame<I> {
|
||||
/// This field initially stores a `QueryStackDeferred` during collection,
|
||||
/// but can later be changed to `QueryStackFrameExtra` containing concrete information
|
||||
/// by calling `lift`. This is done so that collecting query does not need to invoke
|
||||
/// queries, instead `lift` will call queries in a more appropriate location.
|
||||
pub info: I,
|
||||
|
||||
pub dep_kind: DepKind,
|
||||
/// This hash is used to deterministically pick
|
||||
/// a query to remove cycles in the parallel compiler.
|
||||
hash: Hash64,
|
||||
pub def_id: Option<DefId>,
|
||||
/// A def-id that is extracted from a `Ty` in a query key
|
||||
pub def_id_for_ty_in_cycle: Option<DefId>,
|
||||
}
|
||||
|
||||
impl QueryStackFrame {
|
||||
impl<I> QueryStackFrame<I> {
|
||||
#[inline]
|
||||
pub fn new(
|
||||
description: String,
|
||||
span: Option<Span>,
|
||||
def_id: Option<DefId>,
|
||||
def_kind: Option<DefKind>,
|
||||
info: I,
|
||||
dep_kind: DepKind,
|
||||
def_id_for_ty_in_cycle: Option<DefId>,
|
||||
hash: impl FnOnce() -> Hash64,
|
||||
def_id: Option<DefId>,
|
||||
def_id_for_ty_in_cycle: Option<DefId>,
|
||||
) -> Self {
|
||||
Self { description, span, def_id, def_kind, def_id_for_ty_in_cycle, dep_kind, hash: hash() }
|
||||
Self { info, def_id, dep_kind, hash: hash(), def_id_for_ty_in_cycle }
|
||||
}
|
||||
|
||||
fn lift<Qcx: QueryContext<QueryInfo = I>>(
|
||||
&self,
|
||||
qcx: Qcx,
|
||||
) -> QueryStackFrame<QueryStackFrameExtra> {
|
||||
QueryStackFrame {
|
||||
info: qcx.lift_query_info(&self.info),
|
||||
dep_kind: self.dep_kind,
|
||||
hash: self.hash,
|
||||
def_id: self.def_id,
|
||||
def_id_for_ty_in_cycle: self.def_id_for_ty_in_cycle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QueryStackFrameExtra {
|
||||
pub description: String,
|
||||
span: Option<Span>,
|
||||
pub def_kind: Option<DefKind>,
|
||||
}
|
||||
|
||||
impl QueryStackFrameExtra {
|
||||
#[inline]
|
||||
pub fn new(description: String, span: Option<Span>, def_kind: Option<DefKind>) -> Self {
|
||||
Self { description, span, def_kind }
|
||||
}
|
||||
|
||||
// FIXME(eddyb) Get more valid `Span`s on queries.
|
||||
|
@ -62,7 +96,41 @@ impl QueryStackFrame {
|
|||
}
|
||||
}
|
||||
|
||||
/// Track a 'side effects' for a particular query.
|
||||
/// Track a 'side effect' for a particular query.
|
||||
/// This is used to hold a closure which can create `QueryStackFrameExtra`.
|
||||
#[derive(Clone)]
|
||||
pub struct QueryStackDeferred<'tcx> {
|
||||
_dummy: PhantomData<&'tcx ()>,
|
||||
|
||||
// `extract` may contain references to 'tcx, but we can't tell drop checking that it won't
|
||||
// access it in the destructor.
|
||||
extract: Arc<dyn Fn() -> QueryStackFrameExtra + DynSync + DynSend>,
|
||||
}
|
||||
|
||||
impl<'tcx> QueryStackDeferred<'tcx> {
|
||||
pub fn new<C: Copy + DynSync + DynSend + 'tcx>(
|
||||
context: C,
|
||||
extract: fn(C) -> QueryStackFrameExtra,
|
||||
) -> Self {
|
||||
let extract: Arc<dyn Fn() -> QueryStackFrameExtra + DynSync + DynSend + 'tcx> =
|
||||
Arc::new(move || extract(context));
|
||||
// SAFETY: The `extract` closure does not access 'tcx in its destructor as the only
|
||||
// captured variable is `context` which is Copy and cannot have a destructor.
|
||||
Self { _dummy: PhantomData, extract: unsafe { transmute(extract) } }
|
||||
}
|
||||
|
||||
pub fn extract(&self) -> QueryStackFrameExtra {
|
||||
(self.extract)()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Debug for QueryStackDeferred<'tcx> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("QueryStackDeferred")
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks 'side effects' for a particular query.
|
||||
/// This struct is saved to disk along with the query result,
|
||||
/// and loaded from disk if we mark the query as green.
|
||||
/// This allows us to 'replay' changes to global state
|
||||
|
@ -81,12 +149,16 @@ pub enum QuerySideEffect {
|
|||
}
|
||||
|
||||
pub trait QueryContext: HasDepContext {
|
||||
type QueryInfo: Clone;
|
||||
|
||||
fn next_job_id(self) -> QueryJobId;
|
||||
|
||||
/// Get the query information from the TLS context.
|
||||
fn current_query_job(self) -> Option<QueryJobId>;
|
||||
|
||||
fn collect_active_jobs(self) -> Result<QueryMap, QueryMap>;
|
||||
fn collect_active_jobs(self) -> Result<QueryMap<Self::QueryInfo>, QueryMap<Self::QueryInfo>>;
|
||||
|
||||
fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra;
|
||||
|
||||
/// Load a side effect associated to the node in the previous session.
|
||||
fn load_side_effect(
|
||||
|
|
|
@ -16,7 +16,7 @@ use rustc_errors::{Diag, FatalError, StashKey};
|
|||
use rustc_span::{DUMMY_SP, Span};
|
||||
use tracing::instrument;
|
||||
|
||||
use super::QueryConfig;
|
||||
use super::{QueryConfig, QueryStackFrameExtra};
|
||||
use crate::HandleCycleError;
|
||||
use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams};
|
||||
use crate::ich::StableHashingContext;
|
||||
|
@ -29,23 +29,23 @@ fn equivalent_key<K: Eq, V>(k: &K) -> impl Fn(&(K, V)) -> bool + '_ {
|
|||
move |x| x.0 == *k
|
||||
}
|
||||
|
||||
pub struct QueryState<K> {
|
||||
active: Sharded<hashbrown::HashTable<(K, QueryResult)>>,
|
||||
pub struct QueryState<K, I> {
|
||||
active: Sharded<hashbrown::HashTable<(K, QueryResult<I>)>>,
|
||||
}
|
||||
|
||||
/// Indicates the state of a query for a given key in a query map.
|
||||
enum QueryResult {
|
||||
enum QueryResult<I> {
|
||||
/// An already executing query. The query job can be used to await for its completion.
|
||||
Started(QueryJob),
|
||||
Started(QueryJob<I>),
|
||||
|
||||
/// The query panicked. Queries trying to wait on this will raise a fatal error which will
|
||||
/// silently panic.
|
||||
Poisoned,
|
||||
}
|
||||
|
||||
impl QueryResult {
|
||||
impl<I> QueryResult<I> {
|
||||
/// Unwraps the query job expecting that it has started.
|
||||
fn expect_job(self) -> QueryJob {
|
||||
fn expect_job(self) -> QueryJob<I> {
|
||||
match self {
|
||||
Self::Started(job) => job,
|
||||
Self::Poisoned => {
|
||||
|
@ -55,7 +55,7 @@ impl QueryResult {
|
|||
}
|
||||
}
|
||||
|
||||
impl<K> QueryState<K>
|
||||
impl<K, I> QueryState<K, I>
|
||||
where
|
||||
K: Eq + Hash + Copy + Debug,
|
||||
{
|
||||
|
@ -66,8 +66,8 @@ where
|
|||
pub fn try_collect_active_jobs<Qcx: Copy>(
|
||||
&self,
|
||||
qcx: Qcx,
|
||||
make_query: fn(Qcx, K) -> QueryStackFrame,
|
||||
jobs: &mut QueryMap,
|
||||
make_query: fn(Qcx, K) -> QueryStackFrame<I>,
|
||||
jobs: &mut QueryMap<I>,
|
||||
) -> Option<()> {
|
||||
let mut active = Vec::new();
|
||||
|
||||
|
@ -76,7 +76,7 @@ where
|
|||
for shard in self.active.try_lock_shards() {
|
||||
for (k, v) in shard?.iter() {
|
||||
if let QueryResult::Started(ref job) = *v {
|
||||
active.push((*k, job.clone()));
|
||||
active.push((*k, (*job).clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,19 +92,19 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<K> Default for QueryState<K> {
|
||||
fn default() -> QueryState<K> {
|
||||
impl<K, I> Default for QueryState<K, I> {
|
||||
fn default() -> QueryState<K, I> {
|
||||
QueryState { active: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A type representing the responsibility to execute the job in the `job` field.
|
||||
/// This will poison the relevant query if dropped.
|
||||
struct JobOwner<'tcx, K>
|
||||
struct JobOwner<'tcx, K, I>
|
||||
where
|
||||
K: Eq + Hash + Copy,
|
||||
{
|
||||
state: &'tcx QueryState<K>,
|
||||
state: &'tcx QueryState<K, I>,
|
||||
key: K,
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ where
|
|||
}
|
||||
Stash => {
|
||||
let guar = if let Some(root) = cycle_error.cycle.first()
|
||||
&& let Some(span) = root.query.span
|
||||
&& let Some(span) = root.query.info.span
|
||||
{
|
||||
error.stash(span, StashKey::Cycle).unwrap()
|
||||
} else {
|
||||
|
@ -157,7 +157,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, K> JobOwner<'tcx, K>
|
||||
impl<'tcx, K, I> JobOwner<'tcx, K, I>
|
||||
where
|
||||
K: Eq + Hash + Copy,
|
||||
{
|
||||
|
@ -194,7 +194,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, K> Drop for JobOwner<'tcx, K>
|
||||
impl<'tcx, K, I> Drop for JobOwner<'tcx, K, I>
|
||||
where
|
||||
K: Eq + Hash + Copy,
|
||||
{
|
||||
|
@ -222,10 +222,19 @@ where
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CycleError {
|
||||
pub struct CycleError<I = QueryStackFrameExtra> {
|
||||
/// The query and related span that uses the cycle.
|
||||
pub usage: Option<(Span, QueryStackFrame)>,
|
||||
pub cycle: Vec<QueryInfo>,
|
||||
pub usage: Option<(Span, QueryStackFrame<I>)>,
|
||||
pub cycle: Vec<QueryInfo<I>>,
|
||||
}
|
||||
|
||||
impl<I> CycleError<I> {
|
||||
fn lift<Qcx: QueryContext<QueryInfo = I>>(&self, qcx: Qcx) -> CycleError<QueryStackFrameExtra> {
|
||||
CycleError {
|
||||
usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift(qcx))),
|
||||
cycle: self.cycle.iter().map(|info| info.lift(qcx)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether there is already a value for this key in the in-memory
|
||||
|
@ -262,10 +271,10 @@ where
|
|||
{
|
||||
// Ensure there was no errors collecting all active jobs.
|
||||
// We need the complete map to ensure we find a cycle to break.
|
||||
let query_map = qcx.collect_active_jobs().expect("failed to collect active queries");
|
||||
let query_map = qcx.collect_active_jobs().ok().expect("failed to collect active queries");
|
||||
|
||||
let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span);
|
||||
(mk_cycle(query, qcx, error), None)
|
||||
(mk_cycle(query, qcx, error.lift(qcx)), None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -274,7 +283,7 @@ fn wait_for_query<Q, Qcx>(
|
|||
qcx: Qcx,
|
||||
span: Span,
|
||||
key: Q::Key,
|
||||
latch: QueryLatch,
|
||||
latch: QueryLatch<Qcx::QueryInfo>,
|
||||
current: Option<QueryJobId>,
|
||||
) -> (Q::Value, Option<DepNodeIndex>)
|
||||
where
|
||||
|
@ -314,7 +323,7 @@ where
|
|||
|
||||
(v, Some(index))
|
||||
}
|
||||
Err(cycle) => (mk_cycle(query, qcx, cycle), None),
|
||||
Err(cycle) => (mk_cycle(query, qcx, cycle.lift(qcx)), None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,7 +401,7 @@ where
|
|||
fn execute_job<Q, Qcx, const INCR: bool>(
|
||||
query: Q,
|
||||
qcx: Qcx,
|
||||
state: &QueryState<Q::Key>,
|
||||
state: &QueryState<Q::Key, Qcx::QueryInfo>,
|
||||
key: Q::Key,
|
||||
key_hash: u64,
|
||||
id: QueryJobId,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue