paralight/iter/sink/mod.rs
1// Copyright 2026 The Paralight Authors
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Parallel sinks collect items from parallel iterators.
10
11#[cfg(feature = "nightly")]
12pub mod array;
13pub mod vec;
14
15/// A sink to collect items in parallel.
16///
17/// You most likely won't need to interact with this trait directly, as it is an
18/// associated type of the [`FromExactParallelSink`] trait, itself used to
19/// express output types of the
20/// [`collect()`](super::BaseExactParallelIterator::collect) and
21/// [`try_collect()`](super::BaseExactParallelIterator::try_collect) adaptors on
22/// [`BaseExactParallelIterator`](super::BaseExactParallelIterator). You can
23/// however implement this trait if you want to allow your types to be collected
24/// by these adaptors.
25pub trait ExactParallelSink {
26 /// The type of items that this parallel sink collects.
27 type Item: Send;
28
29 /// Set to [`false`] if the skip function is guaranteed to be a noop.
30 ///
31 /// Typically, skipping is a noop when [`std::mem::needs_drop()`] returns
32 /// false for the [`Item`](Self::Item) type.
33 const NEEDS_CLEANUP: bool;
34
35 /// Creates a new sink able to collect the given number of items.
36 fn new(len: usize) -> Self;
37
38 /// Pushes the given item at the given index of this sink.
39 ///
40 /// # Safety
41 ///
42 /// Given the length `len` passed to the [`new()`](Self::new) call that
43 /// initialized this sink:
44 /// - indices passed to [`push_item()`](Self::push_item) must be in the
45 /// `0..len` range,
46 /// - each index in `0..len` must be present at most once in all indices
47 /// passed to calls to [`push_item()`](Self::push_item) and ranges passed
48 /// to calls to [`skip_item_range()`](Self::skip_item_range).
49 ///
50 /// It is therefore undefined behavior to call this function twice with the
51 /// same index, with an index contained in a range for which
52 /// [`skip_item_range()`](Self::skip_item_range) was invoked, etc.
53 ///
54 /// You normally shouldn't have to worry about this, because this API is
55 /// intended to be called by Paralight's internal multi-threading engine.
56 /// This API is public to allow others to implement parallel sinks: when
57 /// implementing your own sink(s), you can rely on these `unsafe`
58 /// pre-conditions.
59 unsafe fn push_item(&self, index: usize, item: Self::Item);
60
61 /// Indicates that the items in the given range won't be pushed.
62 ///
63 /// # Safety
64 ///
65 /// Given the length `len` passed to the [`new()`](Self::new) call that
66 /// initialized this sink:
67 /// - ranges passed to [`skip_item_range()`](Self::skip_item_range) must be
68 /// included in the `0..len` range,
69 /// - each index in `0..len` must be present at most once in all indices
70 /// passed to calls to [`push_item()`](Self::push_item) and ranges passed
71 /// to calls to [`skip_item_range()`](Self::skip_item_range).
72 ///
73 /// It is therefore undefined behavior to call this function twice with the
74 /// same range, with overlapping ranges, with a range that contains an
75 /// index for which [`push_item()`](Self::push_item) was invoked, etc.
76 ///
77 /// You normally shouldn't have to worry about this, because this API is
78 /// intended to be called by Paralight's internal multi-threading engine.
79 /// This API is public to allow others to implement parallel sinks: when
80 /// implementing your own sink(s), you can rely on these `unsafe`
81 /// pre-conditions.
82 unsafe fn skip_item_range(&self, range: std::ops::Range<usize>);
83
84 /// Cancel and cleanup this sink.
85 ///
86 /// The purpose of this function is to properly cleanup the sink during
87 /// [unwinding](https://doc.rust-lang.org/nomicon/unwinding.html) if a panic
88 /// occured before all items have been pushed.
89 ///
90 /// # Safety
91 ///
92 /// This can only be called after all indices have been passed once and only
93 /// once to [`push_item()`](Self::push_item) and
94 /// [`skip_item_range()`](Self::skip_item_range).
95 unsafe fn cancel(self);
96}
97
98/// Trait for collecting items from an [`ExactParallelSink`].
99///
100/// You most likely won't need to interact with this trait directly, as it is
101/// only used to express output types of the
102/// [`collect()`](super::BaseExactParallelIterator::collect) and
103/// [`try_collect()`](super::BaseExactParallelIterator::try_collect) adaptors on
104/// [`BaseExactParallelIterator`](super::BaseExactParallelIterator). You can
105/// however implement this trait if you want to allow your types to be collected
106/// by these adaptors.
107pub trait FromExactParallelSink {
108 /// The type of items that this parallel sink collects.
109 type Item: Send;
110
111 /// Sink from which this type can be created.
112 type Sink: ExactParallelSink<Item = Self::Item> + Sync;
113
114 /// Converts a fully populated sink into this type.
115 ///
116 /// # Safety
117 ///
118 /// - This can only be called after all indices have been passed once and
119 /// only once to the sink's [`push_item()`](ExactParallelSink::push_item).
120 /// - No call to [`skip_item_range()`](ExactParallelSink::skip_item_range)
121 /// must have been made on the sink (otherwise, you need to call
122 /// [`cancel()`](ExactParallelSink::cancel) instead).
123 unsafe fn finalize(sink: Self::Sink) -> Self;
124}