diff options
Diffstat (limited to 'vendor/rayon/src/iter/plumbing')
-rw-r--r-- | vendor/rayon/src/iter/plumbing/README.md | 315 | ||||
-rw-r--r-- | vendor/rayon/src/iter/plumbing/mod.rs | 484 |
2 files changed, 0 insertions, 799 deletions
diff --git a/vendor/rayon/src/iter/plumbing/README.md b/vendor/rayon/src/iter/plumbing/README.md deleted file mode 100644 index 42d22ef..0000000 --- a/vendor/rayon/src/iter/plumbing/README.md +++ /dev/null @@ -1,315 +0,0 @@ -# Parallel Iterators - -These are some notes on the design of the parallel iterator traits. -This file does not describe how to **use** parallel iterators. - -## The challenge - -Parallel iterators are more complicated than sequential iterators. -The reason is that they have to be able to split themselves up and -operate in parallel across the two halves. - -The current design for parallel iterators has two distinct modes in -which they can be used; as we will see, not all iterators support both -modes (which is why there are two): - -- **Pull mode** (the `Producer` and `UnindexedProducer` traits): in this mode, - the iterator is asked to produce the next item using a call to `next`. This - is basically like a normal iterator, but with a twist: you can split the - iterator in half to produce disjoint items in separate threads. - - in the `Producer` trait, splitting is done with `split_at`, which accepts - an index where the split should be performed. Only indexed iterators can - work in this mode, as they know exactly how much data they will produce, - and how to locate the requested index. - - in the `UnindexedProducer` trait, splitting is done with `split`, which - simply requests that the producer divide itself *approximately* in half. - This is useful when the exact length and/or layout is unknown, as with - `String` characters, or when the length might exceed `usize`, as with - `Range<u64>` on 32-bit platforms. - - In theory, any `Producer` could act unindexed, but we don't currently - use that possibility. When you know the exact length, a `split` can - simply be implemented as `split_at(length/2)`. -- **Push mode** (the `Consumer` and `UnindexedConsumer` traits): in - this mode, the iterator instead is *given* each item in turn, which - is then processed. This is the opposite of a normal iterator. It's - more like a `for_each` call: each time a new item is produced, the - `consume` method is called with that item. (The traits themselves are - a bit more complex, as they support state that can be threaded - through and ultimately reduced.) Like producers, there are two - variants of consumers which differ in how the split is performed: - - in the `Consumer` trait, splitting is done with `split_at`, which - accepts an index where the split should be performed. All - iterators can work in this mode. The resulting halves thus have an - idea about how much data they expect to consume. - - in the `UnindexedConsumer` trait, splitting is done with - `split_off_left`. There is no index: the resulting halves must be - prepared to process any amount of data, and they don't know where that - data falls in the overall stream. - - Not all consumers can operate in this mode. It works for - `for_each` and `reduce`, for example, but it does not work for - `collect_into_vec`, since in that case the position of each item is - important for knowing where it ends up in the target collection. - -## How iterator execution proceeds - -We'll walk through this example iterator chain to start. This chain -demonstrates more-or-less the full complexity of what can happen. - -```rust -vec1.par_iter() - .zip(vec2.par_iter()) - .flat_map(some_function) - .for_each(some_other_function) -``` - -To handle an iterator chain, we start by creating consumers. This -works from the end. So in this case, the call to `for_each` is the -final step, so it will create a `ForEachConsumer` that, given an item, -just calls `some_other_function` with that item. (`ForEachConsumer` is -a very simple consumer because it doesn't need to thread any state -between items at all.) - -Now, the `for_each` call will pass this consumer to the base iterator, -which is the `flat_map`. It will do this by calling the `drive_unindexed` -method on the `ParallelIterator` trait. `drive_unindexed` basically -says "produce items for this iterator and feed them to this consumer"; -it only works for unindexed consumers. - -(As an aside, it is interesting that only some consumers can work in -unindexed mode, but all producers can *drive* an unindexed consumer. -In contrast, only some producers can drive an *indexed* consumer, but -all consumers can be supplied indexes. Isn't variance neat.) - -As it happens, `FlatMap` only works with unindexed consumers anyway. -This is because flat-map basically has no idea how many items it will -produce. If you ask flat-map to produce the 22nd item, it can't do it, -at least not without some intermediate state. It doesn't know whether -processing the first item will create 1 item, 3 items, or 100; -therefore, to produce an arbitrary item, it would basically just have -to start at the beginning and execute sequentially, which is not what -we want. But for unindexed consumers, this doesn't matter, since they -don't need to know how much data they will get. - -Therefore, `FlatMap` can wrap the `ForEachConsumer` with a -`FlatMapConsumer` that feeds to it. This `FlatMapConsumer` will be -given one item. It will then invoke `some_function` to get a parallel -iterator out. It will then ask this new parallel iterator to drive the -`ForEachConsumer`. The `drive_unindexed` method on `flat_map` can then -pass the `FlatMapConsumer` up the chain to the previous item, which is -`zip`. At this point, something interesting happens. - -## Switching from push to pull mode - -If you think about `zip`, it can't really be implemented as a -consumer, at least not without an intermediate thread and some -channels or something (or maybe coroutines). The problem is that it -has to walk two iterators *in lockstep*. Basically, it can't call two -`drive` methods simultaneously, it can only call one at a time. So at -this point, the `zip` iterator needs to switch from *push mode* into -*pull mode*. - -You'll note that `Zip` is only usable if its inputs implement -`IndexedParallelIterator`, meaning that they can produce data starting -at random points in the stream. This need to switch to push mode is -exactly why. If we want to split a zip iterator at position 22, we -need to be able to start zipping items from index 22 right away, -without having to start from index 0. - -Anyway, so at this point, the `drive_unindexed` method for `Zip` stops -creating consumers. Instead, it creates a *producer*, a `ZipProducer`, -to be exact, and calls the `bridge` function in the `internals` -module. Creating a `ZipProducer` will in turn create producers for -the two iterators being zipped. This is possible because they both -implement `IndexedParallelIterator`. - -The `bridge` function will then connect the consumer, which is -handling the `flat_map` and `for_each`, with the producer, which is -handling the `zip` and its predecessors. It will split down until the -chunks seem reasonably small, then pull items from the producer and -feed them to the consumer. - -## The base case - -The other time that `bridge` gets used is when we bottom out in an -indexed producer, such as a slice or range. There is also a -`bridge_unindexed` equivalent for - you guessed it - unindexed producers, -such as string characters. - -<a name="producer-callback"> - -## What on earth is `ProducerCallback`? - -We saw that when you call a parallel action method like -`par_iter.reduce()`, that will create a "reducing" consumer and then -invoke `par_iter.drive_unindexed()` (or `par_iter.drive()`) as -appropriate. This may create yet more consumers as we proceed up the -parallel iterator chain. But at some point we're going to get to the -start of the chain, or to a parallel iterator (like `zip()`) that has -to coordinate multiple inputs. At that point, we need to start -converting parallel iterators into producers. - -The way we do this is by invoking the method `with_producer()`, defined on -`IndexedParallelIterator`. This is a callback scheme. In an ideal world, -it would work like this: - -```rust -base_iter.with_producer(|base_producer| { - // here, `base_producer` is the producer for `base_iter` -}); -``` - -In that case, we could implement a combinator like `map()` by getting -the producer for the base iterator, wrapping it to make our own -`MapProducer`, and then passing that to the callback. Something like -this: - -```rust -struct MapProducer<'f, P, F: 'f> { - base: P, - map_op: &'f F, -} - -impl<I, F> IndexedParallelIterator for Map<I, F> - where I: IndexedParallelIterator, - F: MapOp<I::Item>, -{ - fn with_producer<CB>(self, callback: CB) -> CB::Output { - let map_op = &self.map_op; - self.base_iter.with_producer(|base_producer| { - // Here `producer` is the producer for `self.base_iter`. - // Wrap that to make a `MapProducer` - let map_producer = MapProducer { - base: base_producer, - map_op: map_op - }; - - // invoke the callback with the wrapped version - callback(map_producer) - }); - } -}); -``` - -This example demonstrates some of the power of the callback scheme. -It winds up being a very flexible setup. For one thing, it means we -can take ownership of `par_iter`; we can then in turn give ownership -away of its bits and pieces into the producer (this is very useful if -the iterator owns an `&mut` slice, for example), or create shared -references and put *those* in the producer. In the case of map, for -example, the parallel iterator owns the `map_op`, and we borrow -references to it which we then put into the `MapProducer` (this means -the `MapProducer` can easily split itself and share those references). -The `with_producer` method can also create resources that are needed -during the parallel execution, since the producer does not have to be -returned. - -Unfortunately there is a catch. We can't actually use closures the way -I showed you. To see why, think about the type that `map_producer` -would have to have. If we were going to write the `with_producer` -method using a closure, it would have to look something like this: - -```rust -pub trait IndexedParallelIterator: ParallelIterator { - type Producer; - fn with_producer<CB, R>(self, callback: CB) -> R - where CB: FnOnce(Self::Producer) -> R; - ... -} -``` - -Note that we had to add this associated type `Producer` so that -we could specify the argument of the callback to be `Self::Producer`. -Now, imagine trying to write that `MapProducer` impl using this style: - -```rust -impl<I, F> IndexedParallelIterator for Map<I, F> - where I: IndexedParallelIterator, - F: MapOp<I::Item>, -{ - type MapProducer = MapProducer<'f, P::Producer, F>; - // ^^ wait, what is this `'f`? - - fn with_producer<CB, R>(self, callback: CB) -> R - where CB: FnOnce(Self::Producer) -> R - { - let map_op = &self.map_op; - // ^^^^^^ `'f` is (conceptually) the lifetime of this reference, - // so it will be different for each call to `with_producer`! - } -} -``` - -This may look familiar to you: it's the same problem that we have -trying to define an `Iterable` trait. Basically, the producer type -needs to include a lifetime (here, `'f`) that refers to the body of -`with_producer` and hence is not in scope at the impl level. - -If we had [associated type constructors][1598], we could solve this -problem that way. But there is another solution. We can use a -dedicated callback trait like `ProducerCallback`, instead of `FnOnce`: - -[1598]: https://github.com/rust-lang/rfcs/pull/1598 - -```rust -pub trait ProducerCallback<T> { - type Output; - fn callback<P>(self, producer: P) -> Self::Output - where P: Producer<Item=T>; -} -``` - -Using this trait, the signature of `with_producer()` looks like this: - -```rust -fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output; -``` - -Notice that this signature **never has to name the producer type** -- -there is no associated type `Producer` anymore. This is because the -`callback()` method is generically over **all** producers `P`. - -The problem is that now the `||` sugar doesn't work anymore. So we -have to manually create the callback struct, which is a mite tedious. -So our `MapProducer` code looks like this: - -```rust -impl<I, F> IndexedParallelIterator for Map<I, F> - where I: IndexedParallelIterator, - F: MapOp<I::Item>, -{ - fn with_producer<CB>(self, callback: CB) -> CB::Output - where CB: ProducerCallback<Self::Item> - { - return self.base.with_producer(Callback { callback: callback, map_op: self.map_op }); - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // Manual version of the closure sugar: create an instance - // of a struct that implements `ProducerCallback`. - - // The struct declaration. Each field is something that need to capture from the - // creating scope. - struct Callback<CB, F> { - callback: CB, - map_op: F, - } - - // Implement the `ProducerCallback` trait. This is pure boilerplate. - impl<T, F, CB> ProducerCallback<T> for Callback<CB, F> - where F: MapOp<T>, - CB: ProducerCallback<F::Output> - { - type Output = CB::Output; - - fn callback<P>(self, base: P) -> CB::Output - where P: Producer<Item=T> - { - // The body of the closure is here: - let producer = MapProducer { base: base, - map_op: &self.map_op }; - self.callback.callback(producer) - } - } - } -} -``` - -OK, a bit tedious, but it works! diff --git a/vendor/rayon/src/iter/plumbing/mod.rs b/vendor/rayon/src/iter/plumbing/mod.rs deleted file mode 100644 index 71d4fb4..0000000 --- a/vendor/rayon/src/iter/plumbing/mod.rs +++ /dev/null @@ -1,484 +0,0 @@ -//! Traits and functions used to implement parallel iteration. These are -//! low-level details -- users of parallel iterators should not need to -//! interact with them directly. See [the `plumbing` README][r] for a general overview. -//! -//! [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md - -use crate::join_context; - -use super::IndexedParallelIterator; - -use std::cmp; -use std::usize; - -/// The `ProducerCallback` trait is a kind of generic closure, -/// [analogous to `FnOnce`][FnOnce]. See [the corresponding section in -/// the plumbing README][r] for more details. -/// -/// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback -/// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html -pub trait ProducerCallback<T> { - /// The type of value returned by this callback. Analogous to - /// [`Output` from the `FnOnce` trait][Output]. - /// - /// [Output]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html#associatedtype.Output - type Output; - - /// Invokes the callback with the given producer as argument. The - /// key point of this trait is that this method is generic over - /// `P`, and hence implementors must be defined for any producer. - fn callback<P>(self, producer: P) -> Self::Output - where - P: Producer<Item = T>; -} - -/// A `Producer` is effectively a "splittable `IntoIterator`". That -/// is, a producer is a value which can be converted into an iterator -/// at any time: at that point, it simply produces items on demand, -/// like any iterator. But what makes a `Producer` special is that, -/// *before* we convert to an iterator, we can also **split** it at a -/// particular point using the `split_at` method. This will yield up -/// two producers, one producing the items before that point, and one -/// producing the items after that point (these two producers can then -/// independently be split further, or be converted into iterators). -/// In Rayon, this splitting is used to divide between threads. -/// See [the `plumbing` README][r] for further details. -/// -/// Note that each producer will always produce a fixed number of -/// items N. However, this number N is not queryable through the API; -/// the consumer is expected to track it. -/// -/// NB. You might expect `Producer` to extend the `IntoIterator` -/// trait. However, [rust-lang/rust#20671][20671] prevents us from -/// declaring the DoubleEndedIterator and ExactSizeIterator -/// constraints on a required IntoIterator trait, so we inline -/// IntoIterator here until that issue is fixed. -/// -/// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md -/// [20671]: https://github.com/rust-lang/rust/issues/20671 -pub trait Producer: Send + Sized { - /// The type of item that will be produced by this producer once - /// it is converted into an iterator. - type Item; - - /// The type of iterator we will become. - type IntoIter: Iterator<Item = Self::Item> + DoubleEndedIterator + ExactSizeIterator; - - /// Convert `self` into an iterator; at this point, no more parallel splits - /// are possible. - fn into_iter(self) -> Self::IntoIter; - - /// The minimum number of items that we will process - /// sequentially. Defaults to 1, which means that we will split - /// all the way down to a single item. This can be raised higher - /// using the [`with_min_len`] method, which will force us to - /// create sequential tasks at a larger granularity. Note that - /// Rayon automatically normally attempts to adjust the size of - /// parallel splits to reduce overhead, so this should not be - /// needed. - /// - /// [`with_min_len`]: ../trait.IndexedParallelIterator.html#method.with_min_len - fn min_len(&self) -> usize { - 1 - } - - /// The maximum number of items that we will process - /// sequentially. Defaults to MAX, which means that we can choose - /// not to split at all. This can be lowered using the - /// [`with_max_len`] method, which will force us to create more - /// parallel tasks. Note that Rayon automatically normally - /// attempts to adjust the size of parallel splits to reduce - /// overhead, so this should not be needed. - /// - /// [`with_max_len`]: ../trait.IndexedParallelIterator.html#method.with_max_len - fn max_len(&self) -> usize { - usize::MAX - } - - /// Split into two producers; one produces items `0..index`, the - /// other `index..N`. Index must be less than or equal to `N`. - fn split_at(self, index: usize) -> (Self, Self); - - /// Iterate the producer, feeding each element to `folder`, and - /// stop when the folder is full (or all elements have been consumed). - /// - /// The provided implementation is sufficient for most iterables. - fn fold_with<F>(self, folder: F) -> F - where - F: Folder<Self::Item>, - { - folder.consume_iter(self.into_iter()) - } -} - -/// A consumer is effectively a [generalized "fold" operation][fold], -/// and in fact each consumer will eventually be converted into a -/// [`Folder`]. What makes a consumer special is that, like a -/// [`Producer`], it can be **split** into multiple consumers using -/// the `split_at` method. When a consumer is split, it produces two -/// consumers, as well as a **reducer**. The two consumers can be fed -/// items independently, and when they are done the reducer is used to -/// combine their two results into one. See [the `plumbing` -/// README][r] for further details. -/// -/// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md -/// [fold]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold -/// [`Folder`]: trait.Folder.html -/// [`Producer`]: trait.Producer.html -pub trait Consumer<Item>: Send + Sized { - /// The type of folder that this consumer can be converted into. - type Folder: Folder<Item, Result = Self::Result>; - - /// The type of reducer that is produced if this consumer is split. - type Reducer: Reducer<Self::Result>; - - /// The type of result that this consumer will ultimately produce. - type Result: Send; - - /// Divide the consumer into two consumers, one processing items - /// `0..index` and one processing items from `index..`. Also - /// produces a reducer that can be used to reduce the results at - /// the end. - fn split_at(self, index: usize) -> (Self, Self, Self::Reducer); - - /// Convert the consumer into a folder that can consume items - /// sequentially, eventually producing a final result. - fn into_folder(self) -> Self::Folder; - - /// Hint whether this `Consumer` would like to stop processing - /// further items, e.g. if a search has been completed. - fn full(&self) -> bool; -} - -/// The `Folder` trait encapsulates [the standard fold -/// operation][fold]. It can be fed many items using the `consume` -/// method. At the end, once all items have been consumed, it can then -/// be converted (using `complete`) into a final value. -/// -/// [fold]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold -pub trait Folder<Item>: Sized { - /// The type of result that will ultimately be produced by the folder. - type Result; - - /// Consume next item and return new sequential state. - fn consume(self, item: Item) -> Self; - - /// Consume items from the iterator until full, and return new sequential state. - /// - /// This method is **optional**. The default simply iterates over - /// `iter`, invoking `consume` and checking after each iteration - /// whether `full` returns false. - /// - /// The main reason to override it is if you can provide a more - /// specialized, efficient implementation. - fn consume_iter<I>(mut self, iter: I) -> Self - where - I: IntoIterator<Item = Item>, - { - for item in iter { - self = self.consume(item); - if self.full() { - break; - } - } - self - } - - /// Finish consuming items, produce final result. - fn complete(self) -> Self::Result; - - /// Hint whether this `Folder` would like to stop processing - /// further items, e.g. if a search has been completed. - fn full(&self) -> bool; -} - -/// The reducer is the final step of a `Consumer` -- after a consumer -/// has been split into two parts, and each of those parts has been -/// fully processed, we are left with two results. The reducer is then -/// used to combine those two results into one. See [the `plumbing` -/// README][r] for further details. -/// -/// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md -pub trait Reducer<Result> { - /// Reduce two final results into one; this is executed after a - /// split. - fn reduce(self, left: Result, right: Result) -> Result; -} - -/// A stateless consumer can be freely copied. These consumers can be -/// used like regular consumers, but they also support a -/// `split_off_left` method that does not take an index to split, but -/// simply splits at some arbitrary point (`for_each`, for example, -/// produces an unindexed consumer). -pub trait UnindexedConsumer<I>: Consumer<I> { - /// Splits off a "left" consumer and returns it. The `self` - /// consumer should then be used to consume the "right" portion of - /// the data. (The ordering matters for methods like find_first -- - /// values produced by the returned value are given precedence - /// over values produced by `self`.) Once the left and right - /// halves have been fully consumed, you should reduce the results - /// with the result of `to_reducer`. - fn split_off_left(&self) -> Self; - - /// Creates a reducer that can be used to combine the results from - /// a split consumer. - fn to_reducer(&self) -> Self::Reducer; -} - -/// A variant on `Producer` which does not know its exact length or -/// cannot represent it in a `usize`. These producers act like -/// ordinary producers except that they cannot be told to split at a -/// particular point. Instead, you just ask them to split 'somewhere'. -/// -/// (In principle, `Producer` could extend this trait; however, it -/// does not because to do so would require producers to carry their -/// own length with them.) -pub trait UnindexedProducer: Send + Sized { - /// The type of item returned by this producer. - type Item; - - /// Split midway into a new producer if possible, otherwise return `None`. - fn split(self) -> (Self, Option<Self>); - - /// Iterate the producer, feeding each element to `folder`, and - /// stop when the folder is full (or all elements have been consumed). - fn fold_with<F>(self, folder: F) -> F - where - F: Folder<Self::Item>; -} - -/// A splitter controls the policy for splitting into smaller work items. -/// -/// Thief-splitting is an adaptive policy that starts by splitting into -/// enough jobs for every worker thread, and then resets itself whenever a -/// job is actually stolen into a different thread. -#[derive(Clone, Copy)] -struct Splitter { - /// The `splits` tell us approximately how many remaining times we'd - /// like to split this job. We always just divide it by two though, so - /// the effective number of pieces will be `next_power_of_two()`. - splits: usize, -} - -impl Splitter { - #[inline] - fn new() -> Splitter { - Splitter { - splits: crate::current_num_threads(), - } - } - - #[inline] - fn try_split(&mut self, stolen: bool) -> bool { - let Splitter { splits } = *self; - - if stolen { - // This job was stolen! Reset the number of desired splits to the - // thread count, if that's more than we had remaining anyway. - self.splits = cmp::max(crate::current_num_threads(), self.splits / 2); - true - } else if splits > 0 { - // We have splits remaining, make it so. - self.splits /= 2; - true - } else { - // Not stolen, and no more splits -- we're done! - false - } - } -} - -/// The length splitter is built on thief-splitting, but additionally takes -/// into account the remaining length of the iterator. -#[derive(Clone, Copy)] -struct LengthSplitter { - inner: Splitter, - - /// The smallest we're willing to divide into. Usually this is just 1, - /// but you can choose a larger working size with `with_min_len()`. - min: usize, -} - -impl LengthSplitter { - /// Creates a new splitter based on lengths. - /// - /// The `min` is a hard lower bound. We'll never split below that, but - /// of course an iterator might start out smaller already. - /// - /// The `max` is an upper bound on the working size, used to determine - /// the minimum number of times we need to split to get under that limit. - /// The adaptive algorithm may very well split even further, but never - /// smaller than the `min`. - #[inline] - fn new(min: usize, max: usize, len: usize) -> LengthSplitter { - let mut splitter = LengthSplitter { - inner: Splitter::new(), - min: cmp::max(min, 1), - }; - - // Divide the given length by the max working length to get the minimum - // number of splits we need to get under that max. This rounds down, - // but the splitter actually gives `next_power_of_two()` pieces anyway. - // e.g. len 12345 / max 100 = 123 min_splits -> 128 pieces. - let min_splits = len / cmp::max(max, 1); - - // Only update the value if it's not splitting enough already. - if min_splits > splitter.inner.splits { - splitter.inner.splits = min_splits; - } - - splitter - } - - #[inline] - fn try_split(&mut self, len: usize, stolen: bool) -> bool { - // If splitting wouldn't make us too small, try the inner splitter. - len / 2 >= self.min && self.inner.try_split(stolen) - } -} - -/// This helper function is used to "connect" a parallel iterator to a -/// consumer. It will convert the `par_iter` into a producer P and -/// then pull items from P and feed them to `consumer`, splitting and -/// creating parallel threads as needed. -/// -/// This is useful when you are implementing your own parallel -/// iterators: it is often used as the definition of the -/// [`drive_unindexed`] or [`drive`] methods. -/// -/// [`drive_unindexed`]: ../trait.ParallelIterator.html#tymethod.drive_unindexed -/// [`drive`]: ../trait.IndexedParallelIterator.html#tymethod.drive -pub fn bridge<I, C>(par_iter: I, consumer: C) -> C::Result -where - I: IndexedParallelIterator, - C: Consumer<I::Item>, -{ - let len = par_iter.len(); - return par_iter.with_producer(Callback { len, consumer }); - - struct Callback<C> { - len: usize, - consumer: C, - } - - impl<C, I> ProducerCallback<I> for Callback<C> - where - C: Consumer<I>, - { - type Output = C::Result; - fn callback<P>(self, producer: P) -> C::Result - where - P: Producer<Item = I>, - { - bridge_producer_consumer(self.len, producer, self.consumer) - } - } -} - -/// This helper function is used to "connect" a producer and a -/// consumer. You may prefer to call [`bridge`], which wraps this -/// function. This function will draw items from `producer` and feed -/// them to `consumer`, splitting and creating parallel tasks when -/// needed. -/// -/// This is useful when you are implementing your own parallel -/// iterators: it is often used as the definition of the -/// [`drive_unindexed`] or [`drive`] methods. -/// -/// [`bridge`]: fn.bridge.html -/// [`drive_unindexed`]: ../trait.ParallelIterator.html#tymethod.drive_unindexed -/// [`drive`]: ../trait.IndexedParallelIterator.html#tymethod.drive -pub fn bridge_producer_consumer<P, C>(len: usize, producer: P, consumer: C) -> C::Result -where - P: Producer, - C: Consumer<P::Item>, -{ - let splitter = LengthSplitter::new(producer.min_len(), producer.max_len(), len); - return helper(len, false, splitter, producer, consumer); - - fn helper<P, C>( - len: usize, - migrated: bool, - mut splitter: LengthSplitter, - producer: P, - consumer: C, - ) -> C::Result - where - P: Producer, - C: Consumer<P::Item>, - { - if consumer.full() { - consumer.into_folder().complete() - } else if splitter.try_split(len, migrated) { - let mid = len / 2; - let (left_producer, right_producer) = producer.split_at(mid); - let (left_consumer, right_consumer, reducer) = consumer.split_at(mid); - let (left_result, right_result) = join_context( - |context| { - helper( - mid, - context.migrated(), - splitter, - left_producer, - left_consumer, - ) - }, - |context| { - helper( - len - mid, - context.migrated(), - splitter, - right_producer, - right_consumer, - ) - }, - ); - reducer.reduce(left_result, right_result) - } else { - producer.fold_with(consumer.into_folder()).complete() - } - } -} - -/// A variant of [`bridge_producer_consumer`] where the producer is an unindexed producer. -/// -/// [`bridge_producer_consumer`]: fn.bridge_producer_consumer.html -pub fn bridge_unindexed<P, C>(producer: P, consumer: C) -> C::Result -where - P: UnindexedProducer, - C: UnindexedConsumer<P::Item>, -{ - let splitter = Splitter::new(); - bridge_unindexed_producer_consumer(false, splitter, producer, consumer) -} - -fn bridge_unindexed_producer_consumer<P, C>( - migrated: bool, - mut splitter: Splitter, - producer: P, - consumer: C, -) -> C::Result -where - P: UnindexedProducer, - C: UnindexedConsumer<P::Item>, -{ - if consumer.full() { - consumer.into_folder().complete() - } else if splitter.try_split(migrated) { - match producer.split() { - (left_producer, Some(right_producer)) => { - let (reducer, left_consumer, right_consumer) = - (consumer.to_reducer(), consumer.split_off_left(), consumer); - let bridge = bridge_unindexed_producer_consumer; - let (left_result, right_result) = join_context( - |context| bridge(context.migrated(), splitter, left_producer, left_consumer), - |context| bridge(context.migrated(), splitter, right_producer, right_consumer), - ); - reducer.reduce(left_result, right_result) - } - (producer, None) => producer.fold_with(consumer.into_folder()).complete(), - } - } else { - producer.fold_with(consumer.into_folder()).complete() - } -} |