Lrc::new(StableVec::new(v)));
}
- tls::enter_global(GlobalCtxt {
+ let gcx = &GlobalCtxt {
sess: s,
cstore,
global_arenas: &arenas.global,
all_traits: RefCell::new(None),
tx_to_llvm_workers: tx,
output_filenames: Arc::new(output_filenames.clone()),
- }, f)
+ };
+
+ tls::enter_global(gcx, f)
}
pub fn consider_optimizing<T: Fn() -> String>(&self, msg: T) -> bool {
impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> {
/// Call the closure with a local `TyCtxt` using the given arena.
- pub fn enter_local<F, R>(&self, arena: &'tcx DroplessArena, f: F) -> R
- where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
+ pub fn enter_local<F, R>(
+ &self,
+ arena: &'tcx DroplessArena,
+ f: F
+ ) -> R
+ where
+ F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
{
let interners = CtxtInterners::new(arena);
- tls::enter(self, &interners, f)
+ let tcx = TyCtxt {
+ gcx: self,
+ interners: &interners,
+ };
+ ty::tls::with_related_context(tcx.global_tcx(), |icx| {
+ let new_icx = ty::tls::ImplicitCtxt {
+ tcx,
+ query: icx.query.clone(),
+ };
+ ty::tls::enter_context(&new_icx, |new_icx| {
+ f(new_icx.tcx)
+ })
+ })
}
}
}
pub mod tls {
- use super::{CtxtInterners, GlobalCtxt, TyCtxt};
+ use super::{GlobalCtxt, TyCtxt};
use std::cell::Cell;
use std::fmt;
+ use std::mem;
use syntax_pos;
+ use ty::maps;
+ use errors::{Diagnostic, TRACK_DIAGNOSTICS};
+ use rustc_data_structures::OnDrop;
+ use rustc_data_structures::sync::Lrc;
- /// Marker types used for the scoped TLS slot.
- /// The type context cannot be used directly because the scoped TLS
- /// in libstd doesn't allow types generic over lifetimes.
- enum ThreadLocalGlobalCtxt {}
- enum ThreadLocalInterners {}
+ /// This is the implicit state of rustc. It contains the current
+ /// TyCtxt and query. It is updated when creating a local interner or
+ /// executing a new query. Whenever there's a TyCtxt value available
+ /// you should also have access to an ImplicitCtxt through the functions
+ /// in this module.
+ #[derive(Clone)]
+ pub struct ImplicitCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+ /// The current TyCtxt. Initially created by `enter_global` and updated
+ /// by `enter_local` with a new local interner
+ pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
- thread_local! {
- static TLS_TCX: Cell<Option<(*const ThreadLocalGlobalCtxt,
- *const ThreadLocalInterners)>> = Cell::new(None)
+ /// The current query job, if any. This is updated by start_job in
+ /// ty::maps::plumbing when executing a query
+ pub query: Option<Lrc<maps::QueryJob<'gcx>>>,
}
+ // A thread local value which stores a pointer to the current ImplicitCtxt
+ thread_local!(static TLV: Cell<usize> = Cell::new(0));
+
+ fn set_tlv<F: FnOnce() -> R, R>(value: usize, f: F) -> R {
+ let old = get_tlv();
+ let _reset = OnDrop(move || TLV.with(|tlv| tlv.set(old)));
+ TLV.with(|tlv| tlv.set(value));
+ f()
+ }
+
+ fn get_tlv() -> usize {
+ TLV.with(|tlv| tlv.get())
+ }
+
+ /// This is a callback from libsyntax as it cannot access the implicit state
+ /// in librustc otherwise
fn span_debug(span: syntax_pos::Span, f: &mut fmt::Formatter) -> fmt::Result {
with(|tcx| {
write!(f, "{}", tcx.sess.codemap().span_to_string(span))
})
}
- pub fn enter_global<'gcx, F, R>(gcx: GlobalCtxt<'gcx>, f: F) -> R
- where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'gcx>) -> R
+ /// This is a callback from libsyntax as it cannot access the implicit state
+ /// in librustc otherwise. It is used to when diagnostic messages are
+ /// emitted and stores them in the current query, if there is one.
+ fn track_diagnostic(diagnostic: &Diagnostic) {
+ with_context(|context| {
+ if let Some(ref query) = context.query {
+ query.diagnostics.lock().push(diagnostic.clone());
+ }
+ })
+ }
+
+ /// Sets up the callbacks from libsyntax on the current thread
+ pub fn with_thread_locals<F, R>(f: F) -> R
+ where F: FnOnce() -> R
{
syntax_pos::SPAN_DEBUG.with(|span_dbg| {
let original_span_debug = span_dbg.get();
span_dbg.set(span_debug);
- let result = enter(&gcx, &gcx.global_interners, f);
- span_dbg.set(original_span_debug);
- result
+
+ let _on_drop = OnDrop(move || {
+ span_dbg.set(original_span_debug);
+ });
+
+ TRACK_DIAGNOSTICS.with(|current| {
+ let original = current.get();
+ current.set(track_diagnostic);
+
+ let _on_drop = OnDrop(move || {
+ current.set(original);
+ });
+
+ f()
+ })
})
}
- pub fn enter<'a, 'gcx: 'tcx, 'tcx, F, R>(gcx: &'a GlobalCtxt<'gcx>,
- interners: &'a CtxtInterners<'tcx>,
- f: F) -> R
- where F: FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
+ /// Sets `context` as the new current ImplicitCtxt for the duration of the function `f`
+ pub fn enter_context<'a, 'gcx: 'tcx, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'gcx, 'tcx>,
+ f: F) -> R
+ where F: FnOnce(&ImplicitCtxt<'a, 'gcx, 'tcx>) -> R
{
- let gcx_ptr = gcx as *const _ as *const ThreadLocalGlobalCtxt;
- let interners_ptr = interners as *const _ as *const ThreadLocalInterners;
- TLS_TCX.with(|tls| {
- let prev = tls.get();
- tls.set(Some((gcx_ptr, interners_ptr)));
- let ret = f(TyCtxt {
- gcx,
- interners,
- });
- tls.set(prev);
- ret
+ set_tlv(context as *const _ as usize, || {
+ f(&context)
})
}
- pub fn with<F, R>(f: F) -> R
- where F: for<'a, 'gcx, 'tcx> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
+ /// Enters GlobalCtxt by setting up libsyntax callbacks and
+ /// creating a initial TyCtxt and ImplicitCtxt.
+ /// This happens once per rustc session and TyCtxts only exists
+ /// inside the `f` function.
+ pub fn enter_global<'gcx, F, R>(gcx: &GlobalCtxt<'gcx>, f: F) -> R
+ where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'gcx>) -> R
{
- TLS_TCX.with(|tcx| {
- let (gcx, interners) = tcx.get().unwrap();
- let gcx = unsafe { &*(gcx as *const GlobalCtxt) };
- let interners = unsafe { &*(interners as *const CtxtInterners) };
- f(TyCtxt {
+ with_thread_locals(|| {
+ let tcx = TyCtxt {
gcx,
- interners,
+ interners: &gcx.global_interners,
+ };
+ let icx = ImplicitCtxt {
+ tcx,
+ query: None,
+ };
+ enter_context(&icx, |_| {
+ f(tcx)
})
})
}
- pub fn with_opt<F, R>(f: F) -> R
- where F: for<'a, 'gcx, 'tcx> FnOnce(Option<TyCtxt<'a, 'gcx, 'tcx>>) -> R
+ /// Allows access to the current ImplicitCtxt in a closure if one is available
+ pub fn with_context_opt<F, R>(f: F) -> R
+ where F: for<'a, 'gcx, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'gcx, 'tcx>>) -> R
{
- if TLS_TCX.with(|tcx| tcx.get().is_some()) {
- with(|v| f(Some(v)))
- } else {
+ let context = get_tlv();
+ if context == 0 {
f(None)
+ } else {
+ unsafe { f(Some(&*(context as *const ImplicitCtxt))) }
}
}
+
+ /// Allows access to the current ImplicitCtxt.
+ /// Panics if there is no ImplicitCtxt available
+ pub fn with_context<F, R>(f: F) -> R
+ where F: for<'a, 'gcx, 'tcx> FnOnce(&ImplicitCtxt<'a, 'gcx, 'tcx>) -> R
+ {
+ with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls")))
+ }
+
+ /// Allows access to the current ImplicitCtxt whose tcx field has the same global
+ /// interner as the tcx argument passed in. This means the closure is given an ImplicitCtxt
+ /// with the same 'gcx lifetime as the TyCtxt passed in.
+ /// This will panic if you pass it a TyCtxt which has a different global interner from
+ /// the current ImplicitCtxt's tcx field.
+ pub fn with_related_context<'a, 'gcx, 'tcx1, F, R>(tcx: TyCtxt<'a, 'gcx, 'tcx1>, f: F) -> R
+ where F: for<'b, 'tcx2> FnOnce(&ImplicitCtxt<'b, 'gcx, 'tcx2>) -> R
+ {
+ with_context(|context| {
+ unsafe {
+ let gcx = tcx.gcx as *const _ as usize;
+ assert!(context.tcx.gcx as *const _ as usize == gcx);
+ let context: &ImplicitCtxt = mem::transmute(context);
+ f(context)
+ }
+ })
+ }
+
+ /// Allows access to the current ImplicitCtxt whose tcx field has the same global
+ /// interner and local interner as the tcx argument passed in. This means the closure
+ /// is given an ImplicitCtxt with the same 'tcx and 'gcx lifetimes as the TyCtxt passed in.
+ /// This will panic if you pass it a TyCtxt which has a different global interner or
+ /// a different local interner from the current ImplicitCtxt's tcx field.
+ pub fn with_fully_related_context<'a, 'gcx, 'tcx, F, R>(tcx: TyCtxt<'a, 'gcx, 'tcx>, f: F) -> R
+ where F: for<'b> FnOnce(&ImplicitCtxt<'b, 'gcx, 'tcx>) -> R
+ {
+ with_context(|context| {
+ unsafe {
+ let gcx = tcx.gcx as *const _ as usize;
+ let interners = tcx.interners as *const _ as usize;
+ assert!(context.tcx.gcx as *const _ as usize == gcx);
+ assert!(context.tcx.interners as *const _ as usize == interners);
+ let context: &ImplicitCtxt = mem::transmute(context);
+ f(context)
+ }
+ })
+ }
+
+ /// Allows access to the TyCtxt in the current ImplicitCtxt.
+ /// Panics if there is no ImplicitCtxt available
+ pub fn with<F, R>(f: F) -> R
+ where F: for<'a, 'gcx, 'tcx> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
+ {
+ with_context(|context| f(context.tcx))
+ }
+
+ /// Allows access to the TyCtxt in the current ImplicitCtxt.
+ /// The closure is passed None if there is no ImplicitCtxt available
+ pub fn with_opt<F, R>(f: F) -> R
+ where F: for<'a, 'gcx, 'tcx> FnOnce(Option<TyCtxt<'a, 'gcx, 'tcx>>) -> R
+ {
+ with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx)))
+ }
}
macro_rules! sty_debug_print {
use dep_graph::{DepNodeIndex, DepNode, DepKind, DepNodeColor};
use errors::DiagnosticBuilder;
use ty::{TyCtxt};
-use ty::maps::Query; // NB: actually generated by the macros in this file
use ty::maps::config::QueryDescription;
+use ty::maps::job::{QueryResult, QueryInfo};
use ty::item_path;
use rustc_data_structures::fx::{FxHashMap};
-use std::cell::{Ref, RefMut};
+use rustc_data_structures::sync::LockGuard;
use std::marker::PhantomData;
-use std::mem;
use syntax_pos::Span;
pub(super) struct QueryMap<'tcx, D: QueryDescription<'tcx>> {
phantom: PhantomData<(D, &'tcx ())>,
- pub(super) map: FxHashMap<D::Key, QueryValue<D::Value>>,
+ pub(super) map: FxHashMap<D::Key, QueryResult<'tcx, QueryValue<D::Value>>>,
}
pub(super) struct QueryValue<T> {
pub(super) trait GetCacheInternal<'tcx>: QueryDescription<'tcx> + Sized {
fn get_cache_internal<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
- -> Ref<'a, QueryMap<'tcx, Self>>;
+ -> LockGuard<'a, QueryMap<'tcx, Self>>;
}
-pub(super) struct CycleError<'a, 'tcx: 'a> {
- span: Span,
- cycle: RefMut<'a, [(Span, Query<'tcx>)]>,
+#[derive(Clone)]
+pub(super) struct CycleError<'tcx> {
+ pub(super) span: Span,
+ pub(super) cycle: Vec<QueryInfo<'tcx>>,
+}
+
+/// The result of `try_get_lock`
+pub(super) enum TryGetLock<'a, 'tcx: 'a, T, D: QueryDescription<'tcx> + 'a> {
+ /// The query is not yet started. Contains a guard to the map eventually used to start it.
+ NotYetStarted(LockGuard<'a, QueryMap<'tcx, D>>),
+
+ /// The query was already completed.
+ /// Returns the result of the query and its dep node index
+ /// if it succeeded or a cycle error if it failed
+ JobCompleted(Result<(T, DepNodeIndex), CycleError<'tcx>>),
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
- pub(super) fn report_cycle(self, CycleError { span, cycle }: CycleError)
+ pub(super) fn report_cycle(self, CycleError { span, cycle: stack }: CycleError)
-> DiagnosticBuilder<'a>
{
- // Subtle: release the refcell lock before invoking `describe()`
- // below by dropping `cycle`.
- let stack = cycle.to_vec();
- mem::drop(cycle);
-
assert!(!stack.is_empty());
// Disable naming impls with types in this path, since that
"cyclic dependency detected");
err.span_label(span, "cyclic reference");
- err.span_note(self.sess.codemap().def_span(stack[0].0),
- &format!("the cycle begins when {}...", stack[0].1.describe(self)));
+ err.span_note(self.sess.codemap().def_span(stack[0].span),
+ &format!("the cycle begins when {}...", stack[0].query.describe(self)));
- for &(span, ref query) in &stack[1..] {
+ for &QueryInfo { span, ref query, .. } in &stack[1..] {
err.span_note(self.sess.codemap().def_span(span),
&format!("...which then requires {}...", query.describe(self)));
}
err.note(&format!("...which then again requires {}, completing the cycle.",
- stack[0].1.describe(self)));
+ stack[0].query.describe(self)));
return err
})
}
- pub(super) fn cycle_check<F, R>(self, span: Span, query: Query<'gcx>, compute: F)
- -> Result<R, CycleError<'a, 'gcx>>
- where F: FnOnce() -> R
- {
- {
- let mut stack = self.maps.query_stack.borrow_mut();
- if let Some((i, _)) = stack.iter().enumerate().rev()
- .find(|&(_, &(_, ref q))| *q == query) {
- return Err(CycleError {
- span,
- cycle: RefMut::map(stack, |stack| &mut stack[i..])
- });
- }
- stack.push((span, query));
- }
-
- let result = compute();
-
- self.maps.query_stack.borrow_mut().pop();
-
- Ok(result)
- }
-
/// Try to read a node index for the node dep_node.
/// A node will have an index, when it's already been marked green, or when we can mark it
/// green. This function will mark the current task as a reader of the specified node, when
[$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)*) => {
use dep_graph::DepNodeIndex;
- use std::cell::RefCell;
+ use std::mem;
+ use errors::Diagnostic;
+ use errors::FatalError;
+ use rustc_data_structures::sync::{Lock, LockGuard};
+ use rustc_data_structures::OnDrop;
define_map_struct! {
tcx: $tcx,
-> Self {
Maps {
providers,
- query_stack: RefCell::new(vec![]),
- $($name: RefCell::new(QueryMap::new())),*
+ $($name: Lock::new(QueryMap::new())),*
}
}
}
impl<$tcx> GetCacheInternal<$tcx> for queries::$name<$tcx> {
fn get_cache_internal<'a>(tcx: TyCtxt<'a, $tcx, $tcx>)
- -> ::std::cell::Ref<'a, QueryMap<$tcx, Self>> {
+ -> LockGuard<'a, QueryMap<$tcx, Self>> {
tcx.maps.$name.borrow()
}
}
DepNode::new(tcx, $node(*key))
}
+ /// Either get the lock of the query map, allowing us to
+ /// start executing the query, or it returns with the result of the query.
+ /// If the query already executed and panicked, this will fatal error / silently panic
+ fn try_get_lock(
+ tcx: TyCtxt<'a, $tcx, 'lcx>,
+ mut span: Span,
+ key: &$K
+ ) -> TryGetLock<'a, $tcx, $V, Self>
+ {
+ loop {
+ let lock = tcx.maps.$name.borrow_mut();
+ let job = if let Some(value) = lock.map.get(key) {
+ match *value {
+ QueryResult::Started(ref job) => Some(job.clone()),
+ QueryResult::Complete(ref value) => {
+ profq_msg!(tcx, ProfileQueriesMsg::CacheHit);
+ let result = Ok(((&value.value).clone(), value.index));
+ return TryGetLock::JobCompleted(result);
+ },
+ QueryResult::Poisoned => FatalError.raise(),
+ }
+ } else {
+ None
+ };
+ let job = if let Some(job) = job {
+ job
+ } else {
+ return TryGetLock::NotYetStarted(lock);
+ };
+ mem::drop(lock);
+
+ // This just matches the behavior of `try_get_with` so the span when
+ // we await matches the span we would use when executing.
+ // See the FIXME there.
+ if span == DUMMY_SP && stringify!($name) != "def_span" {
+ span = key.default_span(tcx);
+ }
+
+ if let Err(cycle) = job.await(tcx, span) {
+ return TryGetLock::JobCompleted(Err(cycle));
+ }
+ }
+ }
+
fn try_get_with(tcx: TyCtxt<'a, $tcx, 'lcx>,
mut span: Span,
key: $K)
- -> Result<$V, CycleError<'a, $tcx>>
+ -> Result<$V, CycleError<$tcx>>
{
debug!("ty::queries::{}::try_get_with(key={:?}, span={:?})",
stringify!($name),
)
);
- if let Some(value) = tcx.maps.$name.borrow().map.get(&key) {
- profq_msg!(tcx, ProfileQueriesMsg::CacheHit);
- tcx.dep_graph.read_index(value.index);
- return Ok((&value.value).clone());
+ /// Get the lock used to start the query or
+ /// return the result of the completed query
+ macro_rules! get_lock_or_return {
+ () => {{
+ match Self::try_get_lock(tcx, span, &key) {
+ TryGetLock::NotYetStarted(lock) => lock,
+ TryGetLock::JobCompleted(result) => {
+ return result.map(|(v, index)| {
+ tcx.dep_graph.read_index(index);
+ v
+ })
+ }
+ }
+ }}
}
+ let mut lock = get_lock_or_return!();
+
// FIXME(eddyb) Get more valid Span's on queries.
// def_span guard is necessary to prevent a recursive loop,
// default_span calls def_span query internally.
if span == DUMMY_SP && stringify!($name) != "def_span" {
- span = key.default_span(tcx)
+ // This might deadlock if we hold the map lock since we might be
+ // waiting for the def_span query and switch to some other fiber
+ // So we drop the lock here and reacquire it
+ mem::drop(lock);
+ span = key.default_span(tcx);
+ lock = get_lock_or_return!();
}
// Fast path for when incr. comp. is off. `to_dep_node` is
// expensive for some DepKinds.
if !tcx.dep_graph.is_fully_enabled() {
let null_dep_node = DepNode::new_no_params(::dep_graph::DepKind::Null);
- return Self::force(tcx, key, span, null_dep_node)
+ return Self::force_with_lock(tcx, key, span, lock, null_dep_node)
.map(|(v, _)| v);
}
if dep_node.kind.is_anon() {
profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin);
- let res = tcx.cycle_check(span, Query::$name(key), || {
- tcx.sess.diagnostic().track_diagnostics(|| {
- tcx.dep_graph.with_anon_task(dep_node.kind, || {
- Self::compute_result(tcx.global_tcx(), key)
- })
+ let res = Self::start_job(tcx, span, key, lock, |tcx| {
+ tcx.dep_graph.with_anon_task(dep_node.kind, || {
+ Self::compute_result(tcx.global_tcx(), key)
})
})?;
profq_msg!(tcx, ProfileQueriesMsg::ProviderEnd);
- let ((result, dep_node_index), diagnostics) = res;
+ let (((result, dep_node_index), diagnostics), job) = res;
tcx.dep_graph.read_index(dep_node_index);
tcx.on_disk_query_result_cache
.store_diagnostics_for_anon_node(dep_node_index, diagnostics);
- let value = QueryValue::new(result, dep_node_index);
+ let value = QueryValue::new(Clone::clone(&result), dep_node_index);
+
+ tcx.maps
+ .$name
+ .borrow_mut()
+ .map
+ .insert(key, QueryResult::Complete(value));
- return Ok((&tcx.maps
- .$name
- .borrow_mut()
- .map
- .entry(key)
- .or_insert(value)
- .value).clone());
+ job.signal_complete();
+
+ return Ok(result);
}
if !dep_node.kind.is_input() {
+ // try_mark_green_and_read may force queries. So we must drop our lock here
+ mem::drop(lock);
if let Some(dep_node_index) = tcx.try_mark_green_and_read(&dep_node) {
profq_msg!(tcx, ProfileQueriesMsg::CacheHit);
return Self::load_from_disk_and_cache_in_memory(tcx,
dep_node_index,
&dep_node)
}
+ lock = get_lock_or_return!();
}
- match Self::force(tcx, key, span, dep_node) {
+ match Self::force_with_lock(tcx, key, span, lock, dep_node) {
Ok((result, dep_node_index)) => {
tcx.dep_graph.read_index(dep_node_index);
Ok(result)
}
}
+ /// Creates a job for the query and updates the query map indicating that it started.
+ /// Then it changes ImplicitCtxt to point to the new query job while it executes.
+ /// If the query panics, this updates the query map to indicate so.
+ fn start_job<F, R>(tcx: TyCtxt<'_, $tcx, 'lcx>,
+ span: Span,
+ key: $K,
+ mut map: LockGuard<'_, QueryMap<$tcx, Self>>,
+ compute: F)
+ -> Result<((R, Vec<Diagnostic>), Lrc<QueryJob<$tcx>>), CycleError<$tcx>>
+ where F: for<'b> FnOnce(TyCtxt<'b, $tcx, 'lcx>) -> R
+ {
+ let query = Query::$name(Clone::clone(&key));
+
+ let entry = QueryInfo {
+ span,
+ query,
+ };
+
+ // The TyCtxt stored in TLS has the same global interner lifetime
+ // as `tcx`, so we use `with_related_context` to relate the 'gcx lifetimes
+ // when accessing the ImplicitCtxt
+ let (r, job) = ty::tls::with_related_context(tcx, move |icx| {
+ let job = Lrc::new(QueryJob::new(entry, icx.query.clone()));
+
+ // Store the job in the query map and drop the lock to allow
+ // others to wait it
+ map.map.entry(key).or_insert(QueryResult::Started(job.clone()));
+ mem::drop(map);
+
+ let r = {
+ let on_drop = OnDrop(|| {
+ // Poison the query so jobs waiting on it panic
+ tcx.maps
+ .$name
+ .borrow_mut()
+ .map
+ .insert(key, QueryResult::Poisoned);
+ // Also signal the completion of the job, so waiters
+ // will continue execution
+ job.signal_complete();
+ });
+
+ // Update the ImplicitCtxt to point to our new query job
+ let icx = ty::tls::ImplicitCtxt {
+ tcx,
+ query: Some(job.clone()),
+ };
+
+ // Use the ImplicitCtxt while we execute the query
+ let r = ty::tls::enter_context(&icx, |icx| {
+ compute(icx.tcx)
+ });
+
+ mem::forget(on_drop);
+
+ r
+ };
+
+ (r, job)
+ });
+
+ // Extract the diagnostic from the job
+ let diagnostics: Vec<_> = mem::replace(&mut *job.diagnostics.lock(), Vec::new());
+
+ Ok(((r, diagnostics), job))
+ }
+
fn compute_result(tcx: TyCtxt<'a, $tcx, 'lcx>, key: $K) -> $V {
let provider = tcx.maps.providers[key.map_crate()].$name;
provider(tcx.global_tcx(), key)
span: Span,
dep_node_index: DepNodeIndex,
dep_node: &DepNode)
- -> Result<$V, CycleError<'a, $tcx>>
+ -> Result<$V, CycleError<$tcx>>
{
+ // Note this function can be called concurrently from the same query
+ // We must ensure that this is handled correctly
+
debug_assert!(tcx.dep_graph.is_green(dep_node));
// First we try to load the result from the on-disk cache
None
};
- let result = if let Some(result) = result {
- result
+ let (result, job) = if let Some(result) = result {
+ (result, None)
} else {
// We could not load a result from the on-disk cache, so
// recompute.
- let (result, _ ) = tcx.cycle_check(span, Query::$name(key), || {
- // The diagnostics for this query have already been
- // promoted to the current session during
- // try_mark_green(), so we can ignore them here.
- tcx.sess.diagnostic().track_diagnostics(|| {
- // The dep-graph for this computation is already in
- // place
- tcx.dep_graph.with_ignore(|| {
- Self::compute_result(tcx, key)
- })
+
+ // The diagnostics for this query have already been
+ // promoted to the current session during
+ // try_mark_green(), so we can ignore them here.
+ let ((result, _), job) = Self::start_job(tcx,
+ span,
+ key,
+ tcx.maps.$name.borrow_mut(),
+ |tcx| {
+ // The dep-graph for this computation is already in
+ // place
+ tcx.dep_graph.with_ignore(|| {
+ Self::compute_result(tcx, key)
})
})?;
- result
+ (result, Some(job))
};
// If -Zincremental-verify-ich is specified, re-hash results from
tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true);
}
- let value = QueryValue::new(result, dep_node_index);
+ let value = QueryValue::new(Clone::clone(&result), dep_node_index);
+
+ tcx.maps
+ .$name
+ .borrow_mut()
+ .map
+ .insert(key, QueryResult::Complete(value));
- Ok((&tcx.maps
- .$name
- .borrow_mut()
- .map
- .entry(key)
- .or_insert(value)
- .value).clone())
+ job.map(|j| j.signal_complete());
+
+ Ok(result)
}
+ #[allow(dead_code)]
fn force(tcx: TyCtxt<'a, $tcx, 'lcx>,
key: $K,
span: Span,
dep_node: DepNode)
- -> Result<($V, DepNodeIndex), CycleError<'a, $tcx>> {
+ -> Result<($V, DepNodeIndex), CycleError<$tcx>> {
+ // We may be concurrently trying both execute and force a query
+ // Ensure that only one of them runs the query
+ let lock = match Self::try_get_lock(tcx, span, &key) {
+ TryGetLock::NotYetStarted(lock) => lock,
+ TryGetLock::JobCompleted(result) => return result,
+ };
+ Self::force_with_lock(tcx,
+ key,
+ span,
+ lock,
+ dep_node)
+ }
+
+ fn force_with_lock(tcx: TyCtxt<'a, $tcx, 'lcx>,
+ key: $K,
+ span: Span,
+ map: LockGuard<'_, QueryMap<$tcx, Self>>,
+ dep_node: DepNode)
+ -> Result<($V, DepNodeIndex), CycleError<$tcx>> {
debug_assert!(!tcx.dep_graph.dep_node_exists(&dep_node));
profq_msg!(tcx, ProfileQueriesMsg::ProviderBegin);
- let res = tcx.cycle_check(span, Query::$name(key), || {
- tcx.sess.diagnostic().track_diagnostics(|| {
- if dep_node.kind.is_eval_always() {
- tcx.dep_graph.with_eval_always_task(dep_node,
- tcx,
- key,
- Self::compute_result)
- } else {
- tcx.dep_graph.with_task(dep_node,
- tcx,
- key,
- Self::compute_result)
- }
- })
+ let res = Self::start_job(tcx,
+ span,
+ key,
+ map,
+ |tcx| {
+ if dep_node.kind.is_eval_always() {
+ tcx.dep_graph.with_eval_always_task(dep_node,
+ tcx,
+ key,
+ Self::compute_result)
+ } else {
+ tcx.dep_graph.with_task(dep_node,
+ tcx,
+ key,
+ Self::compute_result)
+ }
})?;
profq_msg!(tcx, ProfileQueriesMsg::ProviderEnd);
- let ((result, dep_node_index), diagnostics) = res;
+ let (((result, dep_node_index), diagnostics), job) = res;
if tcx.sess.opts.debugging_opts.query_dep_graph {
tcx.dep_graph.mark_loaded_from_cache(dep_node_index, false);
.store_diagnostics(dep_node_index, diagnostics);
}
- let value = QueryValue::new(result, dep_node_index);
+ let value = QueryValue::new(Clone::clone(&result), dep_node_index);
+
+ tcx.maps
+ .$name
+ .borrow_mut()
+ .map
+ .insert(key, QueryResult::Complete(value));
+
+ let job: Lrc<QueryJob> = job;
+
+ job.signal_complete();
- Ok(((&tcx.maps
- .$name
- .borrow_mut()
- .map
- .entry(key)
- .or_insert(value)
- .value).clone(),
- dep_node_index))
+ Ok((result, dep_node_index))
}
pub fn try_get(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K)
input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => {
pub struct Maps<$tcx> {
providers: IndexVec<CrateNum, Providers<$tcx>>,
- query_stack: RefCell<Vec<(Span, Query<$tcx>)>>,
- $($(#[$attr])* $name: RefCell<QueryMap<$tcx, queries::$name<$tcx>>>,)*
+ $($(#[$attr])* $name: Lock<QueryMap<$tcx, queries::$name<$tcx>>>,)*
}
};
}