par_core/lib.rs
1//! # par-core
2//!
3//! A wrapper for various parallelization library for Rust.
4//! This crate currently supports
5//!
6//! - [`chili`](https://github.com/dragostis/chili)
7//! - [`rayon`](https://github.com/rayon-rs/rayon)
8//! - Disable parallelization.
9//!
10//! # Usage
11//!
12//! If you are developing a library, you should not force the parallelization
13//! library, and let the users choose the parallelization library.
14//!
15//! ## Final application
16//!
17//! If you are developing a final application, you can use cargo feature to
18//! select the parallelization library.
19//!
20//! ### `chili`
21//!
22//! ```toml
23//! [dependencies]
24//! par-core = { version = "1.0.1", features = ["chili"] }
25//! ```
26//!
27//! ### `rayon`
28//!
29//! ```toml
30//! [dependencies]
31//! par-core = { version = "1.0.1", features = ["rayon"] }
32//! ```
33//!
34//! ### Disable parallelization
35//!
36//! ```toml
37//! [dependencies]
38//! par-core = { version = "1.0.1", default-features = false }
39//! ```
40//!
41//! ## Library developers
42//!
43//! If you are developing a library, you can simply depend on `par-core` without
44//! any features. **Note**: To prevent a small mistake of end-user making the
45//! appplication slower, `par-core` emits a error message using a default
46//! feature. So if you are a library developer, you should specify
47//! `default-features = false`.
48//!
49//! ```toml
50//! [dependencies]
51//! par-core = { version = "1.0.1", default-features = false }
52//! ```
53
54#[cfg(all(not(feature = "chili"), not(feature = "rayon"), feature = "parallel"))]
55compile_error!("You must enable `chili` or `rayon` feature if you want to use `parallel` feature");
56
57#[cfg(all(feature = "chili", feature = "rayon"))]
58compile_error!("You must enable `chili` or `rayon` feature, not both");
59
60#[cfg(feature = "chili")]
61mod par_chili {
62 use std::{cell::RefCell, mem::transmute};
63
64 use chili::Scope;
65
66 thread_local! {
67 static SCOPE: RefCell<Option<&'static mut Scope<'static>>> = Default::default();
68 }
69
70 #[inline]
71 fn join_scoped<A, B, RA, RB>(scope: &mut Scope<'_>, oper_a: A, oper_b: B) -> (RA, RB)
72 where
73 A: Send + FnOnce() -> RA,
74 B: Send + FnOnce() -> RB,
75 RA: Send,
76 RB: Send,
77 {
78 scope.join(
79 |scope| {
80 let old_scope = SCOPE.take();
81 // SATETY: it will be only accessed during `oper_a`
82 SCOPE.set(Some(unsafe { transmute::<&mut Scope, &mut Scope>(scope) }));
83
84 let ra = oper_a();
85 SCOPE.set(old_scope);
86
87 ra
88 },
89 |scope| {
90 let old_scope = SCOPE.take();
91 // SATETY: it will be only accessed during `oper_b`
92 SCOPE.set(Some(unsafe { transmute::<&mut Scope, &mut Scope>(scope) }));
93
94 let rb = oper_b();
95 SCOPE.set(old_scope);
96
97 rb
98 },
99 )
100 }
101
102 #[inline]
103 pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
104 where
105 A: Send + FnOnce() -> RA,
106 B: Send + FnOnce() -> RB,
107 RA: Send,
108 RB: Send,
109 {
110 let old_scope: Option<&mut Scope<'_>> = SCOPE.take();
111 match old_scope {
112 Some(scope) => {
113 let (ra, rb) = join_scoped(scope, oper_a, oper_b);
114 SCOPE.set(Some(scope));
115 (ra, rb)
116 }
117 None => join_scoped(&mut Scope::global(), oper_a, oper_b),
118 }
119 }
120}
121
122pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
123where
124 A: Send + FnOnce() -> RA,
125 B: Send + FnOnce() -> RB,
126 RA: Send,
127 RB: Send,
128{
129 #[cfg(feature = "chili")]
130 let (ra, rb) = par_chili::join(oper_a, oper_b);
131
132 #[cfg(feature = "rayon")]
133 let (ra, rb) = rayon::join(oper_a, oper_b);
134
135 #[cfg(not(feature = "parallel"))]
136 let (ra, rb) = (oper_a(), oper_b());
137
138 (ra, rb)
139}