Skip to main content

p3_maybe_rayon/
lib.rs

1//! Thin compatibility layer that re-exports either `rayon` or a sequential
2//! fallback under a unified prelude, gated by the `parallel` feature.
3
4#![no_std]
5
6extern crate alloc;
7
8#[cfg(any(feature = "parallel", test))]
9extern crate std;
10
11/// Whether this crate uses the Rayon backend instead of the serial fallback.
12///
13/// This reflects feature unification across dependents and remains true for a
14/// one-worker Rayon pool, unlike a check of the current worker count.
15pub const PARALLEL_ENABLED: bool = cfg!(feature = "parallel");
16
17#[cfg(not(feature = "parallel"))]
18mod serial;
19
20pub mod task_size;
21
22pub mod prelude {
23    #[cfg(not(feature = "parallel"))]
24    pub use core::iter::{
25        ExactSizeIterator as IndexedParallelIterator, Iterator as ParallelIterator,
26    };
27    use core::marker::{Send, Sync};
28
29    #[cfg(feature = "parallel")]
30    pub use rayon::prelude::*;
31    #[cfg(feature = "parallel")]
32    pub use rayon::{current_num_threads, join};
33
34    #[cfg(not(feature = "parallel"))]
35    pub use super::serial::*;
36    pub use super::task_size::{TaskSizeExt, min_task_len, should_split};
37
38    pub trait SharedExt: ParallelIterator {
39        /// Folds each split of the iterator with `fold_op` (seeded by `identity`), then
40        /// combines the per-split accumulators with `reduce_op`.
41        ///
42        /// `identity()` must be neutral for `reduce_op`, and `fold_op`/`reduce_op` must be
43        /// mutually associative so that regrouping splits doesn't change the result — under
44        /// the `parallel` feature the split count (and thus how many times `reduce_op` runs)
45        /// depends on the thread pool. **The serial fallback never calls `reduce_op`** (there
46        /// is only one split), so an inconsistent `reduce_op` yields results that diverge
47        /// between `cargo test` and `cargo test --features parallel`.
48        fn par_fold_reduce<Acc, Id, F, R>(self, identity: Id, fold_op: F, reduce_op: R) -> Acc
49        where
50            Acc: Send,
51            Id: Fn() -> Acc + Sync + Send,
52            F: Fn(Acc, Self::Item) -> Acc + Sync + Send,
53            R: Fn(Acc, Acc) -> Acc + Sync + Send;
54    }
55
56    impl<I: ParallelIterator> SharedExt for I {
57        #[inline]
58        fn par_fold_reduce<Acc, Id, F, R>(self, identity: Id, fold_op: F, reduce_op: R) -> Acc
59        where
60            Acc: Send,
61            Id: Fn() -> Acc + Sync + Send,
62            F: Fn(Acc, Self::Item) -> Acc + Sync + Send,
63            R: Fn(Acc, Acc) -> Acc + Sync + Send,
64        {
65            #[cfg(feature = "parallel")]
66            {
67                self.fold(&identity, fold_op).reduce(&identity, reduce_op)
68            }
69
70            #[cfg(not(feature = "parallel"))]
71            {
72                let _ = reduce_op;
73                self.fold(identity(), fold_op)
74            }
75        }
76    }
77}
78
79pub mod iter {
80    #[cfg(not(feature = "parallel"))]
81    pub use core::iter::{repeat, repeat_n};
82
83    #[cfg(feature = "parallel")]
84    pub use rayon::iter::{repeat, repeat_n};
85}