1use crate::domain::{Domain, DomainAdmission, DomainBusy, LocalMode};
2use crate::lanes::{map_in_lanes, LaneControl, Lanes, LanesError, MapInError};
3use crate::map::{map, MapError};
4use std::any::Any;
5use std::convert::Infallible;
6use std::fmt;
7use std::panic::AssertUnwindSafe;
8use std::sync::{Mutex, PoisonError};
9
10pub(crate) fn in_rayon_worker_context() -> bool {
11 rayon::current_thread_index().is_some()
12}
13
14fn run_sequential_in_pool<T, U, E, F>(
20 pool: &rayon::ThreadPool,
21 items: Vec<T>,
22 f: F,
23) -> Result<Vec<U>, MapError>
24where
25 T: Send,
26 U: Send,
27 E: fmt::Display + Send,
28 F: Fn(T) -> Result<U, E> + Send + Sync,
29{
30 pool.install(|| map(items, f))
31}
32
33#[derive(Debug)]
35pub enum RayonLanesError {
36 MissingPool,
38 ForeignPool,
40 DomainBusy(DomainBusy),
42}
43
44impl fmt::Display for RayonLanesError {
45 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 Self::MissingPool => formatter.write_str("domain has no Rayon pool"),
48 Self::ForeignPool => formatter.write_str("lanes entered from a foreign Rayon pool"),
49 Self::DomainBusy(error) => error.fmt(formatter),
50 }
51 }
52}
53
54impl std::error::Error for RayonLanesError {
55 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
56 match self {
57 Self::DomainBusy(error) => Some(error),
58 Self::MissingPool | Self::ForeignPool => None,
59 }
60 }
61}
62
63impl From<RayonLanesError> for MapInError {
64 fn from(error: RayonLanesError) -> Self {
65 match error {
66 RayonLanesError::MissingPool => Self::MissingPool,
67 RayonLanesError::ForeignPool => Self::ForeignPool,
68 RayonLanesError::DomainBusy(error) => Self::DomainBusy(error),
69 }
70 }
71}
72
73pub struct RayonLanes<'d> {
105 pool: &'d rayon::ThreadPool,
106 worker_count: usize,
107 _admission: DomainAdmission<'d>,
109}
110
111impl<'d> RayonLanes<'d> {
112 pub fn new(domain: &'d Domain) -> Result<Self, RayonLanesError> {
120 let pool = domain.rayon_pool().ok_or(RayonLanesError::MissingPool)?;
121 if in_rayon_worker_context() && pool.current_thread_index().is_none() {
122 return Err(RayonLanesError::ForeignPool);
123 }
124 let admission = domain.try_admit().map_err(RayonLanesError::DomainBusy)?;
125 Ok(Self {
126 pool,
127 worker_count: domain.worker_count(),
128 _admission: admission,
129 })
130 }
131}
132
133enum LaneReport<E> {
135 Error(E),
136 Panic(Box<dyn Any + Send + 'static>),
137}
138
139fn record_lane_report<E>(report: &Mutex<Option<LaneReport<E>>>, value: LaneReport<E>) {
140 let mut report = report.lock().unwrap_or_else(PoisonError::into_inner);
141 let replace = matches!(
143 (&*report, &value),
144 (None, _) | (Some(LaneReport::Error(_)), LaneReport::Panic(_))
145 );
146 if replace {
147 *report = Some(value);
148 }
149}
150
151impl Lanes for RayonLanes<'_> {
152 type Lane<'l> = ();
153 type Error = Infallible;
154
155 fn lane_count(&self) -> usize {
156 self.worker_count
157 }
158
159 fn run<E: Send>(
160 &mut self,
161 lane: impl Fn(usize, &mut (), &LaneControl<'_>) -> Result<(), E> + Sync,
162 ) -> Result<(), LanesError<E, Self::Error>> {
163 let control = LaneControl::new();
164 let report: Mutex<Option<LaneReport<E>>> = Mutex::new(None);
165 self.pool.broadcast(|context| {
168 let outcome = std::panic::catch_unwind(AssertUnwindSafe(|| {
169 lane(context.index(), &mut (), &control)
170 }));
171 match outcome {
172 Ok(Ok(())) => {}
173 Ok(Err(error)) => {
174 control.cancel();
175 record_lane_report(&report, LaneReport::Error(error));
176 }
177 Err(payload) => {
178 control.cancel();
179 record_lane_report(&report, LaneReport::Panic(payload));
180 }
181 }
182 });
183 match report.into_inner().unwrap_or_else(PoisonError::into_inner) {
184 Some(LaneReport::Panic(payload)) => std::panic::resume_unwind(payload),
185 Some(LaneReport::Error(error)) => Err(LanesError::Lane(error)),
186 None => Ok(()),
187 }
188 }
189
190 fn run_with_coordinator<C, E: Send>(
191 &mut self,
192 coordinator: impl FnOnce(&LaneControl<'_>) -> Result<C, E>,
193 lane: impl Fn(usize, &mut (), &LaneControl<'_>) -> Result<(), E> + Sync,
194 ) -> Result<C, LanesError<E, Self::Error>> {
195 if self.pool.current_thread_index().is_some() {
196 return Err(LanesError::Reentrant);
199 }
200 let control = LaneControl::new();
201 let report: Mutex<Option<LaneReport<E>>> = Mutex::new(None);
202 let coordinator_outcome = self.pool.in_place_scope(|scope| {
205 let lane = &lane;
206 let control = &control;
207 let report = &report;
208 scope.spawn_broadcast(move |_scope, context| {
209 let outcome = std::panic::catch_unwind(AssertUnwindSafe(|| {
210 lane(context.index(), &mut (), control)
211 }));
212 match outcome {
213 Ok(Ok(())) => {}
214 Ok(Err(error)) => {
215 control.cancel();
216 record_lane_report(report, LaneReport::Error(error));
217 }
218 Err(payload) => {
219 control.cancel();
220 record_lane_report(report, LaneReport::Panic(payload));
221 }
222 }
223 });
224 let outcome = std::panic::catch_unwind(AssertUnwindSafe(|| coordinator(control)));
225 control.cancel();
227 outcome
228 });
229
230 let report = report.into_inner().unwrap_or_else(PoisonError::into_inner);
232 match coordinator_outcome {
233 Err(payload) => std::panic::resume_unwind(payload),
234 Ok(result) => match report {
235 Some(LaneReport::Panic(payload)) => std::panic::resume_unwind(payload),
236 Some(LaneReport::Error(error)) => Err(LanesError::Lane(error)),
237 None => result.map_err(LanesError::Coordinator),
238 },
239 }
240 }
241}
242
243pub fn map_in<T, U, E, F>(
283 domain: &Domain,
284 mode: LocalMode,
285 items: Vec<T>,
286 f: F,
287) -> Result<Vec<U>, MapInError>
288where
289 T: Send,
290 U: Send,
291 E: fmt::Display + Send,
292 F: Fn(T) -> Result<U, E> + Send + Sync,
293{
294 let mut lanes = RayonLanes::new(domain)?;
295 match mode {
296 LocalMode::Outer => map_in_lanes(&mut lanes, items, |item, ()| f(item)),
297 LocalMode::Sequential | LocalMode::Inner => {
298 let pool = domain.rayon_pool().ok_or(MapInError::MissingPool)?;
299 run_sequential_in_pool(pool, items, f).map_err(MapInError::Callback)
300 }
301 }
302}
303
304#[cfg(test)]
305mod tests;