1 //! The multi-threading abstractions used by `Hasher::update_with_join`.
2 //!
3 //! Different implementations of the `Join` trait determine whether
4 //! `Hasher::update_with_join` performs multi-threading on sufficiently large
5 //! inputs. The `SerialJoin` implementation is single-threaded, and the
6 //! `RayonJoin` implementation (gated by the `rayon` feature) is multi-threaded.
7 //! Interfaces other than `Hasher::update_with_join`, like [`hash`](crate::hash)
8 //! and [`Hasher::update`](crate::Hasher::update), always use `SerialJoin`
9 //! internally.
10 //!
11 //! The `Join` trait is an almost exact copy of the [`rayon::join`] API, and
12 //! `RayonJoin` is the only non-trivial implementation. Previously this trait
13 //! was public, but currently it's been re-privatized, as it's both 1) of no
14 //! value to most callers and 2) a pretty big implementation detail to commit
15 //! to.
16 //!
17 //! [`rayon::join`]: https://docs.rs/rayon/1.3.0/rayon/fn.join.html
18 
19 /// The trait that abstracts over single-threaded and multi-threaded recursion.
20 ///
21 /// See the [`join` module docs](index.html) for more details.
22 pub trait Join {
join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB) where A: FnOnce() -> RA + Send, B: FnOnce() -> RB + Send, RA: Send, RB: Send23     fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
24     where
25         A: FnOnce() -> RA + Send,
26         B: FnOnce() -> RB + Send,
27         RA: Send,
28         RB: Send;
29 }
30 
31 /// The trivial, serial implementation of `Join`. The left and right sides are
32 /// executed one after the other, on the calling thread. The standalone hashing
33 /// functions and the `Hasher::update` method use this implementation
34 /// internally.
35 ///
36 /// See the [`join` module docs](index.html) for more details.
37 pub enum SerialJoin {}
38 
39 impl Join for SerialJoin {
40     #[inline]
join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB) where A: FnOnce() -> RA + Send, B: FnOnce() -> RB + Send, RA: Send, RB: Send,41     fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
42     where
43         A: FnOnce() -> RA + Send,
44         B: FnOnce() -> RB + Send,
45         RA: Send,
46         RB: Send,
47     {
48         (oper_a(), oper_b())
49     }
50 }
51 
52 /// The Rayon-based implementation of `Join`. The left and right sides are
53 /// executed on the Rayon thread pool, potentially in parallel. This
54 /// implementation is gated by the `rayon` feature, which is off by default.
55 ///
56 /// See the [`join` module docs](index.html) for more details.
57 #[cfg(feature = "rayon")]
58 pub enum RayonJoin {}
59 
60 #[cfg(feature = "rayon")]
61 impl Join for RayonJoin {
62     #[inline]
join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB) where A: FnOnce() -> RA + Send, B: FnOnce() -> RB + Send, RA: Send, RB: Send,63     fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
64     where
65         A: FnOnce() -> RA + Send,
66         B: FnOnce() -> RB + Send,
67         RA: Send,
68         RB: Send,
69     {
70         rayon::join(oper_a, oper_b)
71     }
72 }
73 
74 #[cfg(test)]
75 mod test {
76     use super::*;
77 
78     #[test]
test_serial_join()79     fn test_serial_join() {
80         let oper_a = || 1 + 1;
81         let oper_b = || 2 + 2;
82         assert_eq!((2, 4), SerialJoin::join(oper_a, oper_b));
83     }
84 
85     #[test]
86     #[cfg(feature = "rayon")]
test_rayon_join()87     fn test_rayon_join() {
88         let oper_a = || 1 + 1;
89         let oper_b = || 2 + 2;
90         assert_eq!((2, 4), RayonJoin::join(oper_a, oper_b));
91     }
92 }
93