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}