blake3/
join.rs

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.
22pub trait Join {
23    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.
37pub enum SerialJoin {}
38
39impl Join for SerialJoin {
40    #[inline]
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")]
58pub enum RayonJoin {}
59
60#[cfg(feature = "rayon")]
61impl Join for RayonJoin {
62    #[inline]
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_core::join(oper_a, oper_b)
71    }
72}
73
74#[cfg(test)]
75mod test {
76    use super::*;
77
78    #[test]
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")]
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}