Skip to content

Commit 29a4ec0

Browse files
committed
Make queries thread safe. Remove the query stack and make queries point to their parents instead.
1 parent 4be5d36 commit 29a4ec0

File tree

6 files changed

+486
-182
lines changed

6 files changed

+486
-182
lines changed

src/librustc/ty/context.rs

+136-46
Original file line numberDiff line numberDiff line change
@@ -1222,7 +1222,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
12221222
Lrc::new(StableVec::new(v)));
12231223
}
12241224

1225-
tls::enter_global(GlobalCtxt {
1225+
let gcx = &GlobalCtxt {
12261226
sess: s,
12271227
cstore,
12281228
global_arenas: &arenas.global,
@@ -1263,7 +1263,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
12631263
all_traits: RefCell::new(None),
12641264
tx_to_llvm_workers: tx,
12651265
output_filenames: Arc::new(output_filenames.clone()),
1266-
}, f)
1266+
};
1267+
1268+
tls::enter_global(gcx, f)
12671269
}
12681270

12691271
pub fn consider_optimizing<T: Fn() -> String>(&self, msg: T) -> bool {
@@ -1487,11 +1489,25 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
14871489

14881490
impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> {
14891491
/// Call the closure with a local `TyCtxt` using the given arena.
1490-
pub fn enter_local<F, R>(&self, arena: &'tcx DroplessArena, f: F) -> R
1492+
pub fn enter_local<F, R>(&self,
1493+
arena: &'tcx DroplessArena,
1494+
f: F) -> R
14911495
where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
14921496
{
14931497
let interners = CtxtInterners::new(arena);
1494-
tls::enter(self, &interners, f)
1498+
let tcx = TyCtxt {
1499+
gcx: self,
1500+
interners: &interners,
1501+
};
1502+
ty::tls::with_related_context(tcx.global_tcx(), |icx| {
1503+
let new_icx = ty::tls::ImplicitCtxt {
1504+
tcx,
1505+
query: icx.query.clone(),
1506+
};
1507+
ty::tls::enter_context(&new_icx, |new_icx| {
1508+
f(new_icx.tcx)
1509+
})
1510+
})
14951511
}
14961512
}
14971513

@@ -1638,21 +1654,34 @@ impl<'a, 'tcx> Lift<'tcx> for &'a Slice<Predicate<'a>> {
16381654
}
16391655

16401656
pub mod tls {
1641-
use super::{CtxtInterners, GlobalCtxt, TyCtxt};
1657+
use super::{GlobalCtxt, TyCtxt};
16421658

16431659
use std::cell::Cell;
16441660
use std::fmt;
1661+
use std::mem;
16451662
use syntax_pos;
1663+
use ty::maps;
1664+
use errors::{Diagnostic, TRACK_DIAGNOSTICS};
1665+
use rustc_data_structures::OnDrop;
1666+
use rustc_data_structures::sync::Lrc;
1667+
1668+
#[derive(Clone)]
1669+
pub struct ImplicitCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
1670+
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
1671+
pub query: Option<Lrc<maps::QueryJob<'gcx>>>,
1672+
}
1673+
1674+
thread_local!(static TLV: Cell<usize> = Cell::new(0));
16461675

1647-
/// Marker types used for the scoped TLS slot.
1648-
/// The type context cannot be used directly because the scoped TLS
1649-
/// in libstd doesn't allow types generic over lifetimes.
1650-
enum ThreadLocalGlobalCtxt {}
1651-
enum ThreadLocalInterners {}
1676+
fn set_tlv<F: FnOnce() -> R, R>(value: usize, f: F) -> R {
1677+
let old = get_tlv();
1678+
let _reset = OnDrop(move || TLV.with(|tlv| tlv.set(old)));
1679+
TLV.with(|tlv| tlv.set(value));
1680+
f()
1681+
}
16521682

1653-
thread_local! {
1654-
static TLS_TCX: Cell<Option<(*const ThreadLocalGlobalCtxt,
1655-
*const ThreadLocalInterners)>> = Cell::new(None)
1683+
fn get_tlv() -> usize {
1684+
TLV.with(|tlv| tlv.get())
16561685
}
16571686

16581687
fn span_debug(span: syntax_pos::Span, f: &mut fmt::Formatter) -> fmt::Result {
@@ -1661,59 +1690,120 @@ pub mod tls {
16611690
})
16621691
}
16631692

1664-
pub fn enter_global<'gcx, F, R>(gcx: GlobalCtxt<'gcx>, f: F) -> R
1665-
where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'gcx>) -> R
1693+
fn track_diagnostic(diagnostic: &Diagnostic) {
1694+
with_context(|context| {
1695+
if let Some(ref query) = context.query {
1696+
query.diagnostics.lock().push(diagnostic.clone());
1697+
}
1698+
})
1699+
}
1700+
1701+
pub fn with_thread_locals<F, R>(f: F) -> R
1702+
where F: FnOnce() -> R
16661703
{
16671704
syntax_pos::SPAN_DEBUG.with(|span_dbg| {
16681705
let original_span_debug = span_dbg.get();
16691706
span_dbg.set(span_debug);
1670-
let result = enter(&gcx, &gcx.global_interners, f);
1671-
span_dbg.set(original_span_debug);
1672-
result
1707+
1708+
let _on_drop = OnDrop(move || {
1709+
span_dbg.set(original_span_debug);
1710+
});
1711+
1712+
TRACK_DIAGNOSTICS.with(|current| {
1713+
let original = current.get();
1714+
current.set(track_diagnostic);
1715+
1716+
let _on_drop = OnDrop(move || {
1717+
current.set(original);
1718+
});
1719+
1720+
f()
1721+
})
16731722
})
16741723
}
16751724

1676-
pub fn enter<'a, 'gcx: 'tcx, 'tcx, F, R>(gcx: &'a GlobalCtxt<'gcx>,
1677-
interners: &'a CtxtInterners<'tcx>,
1678-
f: F) -> R
1679-
where F: FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
1725+
pub fn enter_global<'gcx, F, R>(gcx: &GlobalCtxt<'gcx>, f: F) -> R
1726+
where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'gcx>) -> R
16801727
{
1681-
let gcx_ptr = gcx as *const _ as *const ThreadLocalGlobalCtxt;
1682-
let interners_ptr = interners as *const _ as *const ThreadLocalInterners;
1683-
TLS_TCX.with(|tls| {
1684-
let prev = tls.get();
1685-
tls.set(Some((gcx_ptr, interners_ptr)));
1686-
let ret = f(TyCtxt {
1728+
with_thread_locals(|| {
1729+
let tcx = TyCtxt {
16871730
gcx,
1688-
interners,
1689-
});
1690-
tls.set(prev);
1691-
ret
1731+
interners: &gcx.global_interners,
1732+
};
1733+
let icx = ImplicitCtxt {
1734+
tcx,
1735+
query: None,
1736+
};
1737+
enter_context(&icx, |_| {
1738+
f(tcx)
1739+
})
1740+
})
1741+
}
1742+
1743+
pub fn enter_context<'a, 'gcx: 'tcx, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'gcx, 'tcx>,
1744+
f: F) -> R
1745+
where F: FnOnce(&ImplicitCtxt<'a, 'gcx, 'tcx>) -> R
1746+
{
1747+
set_tlv(context as *const _ as usize, || {
1748+
f(&context)
1749+
})
1750+
}
1751+
1752+
pub fn with_context_opt<F, R>(f: F) -> R
1753+
where F: for<'a, 'gcx, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'gcx, 'tcx>>) -> R
1754+
{
1755+
let context = get_tlv();
1756+
if context == 0 {
1757+
f(None)
1758+
} else {
1759+
unsafe { f(Some(&*(context as *const ImplicitCtxt))) }
1760+
}
1761+
}
1762+
1763+
pub fn with_fully_related_context<'a, 'gcx, 'tcx, F, R>(tcx: TyCtxt<'a, 'gcx, 'tcx>, f: F) -> R
1764+
where F: for<'b> FnOnce(&ImplicitCtxt<'b, 'gcx, 'tcx>) -> R
1765+
{
1766+
with_context(|context| {
1767+
unsafe {
1768+
let gcx = tcx.gcx as *const _ as usize;
1769+
let interners = tcx.interners as *const _ as usize;
1770+
assert!(context.tcx.gcx as *const _ as usize == gcx);
1771+
assert!(context.tcx.interners as *const _ as usize == interners);
1772+
let context: &ImplicitCtxt = mem::transmute(context);
1773+
f(context)
1774+
}
16921775
})
16931776
}
16941777

1778+
pub fn with_related_context<'a, 'gcx, 'tcx1, F, R>(tcx: TyCtxt<'a, 'gcx, 'tcx1>, f: F) -> R
1779+
where F: for<'b, 'tcx2> FnOnce(&ImplicitCtxt<'b, 'gcx, 'tcx2>) -> R
1780+
{
1781+
with_context(|context| {
1782+
unsafe {
1783+
let gcx = tcx.gcx as *const _ as usize;
1784+
assert!(context.tcx.gcx as *const _ as usize == gcx);
1785+
let context: &ImplicitCtxt = mem::transmute(context);
1786+
f(context)
1787+
}
1788+
})
1789+
}
1790+
1791+
pub fn with_context<F, R>(f: F) -> R
1792+
where F: for<'a, 'gcx, 'tcx> FnOnce(&ImplicitCtxt<'a, 'gcx, 'tcx>) -> R
1793+
{
1794+
with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls")))
1795+
}
1796+
16951797
pub fn with<F, R>(f: F) -> R
16961798
where F: for<'a, 'gcx, 'tcx> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
16971799
{
1698-
TLS_TCX.with(|tcx| {
1699-
let (gcx, interners) = tcx.get().unwrap();
1700-
let gcx = unsafe { &*(gcx as *const GlobalCtxt) };
1701-
let interners = unsafe { &*(interners as *const CtxtInterners) };
1702-
f(TyCtxt {
1703-
gcx,
1704-
interners,
1705-
})
1706-
})
1800+
with_context(|context| f(context.tcx))
17071801
}
17081802

17091803
pub fn with_opt<F, R>(f: F) -> R
17101804
where F: for<'a, 'gcx, 'tcx> FnOnce(Option<TyCtxt<'a, 'gcx, 'tcx>>) -> R
17111805
{
1712-
if TLS_TCX.with(|tcx| tcx.get().is_some()) {
1713-
with(|v| f(Some(v)))
1714-
} else {
1715-
f(None)
1716-
}
1806+
with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx)))
17171807
}
17181808
}
17191809

src/librustc/ty/maps/job.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(warnings)]
12+
13+
use std::mem;
14+
use std::sync::atomic::AtomicBool;
15+
use std::sync::atomic::Ordering;
16+
use rustc_data_structures::sync::{Lock, LockGuard, Lrc};
17+
use syntax_pos::Span;
18+
use ty::tls;
19+
use ty::maps::Query;
20+
use ty::maps::plumbing::CycleError;
21+
use ty::context::TyCtxt;
22+
use errors::Diagnostic;
23+
use std::process;
24+
use std::fmt;
25+
use std::sync::{Arc, Mutex};
26+
use std::collections::HashSet;
27+
28+
pub struct PoisonedJob;
29+
30+
#[derive(Clone, Debug)]
31+
pub struct StackEntry<'tcx> {
32+
pub span: Span,
33+
pub query: Query<'tcx>,
34+
}
35+
36+
pub struct QueryJob<'tcx> {
37+
pub entry: StackEntry<'tcx>,
38+
pub parent: Option<Lrc<QueryJob<'tcx>>>,
39+
pub track_diagnostics: bool,
40+
pub diagnostics: Lock<Vec<Diagnostic>>,
41+
}
42+
43+
impl<'tcx> QueryJob<'tcx> {
44+
pub fn new(
45+
entry: StackEntry<'tcx>,
46+
track_diagnostics: bool,
47+
parent: Option<Lrc<QueryJob<'tcx>>>,
48+
) -> Self {
49+
QueryJob {
50+
track_diagnostics,
51+
diagnostics: Lock::new(Vec::new()),
52+
entry,
53+
parent,
54+
}
55+
}
56+
57+
pub(super) fn await<'lcx>(
58+
&self,
59+
tcx: TyCtxt<'_, 'tcx, 'lcx>,
60+
span: Span,
61+
) -> Result<(), CycleError<'tcx>> {
62+
// The query is already executing, so this must be a cycle for single threaded rustc,
63+
// so we find the cycle and return it
64+
65+
let mut current_job = tls::with_related_context(tcx, |icx| icx.query.clone());
66+
let mut cycle = Vec::new();
67+
68+
while let Some(job) = current_job {
69+
cycle.insert(0, job.entry.clone());
70+
71+
if &*job as *const _ == self as *const _ {
72+
break;
73+
}
74+
75+
current_job = job.parent.clone();
76+
}
77+
78+
Err(CycleError { span, cycle })
79+
}
80+
81+
pub fn signal_complete(&self) {
82+
// Signals to waiters that the query is complete.
83+
// This is a no-op for single threaded rustc
84+
}
85+
}
86+
87+
pub(super) enum QueryResult<'tcx, T> {
88+
Started(Lrc<QueryJob<'tcx>>),
89+
Complete(T),
90+
Poisoned,
91+
}

src/librustc/ty/maps/mod.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use hir::def_id::{CrateNum, DefId, DefIndex};
1414
use hir::def::{Def, Export};
1515
use hir::{self, TraitCandidate, ItemLocalId, TransFnAttrs};
1616
use hir::svh::Svh;
17-
use infer::canonical::{Canonical, QueryResult};
17+
use infer::canonical::{self, Canonical};
1818
use lint;
1919
use middle::borrowck::BorrowCheckResult;
2020
use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary,
@@ -66,6 +66,10 @@ mod plumbing;
6666
use self::plumbing::*;
6767
pub use self::plumbing::force_from_dep_node;
6868

69+
mod job;
70+
pub use self::job::{QueryJob, StackEntry, PoisonedJob};
71+
use self::job::QueryResult;
72+
6973
mod keys;
7074
pub use self::keys::Key;
7175

@@ -399,7 +403,7 @@ define_maps! { <'tcx>
399403
[] fn normalize_projection_ty: NormalizeProjectionTy(
400404
CanonicalProjectionGoal<'tcx>
401405
) -> Result<
402-
Lrc<Canonical<'tcx, QueryResult<'tcx, NormalizationResult<'tcx>>>>,
406+
Lrc<Canonical<'tcx, canonical::QueryResult<'tcx, NormalizationResult<'tcx>>>>,
403407
NoSolution,
404408
>,
405409

@@ -412,7 +416,7 @@ define_maps! { <'tcx>
412416
[] fn dropck_outlives: DropckOutlives(
413417
CanonicalTyGoal<'tcx>
414418
) -> Result<
415-
Lrc<Canonical<'tcx, QueryResult<'tcx, DropckOutlivesResult<'tcx>>>>,
419+
Lrc<Canonical<'tcx, canonical::QueryResult<'tcx, DropckOutlivesResult<'tcx>>>>,
416420
NoSolution,
417421
>,
418422

0 commit comments

Comments
 (0)