rustc_data_structures/sync/
parallel.rs1use std::any::Any;
5use std::ops::{Index, Range};
6use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
7
8use parking_lot::Mutex;
9
10use crate::FatalErrorMarker;
11use crate::sync::{DynSend, DynSync, FromDyn, IntoDynSyncSend, mode};
12
13pub trait Dss = DynSend + DynSync;
14
15pub trait HasLength {
16 fn len(&self) -> usize;
17}
18
19impl<T> HasLength for [T] {
20 fn len(&self) -> usize {
21 self.len()
22 }
23}
24
25impl<T> HasLength for indexmap::set::Slice<T> {
26 fn len(&self) -> usize {
27 self.len()
28 }
29}
30
31pub trait ParSlice<T> = HasLength + Index<usize, Output = T> + DynSync;
32
33pub struct ParallelGuard {
39 panic: Mutex<Option<IntoDynSyncSend<Box<dyn Any + Send + 'static>>>>,
40}
41
42impl ParallelGuard {
43 pub fn run<R>(&self, f: impl FnOnce() -> R) -> Option<R> {
44 catch_unwind(AssertUnwindSafe(f))
45 .map_err(|err| {
46 let mut panic = self.panic.lock();
47 if panic.is_none() || !(*err).is::<FatalErrorMarker>() {
48 *panic = Some(IntoDynSyncSend(err));
49 }
50 })
51 .ok()
52 }
53}
54
55#[inline]
58pub fn parallel_guard<R>(f: impl FnOnce(&ParallelGuard) -> R) -> R {
59 let guard = ParallelGuard { panic: Mutex::new(None) };
60 let ret = f(&guard);
61 if let Some(IntoDynSyncSend(panic)) = guard.panic.into_inner() {
62 resume_unwind(panic);
63 }
64 ret
65}
66
67fn serial_join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
68where
69 A: FnOnce() -> RA,
70 B: FnOnce() -> RB,
71{
72 let (a, b) = parallel_guard(|guard| {
73 let a = guard.run(oper_a);
74 let b = guard.run(oper_b);
75 (a, b)
76 });
77 (a.unwrap(), b.unwrap())
78}
79
80pub fn spawn(func: impl FnOnce() + DynSend + 'static) {
81 if let Some(proof) = mode::check_dyn_thread_safe() {
82 let func = proof.derive(func);
83 rustc_thread_pool::spawn(|| {
84 (func.into_inner())();
85 });
86 } else {
87 func()
88 }
89}
90
91pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) {
96 parallel_guard(|guard: &ParallelGuard| {
97 if let Some(proof) = mode::check_dyn_thread_safe() {
98 let funcs = proof.derive(funcs);
99 rustc_thread_pool::scope(|s| {
100 let Some((first, rest)) = funcs.into_inner().split_at_mut_checked(1) else {
101 return;
102 };
103
104 for f in rest.iter_mut().rev() {
108 let f = proof.derive(f);
109 s.spawn(|_| {
110 guard.run(|| (f.into_inner())());
111 });
112 }
113
114 guard.run(|| first[0]());
117 });
118 } else {
119 for f in funcs {
120 guard.run(|| f());
121 }
122 }
123 });
124}
125
126#[inline]
127pub fn par_join<A, B, RA: DynSend, RB: DynSend>(oper_a: A, oper_b: B) -> (RA, RB)
128where
129 A: FnOnce() -> RA + DynSend,
130 B: FnOnce() -> RB + DynSend,
131{
132 if let Some(proof) = mode::check_dyn_thread_safe() {
133 let oper_a = proof.derive(oper_a);
134 let oper_b = proof.derive(oper_b);
135 let (a, b) = parallel_guard(|guard| {
136 rustc_thread_pool::join(
137 move || guard.run(move || proof.derive(oper_a.into_inner()())),
138 move || guard.run(move || proof.derive(oper_b.into_inner()())),
139 )
140 });
141 (a.unwrap().into_inner(), b.unwrap().into_inner())
142 } else {
143 serial_join(oper_a, oper_b)
144 }
145}
146
147const MAX_GROUP_COUNT: usize = 128;
148
149fn par_range_internal(
150 range: Range<usize>,
151 guard: &ParallelGuard,
152 proof: FromDyn<()>,
153 op: impl Fn(usize) + Dss,
154) {
155 let len = range.end - range.start;
156 match len {
157 0 => return,
158 1 => {
159 guard.run(|| op(range.start));
160 return;
161 }
162 _ => {}
163 }
164
165 let op = proof.derive(op);
166
167 rustc_thread_pool::scope(|s| {
168 let group_size = len.div_ceil(MAX_GROUP_COUNT);
169 if true {
if !(group_size > 0) {
::core::panicking::panic("assertion failed: group_size > 0")
};
};debug_assert!(group_size > 0);
170
171 let first_group = range.start..range.end.min(range.start + group_size);
173
174 let range = first_group.end..range.end;
175 let mut curr_end = range.end;
176
177 loop {
179 let curr_start = curr_end.checked_sub(group_size).unwrap_or(range.start);
180
181 let curr_start = curr_start.max(range.start);
183
184 if curr_start == curr_end {
186 break;
187 }
188
189 let curr_range = curr_start..curr_end;
190 s.spawn(|_| {
191 for idx in curr_range {
192 guard.run(|| op(idx));
193 }
194 });
195
196 if curr_start == range.start {
197 break;
198 }
199
200 curr_end -= group_size;
201 }
202
203 for idx in first_group {
204 guard.run(|| op(idx));
205 }
206 });
207}
208
209pub fn par_range(range: Range<usize>, op: impl Fn(usize) + Dss) {
214 parallel_guard(|guard| {
215 if let Some(proof) = mode::check_dyn_thread_safe() {
216 par_range_internal(range, guard, proof, op);
217 } else {
218 for i in range {
219 guard.run(|| op(i));
220 }
221 }
222 })
223}
224
225fn par_slice_mut<I: DynSend>(
226 items: &mut [I],
227 guard: &ParallelGuard,
228 for_each: impl Fn(&mut I) + Dss,
229 proof: FromDyn<()>,
230) {
231 match items {
232 [] => return,
233 [item] => {
234 guard.run(|| for_each(item));
235 return;
236 }
237 _ => (),
238 }
239
240 let for_each = proof.derive(for_each);
241 let mut items = for_each.derive(items);
242 rustc_thread_pool::scope(|s| {
243 let proof = items.derive(());
244
245 let group_size = items.len().div_ceil(MAX_GROUP_COUNT);
246 let mut groups = items.chunks_mut(group_size);
247
248 let Some(first_group) = groups.next() else { return };
249
250 for group in groups.rev() {
254 let group = proof.derive(group);
255 s.spawn(|_| {
256 let mut group = group;
257 for i in group.iter_mut() {
258 guard.run(|| for_each(i));
259 }
260 });
261 }
262
263 for i in first_group.iter_mut() {
265 guard.run(|| for_each(i));
266 }
267 });
268}
269
270pub fn par_for_each_in<T: Dss>(items: &(impl ParSlice<T> + ?Sized), for_each: impl Fn(&T) + Dss) {
271 let range = 0..items.len();
272 parallel_guard(|guard| {
273 if let Some(proof) = mode::check_dyn_thread_safe() {
274 par_range_internal(range, guard, proof, |idx| for_each(&items[idx]));
275 } else {
276 for idx in range {
277 guard.run(|| for_each(&items[idx]));
278 }
279 }
280 })
281}
282
283pub fn par_for_each_slice<T>(items: &mut [T], for_each: impl Fn(&mut T)) {
285 parallel_guard(|guard| {
286 items.iter_mut().for_each(|i| {
287 guard.run(|| for_each(i));
288 });
289 });
290}
291
292pub fn try_par_for_each_in<T: Dss, E: DynSend>(
297 items: &(impl ParSlice<T> + ?Sized),
298 for_each: impl Fn(&T) -> Result<(), E> + Dss,
299) -> Result<(), E> {
300 let range = 0..items.len();
301 parallel_guard(|guard| {
302 if let Some(proof) = mode::check_dyn_thread_safe() {
303 let error = Mutex::new(None);
304
305 par_range_internal(range, guard, proof, |idx| {
306 if let Err(err) = for_each(&items[idx]) {
307 *error.lock() = Some(err);
308 }
309 });
310
311 if let Some(err) = error.into_inner() { Err(err) } else { Ok(()) }
312 } else {
313 range
314 .into_iter()
315 .filter_map(|idx| guard.run(|| for_each(&items[idx])))
316 .fold(Ok(()), Result::and)
317 }
318 })
319}
320
321pub fn par_map<I: DynSend, T: IntoIterator<Item = I>, R: DynSend, C: FromIterator<R>>(
322 t: T,
323 map: impl Fn(I) -> R + Dss,
324) -> C {
325 parallel_guard(|guard| {
326 if let Some(proof) = mode::check_dyn_thread_safe() {
327 let map = proof.derive(map);
328
329 let mut items: Vec<(Option<I>, Option<R>)> =
330 t.into_iter().map(|i| (Some(i), None)).collect();
331
332 par_slice_mut(
333 &mut items,
334 guard,
335 |i| {
336 i.1 = Some(map(i.0.take().unwrap()));
337 },
338 proof,
339 );
340
341 items.into_iter().filter_map(|i| i.1).collect()
342 } else {
343 t.into_iter().filter_map(|i| guard.run(|| map(i))).collect()
344 }
345 })
346}
347
348pub fn broadcast<R: DynSend>(op: impl Fn(usize) -> R + DynSync) -> Vec<R> {
349 if let Some(proof) = mode::check_dyn_thread_safe() {
350 let op = proof.derive(op);
351 let results = rustc_thread_pool::broadcast(|context| op.derive(op(context.index())));
352 results.into_iter().map(|r| r.into_inner()).collect()
353 } else {
354 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[op(0)]))vec![op(0)]
355 }
356}