Skip to main content

timely/dataflow/operators/generic/
operator.rs

1
2//! Methods to construct generic streaming and blocking unary operators.
3
4use crate::progress::frontier::MutableAntichain;
5use crate::progress::Timestamp;
6use crate::dataflow::channels::pact::ParallelizationContract;
7
8use crate::dataflow::operators::generic::handles::{InputHandleCore, OutputBuilderSession, OutputBuilder};
9use crate::dataflow::operators::capability::Capability;
10
11use crate::dataflow::{Scope, Stream};
12
13use super::builder_rc::OperatorBuilder;
14use crate::dataflow::operators::generic::OperatorInfo;
15use crate::{Container, ContainerBuilder};
16use crate::container::CapacityContainerBuilder;
17
18/// Methods to construct generic streaming and blocking operators.
19pub trait Operator<'scope, T: Timestamp, C1> {
20    /// Creates a new dataflow operator that partitions its input stream by a parallelization
21    /// strategy `pact`, and repeatedly invokes `logic`, the function returned by the function passed as `constructor`.
22    /// `logic` can read from the input stream, write to the output stream, and inspect the frontier at the input.
23    ///
24    /// # Examples
25    /// ```
26    /// use std::collections::HashMap;
27    /// use timely::dataflow::operators::ToStream;
28    /// use timely::dataflow::operators::generic::Operator;
29    /// use timely::dataflow::channels::pact::Pipeline;
30    ///
31    /// timely::example(|scope| {
32    ///     (0u64..10)
33    ///         .to_stream(scope)
34    ///         .container::<Vec<_>>()
35    ///         .unary_frontier(Pipeline, "example", |default_cap, _info| {
36    ///             let mut cap = Some(default_cap.delayed(&12));
37    ///             let mut stash = HashMap::new();
38    ///             move |(input, frontier), output| {
39    ///                 if let Some(ref c) = cap.take() {
40    ///                     output.session(&c).give(12);
41    ///                 }
42    ///                 // Stash data under a capability for its least time.
43    ///                 input.for_each_stamp(|cap, data| {
44    ///                     if let Some(cap) = cap.retain_least(output.output_index()) {
45    ///                         stash.entry(cap)
46    ///                              .or_insert(Vec::new())
47    ///                              .extend(data.flat_map(|d| d.drain(..)));
48    ///                     }
49    ///                 });
50    ///                 // Send stashed data whose time the input frontier has passed.
51    ///                 stash.retain(|cap, vec| {
52    ///                     if frontier.frontier().less_equal(cap.time()) { true } else {
53    ///                         output.session(cap).give_iterator(vec.drain(..));
54    ///                         false
55    ///                     }
56    ///                 });
57    ///             }
58    ///         })
59    ///         .container::<Vec<_>>();
60    /// });
61    /// ```
62    fn unary_frontier<CB, B, L, P>(self, pact: P, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
63    where
64        CB: ContainerBuilder,
65        B: FnOnce(Capability<T>, OperatorInfo) -> L,
66        L: FnMut((&mut InputHandleCore<T, C1, P::Puller>, &MutableAntichain<T>),
67                 &mut OutputBuilderSession<'_, T, CB>)+'static,
68        P: ParallelizationContract<T, C1>;
69
70
71    /// Creates a new dataflow operator that partitions its input stream by a parallelization
72    /// strategy `pact`, and repeatedly invokes `logic`, the function returned by the function passed as `constructor`.
73    /// `logic` can read from the input stream, and write to the output stream.
74    ///
75    /// # Examples
76    /// ```
77    /// use timely::dataflow::operators::ToStream;
78    /// use timely::dataflow::operators::generic::operator::Operator;
79    /// use timely::dataflow::channels::pact::Pipeline;
80    /// use timely::dataflow::Scope;
81    ///
82    /// timely::example(|scope| {
83    ///     (0u64..10)
84    ///         .to_stream(scope)
85    ///         .container::<Vec<_>>()
86    ///         .unary(Pipeline, "example", |default_cap, _info| {
87    ///             let mut cap = Some(default_cap.delayed(&12));
88    ///             move |input, output| {
89    ///                 if let Some(ref c) = cap.take() {
90    ///                     output.session(&c).give(100);
91    ///                 }
92    ///                 input.for_each_stamp(|cap, data| {
93    ///                     output.session(&cap).give_containers(data);
94    ///                 });
95    ///             }
96    ///         });
97    /// });
98    /// ```
99    fn unary<CB, B, L, P>(self, pact: P, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
100    where
101        CB: ContainerBuilder,
102        B: FnOnce(Capability<T>, OperatorInfo) -> L,
103        L: FnMut(&mut InputHandleCore<T, C1, P::Puller>,
104                 &mut OutputBuilderSession<T, CB>)+'static,
105        P: ParallelizationContract<T, C1>;
106
107    /// Creates a new dataflow operator that partitions its input streams by a parallelization
108    /// strategy `pact`, and repeatedly invokes `logic`, the function returned by the function passed as `constructor`.
109    /// `logic` can read from the input streams, write to the output stream, and inspect the frontier at the inputs.
110    ///
111    /// # Examples
112    /// ```
113    /// use std::collections::HashMap;
114    /// use timely::dataflow::operators::{Input, Inspect};
115    /// use timely::dataflow::operators::generic::operator::Operator;
116    /// use timely::dataflow::channels::pact::Pipeline;
117    ///
118    /// timely::execute(timely::Config::thread(), |worker| {
119    ///    let (mut in1, mut in2) = worker.dataflow::<usize,_,_>(|scope| {
120    ///        let (in1_handle, in1) = scope.new_input::<Vec<_>>();
121    ///        let (in2_handle, in2) = scope.new_input::<Vec<_>>();
122    ///        in1.binary_frontier(in2, Pipeline, Pipeline, "example", |mut _default_cap, _info| {
123    ///            let mut stash = HashMap::new();
124    ///            move |(input1, frontier1), (input2, frontier2), output| {
125    ///                // Stash data from either input under a capability for its least time.
126    ///                input1.for_each_stamp(|cap, data| {
127    ///                    if let Some(cap) = cap.retain_least(output.output_index()) {
128    ///                        stash.entry(cap).or_insert(Vec::new()).extend(data.flat_map(|d| d.drain(..)));
129    ///                    }
130    ///                });
131    ///                input2.for_each_stamp(|cap, data| {
132    ///                    if let Some(cap) = cap.retain_least(output.output_index()) {
133    ///                        stash.entry(cap).or_insert(Vec::new()).extend(data.flat_map(|d| d.drain(..)));
134    ///                    }
135    ///                });
136    ///                // Send stashed data whose time both input frontiers have passed.
137    ///                stash.retain(|cap, vec| {
138    ///                    if frontier1.frontier().less_equal(cap.time()) || frontier2.frontier().less_equal(cap.time()) { true } else {
139    ///                        output.session(cap).give_iterator(vec.drain(..));
140    ///                        false
141    ///                    }
142    ///                });
143    ///            }
144    ///        })
145    ///        .container::<Vec<_>>()
146    ///        .inspect_core(|e| if let Ok((s, x)) = e { println!("{:?} -> {:?}", s, x) });
147    ///
148    ///        (in1_handle, in2_handle)
149    ///    });
150    ///
151    ///    for i in 1..10 {
152    ///        in1.send(i - 1);
153    ///        in1.advance_to(i);
154    ///        in2.send(i - 1);
155    ///        in2.advance_to(i);
156    ///    }
157    /// }).unwrap();
158    /// ```
159    fn binary_frontier<C2, CB, B, L, P1, P2>(self, other: Stream<'scope, T, C2>, pact1: P1, pact2: P2, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
160    where
161        C2: Container,
162        CB: ContainerBuilder,
163        B: FnOnce(Capability<T>, OperatorInfo) -> L,
164        L: FnMut((&mut InputHandleCore<T, C1, P1::Puller>, &MutableAntichain<T>),
165                 (&mut InputHandleCore<T, C2, P2::Puller>, &MutableAntichain<T>),
166                 &mut OutputBuilderSession<'_, T, CB>)+'static,
167        P1: ParallelizationContract<T, C1>,
168        P2: ParallelizationContract<T, C2>;
169
170
171    /// Creates a new dataflow operator that partitions its input streams by a parallelization
172    /// strategy `pact`, and repeatedly invokes `logic`, the function returned by the function passed as `constructor`.
173    /// `logic` can read from the input streams, write to the output stream, and inspect the frontier at the inputs.
174    ///
175    /// # Examples
176    /// ```
177    /// use timely::dataflow::operators::{ToStream, Inspect};
178    /// use timely::dataflow::operators::generic::operator::Operator;
179    /// use timely::dataflow::channels::pact::Pipeline;
180    /// use timely::dataflow::Scope;
181    ///
182    /// timely::example(|scope| {
183    ///     let stream1 = (0u64..10).to_stream(scope).container::<Vec<_>>();
184    ///     let stream2 = (0u64..10).to_stream(scope).container::<Vec<_>>();
185    ///     stream1
186    ///         .binary(stream2, Pipeline, Pipeline, "example", |default_cap, _info| {
187    ///             let mut cap = Some(default_cap.delayed(&12));
188    ///             move |input1, input2, output| {
189    ///                 if let Some(ref c) = cap.take() {
190    ///                     output.session(&c).give(100);
191    ///                 }
192    ///                 input1.for_each_stamp(|cap, data| output.session(&cap).give_containers(data));
193    ///                 input2.for_each_stamp(|cap, data| output.session(&cap).give_containers(data));
194    ///             }
195    ///         }).inspect(|x| println!("{:?}", x));
196    /// });
197    /// ```
198    fn binary<C2, CB, B, L, P1, P2>(self, other: Stream<'scope, T, C2>, pact1: P1, pact2: P2, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
199    where
200        C2: Container,
201        CB: ContainerBuilder,
202        B: FnOnce(Capability<T>, OperatorInfo) -> L,
203        L: FnMut(&mut InputHandleCore<T, C1, P1::Puller>,
204                 &mut InputHandleCore<T, C2, P2::Puller>,
205                 &mut OutputBuilderSession<'_, T, CB>)+'static,
206        P1: ParallelizationContract<T, C1>,
207        P2: ParallelizationContract<T, C2>;
208
209    /// Creates a new dataflow operator that partitions its input stream by a parallelization
210    /// strategy `pact`, and repeatedly invokes the function `logic` which can read from the input stream
211    /// and inspect the frontier at the input.
212    ///
213    /// # Examples
214    /// ```
215    /// use timely::dataflow::operators::ToStream;
216    /// use timely::dataflow::operators::generic::operator::Operator;
217    /// use timely::dataflow::channels::pact::Pipeline;
218    /// use timely::dataflow::Scope;
219    ///
220    /// timely::example(|scope| {
221    ///     (0u64..10)
222    ///         .to_stream(scope)
223    ///         .container::<Vec<_>>()
224    ///         .sink(Pipeline, "example", |(input, frontier)| {
225    ///             input.for_each_stamp(|cap, data| {
226    ///                 for datum in data.flatten() {
227    ///                     println!("{:?}:\t{:?}", cap, datum);
228    ///                 }
229    ///             });
230    ///         });
231    /// });
232    /// ```
233    fn sink<L, P>(self, pact: P, name: &str, logic: L)
234    where
235        L: FnMut((&mut InputHandleCore<T, C1, P::Puller>, &MutableAntichain<T>))+'static,
236        P: ParallelizationContract<T, C1>;
237}
238
239impl<'scope, T: Timestamp, C1: Container> Operator<'scope, T, C1> for Stream<'scope, T, C1> {
240
241    fn unary_frontier<CB, B, L, P>(self, pact: P, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
242    where
243        CB: ContainerBuilder,
244        B: FnOnce(Capability<T>, OperatorInfo) -> L,
245        L: FnMut((&mut InputHandleCore<T, C1, P::Puller>, &MutableAntichain<T>),
246                 &mut OutputBuilderSession<'_, T, CB>)+'static,
247        P: ParallelizationContract<T, C1> {
248
249        let mut builder = OperatorBuilder::new(name.to_owned(), self.scope());
250        let operator_info = builder.operator_info();
251
252        let mut input = builder.new_input(self, pact);
253        let (output, stream) = builder.new_output();
254        let mut output = OutputBuilder::from(output);
255
256        builder.build(move |mut capabilities| {
257            // `capabilities` should be a single-element vector.
258            let capability = capabilities.pop().unwrap();
259            let mut logic = constructor(capability, operator_info);
260            move |frontiers| {
261                let mut output_handle = output.activate();
262                logic((&mut input, &frontiers[0]), &mut output_handle);
263            }
264        });
265
266        stream
267    }
268
269
270    fn unary<CB, B, L, P>(self, pact: P, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
271    where
272        CB: ContainerBuilder,
273        B: FnOnce(Capability<T>, OperatorInfo) -> L,
274        L: FnMut(&mut InputHandleCore<T, C1, P::Puller>,
275                 &mut OutputBuilderSession<'_, T, CB>)+'static,
276        P: ParallelizationContract<T, C1> {
277
278        let mut builder = OperatorBuilder::new(name.to_owned(), self.scope());
279        let operator_info = builder.operator_info();
280
281        let mut input = builder.new_input(self, pact);
282        builder.set_notify_for(0, crate::progress::operate::FrontierInterest::Never);
283        let (output, stream) = builder.new_output();
284        let mut output = OutputBuilder::from(output);
285
286        builder.build(move |mut capabilities| {
287            // `capabilities` should be a single-element vector.
288            let capability = capabilities.pop().unwrap();
289            let mut logic = constructor(capability, operator_info);
290            move |_frontiers| logic(&mut input, &mut output.activate())
291        });
292
293        stream
294    }
295
296    fn binary_frontier<C2, CB, B, L, P1, P2>(self, other: Stream<'scope, T, C2>, pact1: P1, pact2: P2, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
297    where
298        C2: Container,
299        CB: ContainerBuilder,
300        B: FnOnce(Capability<T>, OperatorInfo) -> L,
301        L: FnMut((&mut InputHandleCore<T, C1, P1::Puller>, &MutableAntichain<T>),
302                 (&mut InputHandleCore<T, C2, P2::Puller>, &MutableAntichain<T>),
303                 &mut OutputBuilderSession<'_, T, CB>)+'static,
304        P1: ParallelizationContract<T, C1>,
305        P2: ParallelizationContract<T, C2> {
306
307        let mut builder = OperatorBuilder::new(name.to_owned(), self.scope());
308        let operator_info = builder.operator_info();
309
310        let mut input1 = builder.new_input(self, pact1);
311        let mut input2 = builder.new_input(other, pact2);
312        let (output, stream) = builder.new_output();
313        let mut output = OutputBuilder::from(output);
314
315        builder.build(move |mut capabilities| {
316            // `capabilities` should be a single-element vector.
317            let capability = capabilities.pop().unwrap();
318            let mut logic = constructor(capability, operator_info);
319            move |frontiers| {
320                let mut output_handle = output.activate();
321                logic((&mut input1, &frontiers[0]), (&mut input2, &frontiers[1]), &mut output_handle);
322            }
323        });
324
325        stream
326    }
327
328
329
330    fn binary<C2, CB, B, L, P1, P2>(self, other: Stream<'scope, T, C2>, pact1: P1, pact2: P2, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
331    where
332        C2: Container,
333        CB: ContainerBuilder,
334        B: FnOnce(Capability<T>, OperatorInfo) -> L,
335        L: FnMut(&mut InputHandleCore<T, C1, P1::Puller>,
336                 &mut InputHandleCore<T, C2, P2::Puller>,
337                 &mut OutputBuilderSession<'_, T, CB>)+'static,
338        P1: ParallelizationContract<T, C1>,
339        P2: ParallelizationContract<T, C2> {
340
341        let mut builder = OperatorBuilder::new(name.to_owned(), self.scope());
342        let operator_info = builder.operator_info();
343
344        let mut input1 = builder.new_input(self, pact1);
345        let mut input2 = builder.new_input(other, pact2);
346        builder.set_notify_for(0, crate::progress::operate::FrontierInterest::Never);
347        builder.set_notify_for(1, crate::progress::operate::FrontierInterest::Never);
348        let (output, stream) = builder.new_output();
349        let mut output = OutputBuilder::from(output);
350
351        builder.build(move |mut capabilities| {
352            // `capabilities` should be a single-element vector.
353            let capability = capabilities.pop().unwrap();
354            let mut logic = constructor(capability, operator_info);
355            move |_frontiers| {
356                let mut output_handle = output.activate();
357                logic(&mut input1, &mut input2, &mut output_handle);
358            }
359        });
360
361        stream
362    }
363
364    fn sink<L, P>(self, pact: P, name: &str, mut logic: L)
365    where
366        L: FnMut((&mut InputHandleCore<T, C1, P::Puller>, &MutableAntichain<T>))+'static,
367        P: ParallelizationContract<T, C1> {
368
369        let mut builder = OperatorBuilder::new(name.to_owned(), self.scope());
370        let mut input = builder.new_input(self, pact);
371
372        builder.build(|_capabilities| {
373            move |frontiers| {
374                logic((&mut input, &frontiers[0]));
375            }
376        });
377    }
378}
379
380/// Creates a new data stream source for a scope.
381///
382/// The source is defined by a name, and a constructor which takes a default capability to
383/// a method that can be repeatedly called on a output handle. The method is then repeatedly
384/// invoked, and is expected to eventually send data and downgrade and release capabilities.
385///
386/// # Examples
387/// ```
388/// use timely::dataflow::operators::Inspect;
389/// use timely::dataflow::operators::generic::operator::source;
390/// use timely::dataflow::Scope;
391///
392/// timely::example(|scope| {
393///
394///     source(scope, "Source", |capability, info| {
395///
396///         let activator = scope.activator_for(info.address);
397///
398///         let mut cap = Some(capability);
399///         move |output| {
400///
401///             let mut done = false;
402///             if let Some(cap) = cap.as_mut() {
403///                 // get some data and send it.
404///                 let time = cap.time().clone();
405///                 output.session(&cap)
406///                       .give(*cap.time());
407///
408///                 // downgrade capability.
409///                 cap.downgrade(&(time + 1));
410///                 done = time > 20;
411///             }
412///
413///             if done { cap = None; }
414///             else    { activator.activate(); }
415///         }
416///     })
417///     .container::<Vec<_>>()
418///     .inspect(|x| println!("number: {:?}", x));
419/// });
420/// ```
421pub fn source<'scope, T: Timestamp, CB, B, L>(scope: Scope<'scope, T>, name: &str, constructor: B) -> Stream<'scope, T, CB::Container>
422where
423    CB: ContainerBuilder,
424    B: FnOnce(Capability<T>, OperatorInfo) -> L,
425    L: FnMut(&mut OutputBuilderSession<'_, T, CB>)+'static {
426
427    let mut builder = OperatorBuilder::new(name.to_owned(), scope);
428    let operator_info = builder.operator_info();
429
430    let (output, stream) = builder.new_output();
431    let mut output = OutputBuilder::from(output);
432
433    builder.build(move |mut capabilities| {
434        // `capabilities` should be a single-element vector.
435        let capability = capabilities.pop().unwrap();
436        let mut logic = constructor(capability, operator_info);
437        move |_frontier| {
438            logic(&mut output.activate());
439        }
440    });
441
442    stream
443}
444
445/// Constructs an empty stream.
446///
447/// This method is useful in patterns where an input is required, but there is no
448/// meaningful data to provide. The replaces patterns like `stream.filter(|_| false)`
449/// which are just silly.
450///
451/// # Examples
452/// ```
453/// use timely::dataflow::operators::Inspect;
454/// use timely::dataflow::operators::generic::operator::empty;
455/// use timely::dataflow::Scope;
456///
457/// timely::example(|scope| {
458///
459///
460///     empty::<_, Vec<_>>(scope)     // type required in this example
461///         .inspect(|()| panic!("never called"));
462///
463/// });
464/// ```
465pub fn empty<'scope, T: Timestamp, C: Container>(scope: Scope<'scope, T>) -> Stream<'scope, T, C> {
466    source::<_, CapacityContainerBuilder<C>, _, _>(scope, "Empty", |_capability, _info| |_output| {
467        // drop capability, do nothing
468    })
469}