Skip to content

Commit ac58a2a

Browse files
committed
Use horde's SyncTable for default query caches and CtxtInterners
1 parent 1f7f8ea commit ac58a2a

13 files changed

Lines changed: 271 additions & 78 deletions

File tree

Cargo.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,15 @@ dependencies = [
17091709
"windows-sys 0.61.2",
17101710
]
17111711

1712+
[[package]]
1713+
name = "horde"
1714+
version = "0.1.2"
1715+
source = "registry+https://github.com/rust-lang/crates.io-index"
1716+
checksum = "f3d7c629771c2e116e71d8e7bbc6e6e0450a8817766a7230bb0c98f81311df34"
1717+
dependencies = [
1718+
"parking_lot",
1719+
]
1720+
17121721
[[package]]
17131722
name = "html-checker"
17141723
version = "0.1.0"
@@ -3803,6 +3812,7 @@ dependencies = [
38033812
"elsa",
38043813
"ena",
38053814
"hashbrown 0.16.1",
3815+
"horde",
38063816
"indexmap",
38073817
"jobserver",
38083818
"libc",

compiler/rustc_data_structures/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ bitflags = "2.4.1"
1010
either = "1.0"
1111
elsa = "1.11.0"
1212
ena = "0.14.4"
13+
horde = { version = "0.1.2", features = ["nightly"] }
1314
indexmap = "2.12.1"
1415
jobserver_crate = { version = "0.1.28", package = "jobserver" }
1516
measureme = "12.0.1"
@@ -33,7 +34,7 @@ tracing = "0.1"
3334
[dependencies.hashbrown]
3435
version = "0.16.1"
3536
default-features = false
36-
features = ["nightly"] # for may_dangle
37+
features = ["nightly"] # for may_dangle
3738

3839
[target.'cfg(windows)'.dependencies.windows]
3940
version = "0.61.0"

compiler/rustc_data_structures/src/sharded.rs

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -201,60 +201,6 @@ impl<K: Eq + Hash, V> ShardedHashMap<K, V> {
201201
}
202202
}
203203

204-
impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
205-
#[inline]
206-
pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K
207-
where
208-
K: Borrow<Q>,
209-
Q: Hash + Eq,
210-
{
211-
let hash = make_hash(value);
212-
let mut shard = self.lock_shard_by_hash(hash);
213-
214-
match table_entry(&mut shard, hash, value) {
215-
Entry::Occupied(e) => e.get().0,
216-
Entry::Vacant(e) => {
217-
let v = make();
218-
e.insert((v, ()));
219-
v
220-
}
221-
}
222-
}
223-
224-
#[inline]
225-
pub fn intern<Q>(&self, value: Q, make: impl FnOnce(Q) -> K) -> K
226-
where
227-
K: Borrow<Q>,
228-
Q: Hash + Eq,
229-
{
230-
let hash = make_hash(&value);
231-
let mut shard = self.lock_shard_by_hash(hash);
232-
233-
match table_entry(&mut shard, hash, &value) {
234-
Entry::Occupied(e) => e.get().0,
235-
Entry::Vacant(e) => {
236-
let v = make(value);
237-
e.insert((v, ()));
238-
v
239-
}
240-
}
241-
}
242-
}
243-
244-
pub trait IntoPointer {
245-
/// Returns a pointer which outlives `self`.
246-
fn into_pointer(&self) -> *const ();
247-
}
248-
249-
impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
250-
pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool {
251-
let hash = make_hash(&value);
252-
let shard = self.lock_shard_by_hash(hash);
253-
let value = value.into_pointer();
254-
shard.find(hash, |(k, ())| k.into_pointer() == value).is_some()
255-
}
256-
}
257-
258204
#[inline]
259205
pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
260206
let mut state = FxHasher::default();

compiler/rustc_data_structures/src/sync.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use std::collections::HashMap;
2626
use std::hash::{BuildHasher, Hash};
2727

28+
pub use horde::collect;
2829
pub use parking_lot::{
2930
MappedRwLockReadGuard as MappedReadGuard, MappedRwLockWriteGuard as MappedWriteGuard,
3031
RwLockReadGuard as ReadGuard, RwLockWriteGuard as WriteGuard,
@@ -39,13 +40,15 @@ pub use self::parallel::{
3940
broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn,
4041
try_par_for_each_in,
4142
};
43+
pub use self::sync_table::{IntoPointer, LockedWrite, Read, SyncTable};
4244
pub use self::vec::{AppendOnlyIndexVec, AppendOnlyVec};
4345
pub use self::worker_local::{Registry, WorkerLocal};
4446
pub use crate::marker::*;
4547

4648
mod freeze;
4749
mod lock;
4850
mod parallel;
51+
mod sync_table;
4952
mod vec;
5053
mod worker_local;
5154

compiler/rustc_data_structures/src/sync/lock.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ impl<T> Lock<T> {
112112
self.data.get_mut()
113113
}
114114

115+
#[inline(always)]
116+
pub fn mode(&self) -> Mode {
117+
self.mode
118+
}
119+
115120
#[inline(always)]
116121
pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
117122
let mode = self.mode;
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
use std::borrow::Borrow;
2+
use std::hash::{BuildHasher, Hash, Hasher};
3+
use std::hint::cold_path;
4+
use std::ops::{Deref, DerefMut};
5+
6+
use horde::collect::{Pin, pin};
7+
pub use horde::sync_table::Read;
8+
use horde::sync_table::Write;
9+
use rustc_hash::FxBuildHasher;
10+
11+
use crate::sync::{DynSync, Lock, LockGuard, Mode};
12+
13+
pub struct SyncTable<K, V> {
14+
// We use this lock to protect `table` instead of the internal mutex in `horde::SyncTable`
15+
// as it's faster when synchronization is disabled.
16+
lock: Lock<()>,
17+
18+
table: horde::SyncTable<K, V, FxBuildHasher>,
19+
}
20+
21+
// Memory reclamation can move elements to other threads for dropping,
22+
// so we require `Sync` instead of `DynSync` here
23+
unsafe impl<K: Sync, V: Sync> DynSync for SyncTable<K, V> where FxBuildHasher: Sync {}
24+
25+
impl<K, V> Default for SyncTable<K, V> {
26+
fn default() -> Self {
27+
Self { lock: Lock::default(), table: horde::SyncTable::default() }
28+
}
29+
}
30+
31+
impl<K, V> SyncTable<K, V> {
32+
/// Creates a [Read] handle from a pinned region.
33+
///
34+
/// Use [horde::collect::pin] to get a `Pin` instance.
35+
#[inline]
36+
pub fn read<'a>(&'a self, pin: Pin<'a>) -> Read<'a, K, V, FxBuildHasher> {
37+
self.table.read(pin)
38+
}
39+
40+
/// Creates a [LockedWrite] handle by taking the underlying mutex that protects writes.
41+
#[inline]
42+
pub fn lock(&self) -> LockedWrite<'_, K, V> {
43+
LockedWrite {
44+
_guard: self.lock.lock(),
45+
table: {
46+
// SAFETY: We ensure there's only 1 writer at a time using our own lock
47+
unsafe { self.table.unsafe_write() }
48+
},
49+
}
50+
}
51+
52+
/// Hashes a key with the table's hasher.
53+
#[inline]
54+
pub fn hash_key<Q>(&self, key: &Q) -> u64
55+
where
56+
K: Borrow<Q>,
57+
Q: ?Sized + Hash,
58+
{
59+
self.table.hash_key::<Q>(key)
60+
}
61+
62+
pub fn len(&self) -> usize {
63+
pin(|pin| self.read(pin).len())
64+
}
65+
66+
pub fn with_capacity(cap: usize) -> Self {
67+
Self { lock: Lock::new(()), table: horde::SyncTable::new_with(FxBuildHasher, cap) }
68+
}
69+
}
70+
71+
/// A handle to a [SyncTable] with write access protected by a lock.
72+
pub struct LockedWrite<'a, K, V> {
73+
table: Write<'a, K, V, FxBuildHasher>,
74+
_guard: LockGuard<'a, ()>,
75+
}
76+
77+
impl<'a, K, V> Deref for LockedWrite<'a, K, V> {
78+
type Target = Write<'a, K, V, FxBuildHasher>;
79+
80+
#[inline]
81+
fn deref(&self) -> &Self::Target {
82+
&self.table
83+
}
84+
}
85+
86+
impl<'a, K, V> DerefMut for LockedWrite<'a, K, V> {
87+
#[inline]
88+
fn deref_mut(&mut self) -> &mut Self::Target {
89+
&mut self.table
90+
}
91+
}
92+
93+
pub trait IntoPointer {
94+
/// Returns a pointer which outlives `self`.
95+
fn into_pointer(&self) -> *const ();
96+
}
97+
98+
impl<K: Eq + Hash + Copy + Send> SyncTable<K, ()> {
99+
pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool
100+
where
101+
K: IntoPointer,
102+
{
103+
pin(|pin| {
104+
let mut state = FxBuildHasher.build_hasher();
105+
value.hash(&mut state);
106+
let hash = state.finish();
107+
let value = value.into_pointer();
108+
self.read(pin).get_from_hash(hash, |entry| entry.into_pointer() == value).is_some()
109+
})
110+
}
111+
112+
#[inline]
113+
pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K
114+
where
115+
K: Borrow<Q>,
116+
Q: Hash + Eq,
117+
{
118+
if self.lock.mode() == Mode::Sync {
119+
pin(|pin| {
120+
let hash = self.hash_key(value);
121+
122+
let potential = match self.read(pin).get_potential(&value, Some(hash)) {
123+
Ok(entry) => return *entry.0,
124+
Err(potential) => {
125+
cold_path();
126+
potential
127+
}
128+
};
129+
130+
let mut write = self.lock();
131+
132+
let potential = match potential.refresh(self.read(pin), &value, Some(hash)) {
133+
Ok(entry) => {
134+
cold_path();
135+
return *entry.0;
136+
}
137+
Err(potential) => potential,
138+
};
139+
140+
let result = make();
141+
142+
potential.insert_new(&mut write, result, (), Some(hash));
143+
144+
result
145+
})
146+
} else {
147+
let mut write = self.lock();
148+
149+
let hash = self.hash_key(&value);
150+
151+
let entry = write.read().get(&value, Some(hash));
152+
if let Some(entry) = entry {
153+
return *entry.0;
154+
}
155+
156+
let result = make();
157+
158+
write.insert_new(result, (), Some(hash));
159+
160+
result
161+
}
162+
}
163+
164+
#[inline]
165+
pub fn intern<Q>(&self, value: Q, make: impl FnOnce(Q) -> K) -> K
166+
where
167+
K: Borrow<Q>,
168+
Q: Hash + Eq,
169+
{
170+
if self.lock.mode() == Mode::Sync {
171+
pin(|pin| {
172+
let hash = self.hash_key(&value);
173+
174+
let potential = match self.read(pin).get_potential(&value, Some(hash)) {
175+
Ok(entry) => return *entry.0,
176+
Err(potential) => {
177+
cold_path();
178+
potential
179+
}
180+
};
181+
182+
let mut write = self.lock();
183+
184+
let potential = match potential.refresh(self.read(pin), &value, Some(hash)) {
185+
Ok(entry) => {
186+
cold_path();
187+
return *entry.0;
188+
}
189+
Err(potential) => potential,
190+
};
191+
192+
let result = make(value);
193+
194+
potential.insert_new(&mut write, result, (), Some(hash));
195+
196+
result
197+
})
198+
} else {
199+
let mut write = self.lock();
200+
201+
let hash = self.hash_key(&value);
202+
203+
let entry = write.read().get(&value, Some(hash));
204+
if let Some(entry) = entry {
205+
return *entry.0;
206+
}
207+
208+
let result = make(value);
209+
210+
write.insert_new(result, (), Some(hash));
211+
212+
result
213+
}
214+
}
215+
}

compiler/rustc_interface/src/util.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_codegen_ssa::traits::CodegenBackend;
1414
use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig};
1515
use rustc_data_structures::fx::FxIndexMap;
1616
use rustc_data_structures::jobserver::Proxy;
17-
use rustc_data_structures::sync;
17+
use rustc_data_structures::sync::{self, collect};
1818
use rustc_metadata::{DylibError, EncodedMetadata, load_symbol_from_dylib};
1919
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
2020
use rustc_middle::ty::{CurrentGcx, TyCtxt};
@@ -216,7 +216,10 @@ pub(crate) fn run_in_thread_pool_with_globals<
216216
let builder = rustc_thread_pool::ThreadPoolBuilder::new()
217217
.thread_name(|_| "rustc".to_string())
218218
.acquire_thread_handler(move || proxy_.acquire_thread())
219-
.release_thread_handler(move || proxy__.release_thread())
219+
.release_thread_handler(move || {
220+
collect::release();
221+
proxy__.release_thread()
222+
})
220223
.num_threads(threads)
221224
.deadlock_handler(move || {
222225
// On deadlock, creates a new thread and forwards information in thread

0 commit comments

Comments
 (0)