Skip to content

Commit ff00c6f

Browse files
committed
Use horde's SyncTable for default query caches and CtxtInterners
1 parent 365c0e1 commit ff00c6f

12 files changed

Lines changed: 264 additions & 72 deletions

File tree

Cargo.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,15 @@ dependencies = [
16891689
"windows-sys 0.61.2",
16901690
]
16911691

1692+
[[package]]
1693+
name = "horde"
1694+
version = "0.1.4"
1695+
source = "registry+https://github.com/rust-lang/crates.io-index"
1696+
checksum = "7dd2fd3c2d18a3b8dc3c1afa76dce1e9e207f131b7f2e1a2970a85bfa86cccce"
1697+
dependencies = [
1698+
"parking_lot",
1699+
]
1700+
16921701
[[package]]
16931702
name = "html-checker"
16941703
version = "0.1.0"
@@ -3770,6 +3779,7 @@ dependencies = [
37703779
"elsa",
37713780
"ena",
37723781
"hashbrown 0.17.0",
3782+
"horde",
37733783
"indexmap",
37743784
"jobserver",
37753785
"libc",

compiler/rustc_data_structures/Cargo.toml

Lines changed: 1 addition & 0 deletions
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.3"
13+
horde = { version = "0.1.4", features = ["nightly"] }
1314
indexmap = "2.14.0"
1415
jobserver_crate = { version = "0.1.28", package = "jobserver" }
1516
measureme = "12.0.1"

compiler/rustc_data_structures/src/sharded.rs

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -209,60 +209,6 @@ impl<K: Eq + Hash, V> ShardedHashMap<K, V> {
209209
}
210210
}
211211

212-
impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
213-
#[inline]
214-
pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K
215-
where
216-
K: Borrow<Q>,
217-
Q: Hash + Eq,
218-
{
219-
let hash = make_hash(value);
220-
let mut shard = self.lock_shard_by_hash(hash);
221-
222-
match table_entry(&mut shard, hash, value) {
223-
Entry::Occupied(e) => e.get().0,
224-
Entry::Vacant(e) => {
225-
let v = make();
226-
e.insert((v, ()));
227-
v
228-
}
229-
}
230-
}
231-
232-
#[inline]
233-
pub fn intern<Q>(&self, value: Q, make: impl FnOnce(Q) -> K) -> K
234-
where
235-
K: Borrow<Q>,
236-
Q: Hash + Eq,
237-
{
238-
let hash = make_hash(&value);
239-
let mut shard = self.lock_shard_by_hash(hash);
240-
241-
match table_entry(&mut shard, hash, &value) {
242-
Entry::Occupied(e) => e.get().0,
243-
Entry::Vacant(e) => {
244-
let v = make(value);
245-
e.insert((v, ()));
246-
v
247-
}
248-
}
249-
}
250-
}
251-
252-
pub trait IntoPointer {
253-
/// Returns a pointer which outlives `self`.
254-
fn into_pointer(&self) -> *const ();
255-
}
256-
257-
impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
258-
pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool {
259-
let hash = make_hash(&value);
260-
let shard = self.lock_shard_by_hash(hash);
261-
let value = value.into_pointer();
262-
shard.find(hash, |(k, ())| k.into_pointer() == value).is_some()
263-
}
264-
}
265-
266212
#[inline]
267213
pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
268214
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,
@@ -41,13 +42,15 @@ pub use self::parallel::{
4142
broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn,
4243
try_par_for_each_in,
4344
};
45+
pub use self::sync_table::{IntoPointer, LockedWrite, Read, SyncTable};
4446
pub use self::vec::{AppendOnlyIndexVec, AppendOnlyVec};
4547
pub use self::worker_local::{Registry, WorkerLocal};
4648
pub use crate::marker::*;
4749

4850
mod freeze;
4951
mod lock;
5052
mod parallel;
53+
mod sync_table;
5154
mod vec;
5255
mod worker_local;
5356

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

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};
@@ -215,7 +215,10 @@ pub(crate) fn run_in_thread_pool_with_globals<
215215
let builder = rustc_thread_pool::ThreadPoolBuilder::new()
216216
.thread_name(|_| "rustc".to_string())
217217
.acquire_thread_handler(move || proxy_.acquire_thread())
218-
.release_thread_handler(move || proxy__.release_thread())
218+
.release_thread_handler(move || {
219+
collect::release();
220+
proxy__.release_thread()
221+
})
219222
.num_threads(threads)
220223
.deadlock_handler(move || {
221224
// On deadlock, creates a new thread and forwards information in thread

0 commit comments

Comments
 (0)