timely/dataflow/operators/capability.rs
1//! Capabilities to send data from operators
2//!
3//! Timely dataflow operators are only able to send data if they possess a "capability",
4//! a system-created object which warns the runtime that the operator may still produce
5//! output records.
6//!
7//! The timely dataflow runtime creates a capability and provides it to an operator whenever
8//! the operator receives input data. The capabilities allow the operator to respond to the
9//! received data, immediately or in the future, for as long as the capability is held.
10//!
11//! Timely dataflow's progress tracking infrastructure communicates the number of outstanding
12//! capabilities across all workers.
13//! Each operator may hold on to its capabilities, and may clone, advance, and drop them.
14//! Each of these actions informs the timely dataflow runtime of changes to the number of outstanding
15//! capabilities, so that the runtime can notice when the count for some capability reaches zero.
16//! While an operator can hold capabilities indefinitely, and create as many new copies of them
17//! as it would like, the progress tracking infrastructure will not move forward until the
18//! operators eventually release their capabilities.
19//!
20//! Note that these capabilities currently lack the property of "transferability":
21//! An operator should not hand its capabilities to some other operator. In the future, we should
22//! probably bind capabilities more strongly to a specific operator and output.
23
24use std::{borrow, error::Error, fmt::Display, ops::Deref};
25use std::rc::Rc;
26use std::cell::{OnceCell, RefCell};
27use std::fmt::{self, Debug};
28
29use crate::order::{PartialOrder, TotalOrder};
30use crate::progress::Timestamp;
31use crate::progress::{ChangeBatch, Stamp};
32use crate::progress::operate::PortConnectivity;
33use crate::scheduling::Activations;
34use crate::dataflow::channels::pullers::counter::ConsumedGuard;
35
36/// An internal trait expressing the capability to send messages with given timestamps.
37pub trait CapabilityTrait<T: Timestamp> {
38 /// The stamp of timestamps to attach to messages sent with this capability.
39 ///
40 /// Messages may only result in downstream work at times greater or equal to
41 /// some element of the stamp. An empty stamp makes no progress claims, and
42 /// such messages may be delivered after downstream frontiers have advanced
43 /// past all times in their contents.
44 fn stamp(&self) -> Stamp<T>;
45 /// Validates that the capability is valid for a specific internal buffer and output port.
46 fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool;
47}
48
49impl<T: Timestamp, C: CapabilityTrait<T>> CapabilityTrait<T> for &C {
50 fn stamp(&self) -> Stamp<T> { (**self).stamp() }
51 fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
52 (**self).valid_for_output(query_buffer, port)
53 }
54}
55impl<T: Timestamp, C: CapabilityTrait<T>> CapabilityTrait<T> for &mut C {
56 fn stamp(&self) -> Stamp<T> { (**self).stamp() }
57 fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
58 (**self).valid_for_output(query_buffer, port)
59 }
60}
61
62/// The capability to send data with a certain timestamp on a dataflow edge.
63///
64/// Capabilities are used by timely dataflow's progress tracking machinery to restrict and track
65/// when user code retains the ability to send messages on dataflow edges. All capabilities are
66/// constructed by the system, and should eventually be dropped by the user. Failure to drop
67/// a capability (for whatever reason) will cause timely dataflow's progress tracking to stall.
68pub struct Capability<T: Timestamp> {
69 time: T,
70 internal: Rc<RefCell<ChangeBatch<T>>>,
71}
72
73impl<T: Timestamp> CapabilityTrait<T> for Capability<T> {
74 fn stamp(&self) -> Stamp<T> { Stamp::from_elem(self.time.clone()) }
75 fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, _port: usize) -> bool {
76 Rc::ptr_eq(&self.internal, query_buffer)
77 }
78}
79
80impl<T: Timestamp> Capability<T> {
81 /// Creates a new capability at `time` while incrementing (and keeping a reference to) the provided
82 /// [`ChangeBatch`].
83 pub(crate) fn new(time: T, internal: Rc<RefCell<ChangeBatch<T>>>) -> Self {
84 internal.borrow_mut().update(time.clone(), 1);
85
86 Self {
87 time,
88 internal,
89 }
90 }
91
92 /// The timestamp associated with this capability.
93 pub fn time(&self) -> &T {
94 &self.time
95 }
96
97 /// Makes a new capability for a timestamp `new_time` greater or equal to the timestamp of
98 /// the source capability (`self`).
99 ///
100 /// This method panics if `self.time` is not less or equal to `new_time`.
101 pub fn delayed(&self, new_time: &T) -> Capability<T> {
102 /// Makes the panic branch cold & outlined to decrease code bloat & give
103 /// the inner function the best chance possible of being inlined with
104 /// minimal code bloat
105 #[cold]
106 #[inline(never)]
107 fn delayed_panic(capability: &dyn Debug, invalid_time: &dyn Debug) -> ! {
108 // Formatting & panic machinery is relatively expensive in terms of code bloat, so
109 // we outline it
110 panic!(
111 "Attempted to delay {:?} to {:?}, which is not beyond the capability's time.",
112 capability,
113 invalid_time,
114 )
115 }
116
117 self.try_delayed(new_time)
118 .unwrap_or_else(|| delayed_panic(self, new_time))
119 }
120
121 /// Attempts to make a new capability for a timestamp `new_time` that is
122 /// greater or equal to the timestamp of the source capability (`self`).
123 ///
124 /// Returns [`None`] `self.time` is not less or equal to `new_time`.
125 pub fn try_delayed(&self, new_time: &T) -> Option<Capability<T>> {
126 if self.time.less_equal(new_time) {
127 Some(Self::new(new_time.clone(), Rc::clone(&self.internal)))
128 } else {
129 None
130 }
131 }
132
133 /// Downgrades the capability to one corresponding to `new_time`.
134 ///
135 /// This method panics if `self.time` is not less or equal to `new_time`.
136 pub fn downgrade(&mut self, new_time: &T) {
137 /// Makes the panic branch cold & outlined to decrease code bloat & give
138 /// the inner function the best chance possible of being inlined with
139 /// minimal code bloat
140 #[cold]
141 #[inline(never)]
142 fn downgrade_panic(capability: &dyn Debug, invalid_time: &dyn Debug) -> ! {
143 // Formatting & panic machinery is relatively expensive in terms of code bloat, so
144 // we outline it
145 panic!(
146 "Attempted to downgrade {:?} to {:?}, which is not beyond the capability's time.",
147 capability,
148 invalid_time,
149 )
150 }
151
152 self.try_downgrade(new_time)
153 .unwrap_or_else(|_| downgrade_panic(self, new_time))
154 }
155
156 /// Attempts to downgrade the capability to one corresponding to `new_time`.
157 ///
158 /// Returns a [DowngradeError] if `self.time` is not less or equal to `new_time`.
159 pub fn try_downgrade(&mut self, new_time: &T) -> Result<(), DowngradeError> {
160 if let Some(new_capability) = self.try_delayed(new_time) {
161 *self = new_capability;
162 Ok(())
163 } else {
164 Err(DowngradeError(()))
165 }
166 }
167}
168
169// Necessary for correctness. When a capability is dropped, the "internal" `ChangeBatch` needs to be
170// updated accordingly to inform the rest of the system that the operator has released its permit
171// to send data and request notification at the associated timestamp.
172impl<T: Timestamp> Drop for Capability<T> {
173 fn drop(&mut self) {
174 let time = ::std::mem::replace(&mut self.time, T::minimum());
175 self.internal.borrow_mut().update(time, -1);
176 }
177}
178
179impl<T: Timestamp> Clone for Capability<T> {
180 fn clone(&self) -> Capability<T> {
181 Self::new(self.time.clone(), Rc::clone(&self.internal))
182 }
183}
184
185impl<T: Timestamp> Deref for Capability<T> {
186 type Target = T;
187
188 fn deref(&self) -> &T {
189 &self.time
190 }
191}
192
193impl<T: Timestamp> Debug for Capability<T> {
194 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195 f.debug_struct("Capability")
196 .field("time", &self.time)
197 .field("internal", &"...")
198 .finish()
199 }
200}
201
202impl<T: Timestamp> PartialEq for Capability<T> {
203 fn eq(&self, other: &Self) -> bool {
204 self.time() == other.time() && Rc::ptr_eq(&self.internal, &other.internal)
205 }
206}
207impl<T: Timestamp> Eq for Capability<T> { }
208
209impl<T: Timestamp> PartialOrder for Capability<T> {
210 fn less_equal(&self, other: &Self) -> bool {
211 self.time().less_equal(other.time()) && Rc::ptr_eq(&self.internal, &other.internal)
212 }
213}
214
215impl<T: Timestamp+::std::hash::Hash> ::std::hash::Hash for Capability<T> {
216 fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
217 self.time.hash(state);
218 }
219}
220
221/// An error produced when trying to downgrade a capability with a time
222/// that's not less than or equal to the current capability
223#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
224pub struct DowngradeError(());
225
226impl Display for DowngradeError {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 f.write_str("could not downgrade the given capability")
229 }
230}
231
232impl Error for DowngradeError {}
233
234/// A shared list of shared output capability buffers.
235type CapabilityUpdates<T> = Rc<RefCell<Vec<Rc<RefCell<ChangeBatch<T>>>>>>;
236
237/// An capability of an input port.
238///
239/// Holding onto this capability will implicitly hold on to a capability for all the outputs
240/// ports this input is connected to, after the connection summaries have been applied.
241///
242/// This input capability supplies a `retain_for_output(self)` method which consumes the input
243/// capability and turns it into a [Capability] for a specific output port.
244pub struct InputCapability<T: Timestamp> {
245 /// Output capability buffers, for use in minting capabilities.
246 internal: CapabilityUpdates<T>,
247 /// Timestamp summaries for each output.
248 summaries: Rc<OnceCell<PortConnectivity<T::Summary>>>,
249 /// A drop guard that updates the consumed capability this InputCapability refers to on drop
250 consumed_guard: ConsumedGuard<T>,
251}
252
253impl<T: Timestamp> CapabilityTrait<T> for InputCapability<T> {
254 fn stamp(&self) -> Stamp<T> { self.stamp().clone() }
255 fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
256 let summaries_borrow = self.summaries.get().expect("connectivity frozen at operator build");
257 let internal_borrow = self.internal.borrow();
258 // To be valid, the output buffer must match and the timestamp summary needs to be the default.
259 Rc::ptr_eq(&internal_borrow[port], query_buffer) &&
260 summaries_borrow.get(port).map_or(false, |path| path.elements() == [Default::default()])
261 }
262}
263
264impl<T: Timestamp> InputCapability<T> {
265 /// Creates a new capability reference at `time` while incrementing (and keeping a reference to)
266 /// the provided [`ChangeBatch`].
267 pub(crate) fn new(internal: CapabilityUpdates<T>, summaries: Rc<OnceCell<PortConnectivity<T::Summary>>>, guard: ConsumedGuard<T>) -> Self {
268 InputCapability {
269 internal,
270 summaries,
271 consumed_guard: guard,
272 }
273 }
274
275 /// The stamp of timestamps associated with the received message.
276 #[inline]
277 pub fn stamp(&self) -> &Stamp<T> {
278 self.consumed_guard.stamp()
279 }
280
281 /// The least timestamp of the message's stamp, if the stamp is non-empty.
282 ///
283 /// This method exists only for totally ordered timestamps, where every non-empty stamp
284 /// has a least element: the time at which the message may first result in downstream
285 /// work, and the time at which to hold a capability for it. Partially ordered timestamps
286 /// have no such element; operators over them must read the whole
287 /// [`InputCapability::stamp`] and forward each of its elements.
288 ///
289 /// Messages sent under no capabilities have an empty stamp and no least element. An
290 /// operator that needs a time should `if let Some(time) = cap.least()` and drop such
291 /// messages otherwise; they make no progress claims, and may arrive after the frontier
292 /// has passed every time in their contents.
293 #[inline]
294 pub fn least(&self) -> Option<&T> where T: TotalOrder {
295 self.stamp().least()
296 }
297
298 /// Delays capability for a specific output port.
299 ///
300 /// Makes a new capability for a timestamp `new_time` greater or equal to some element
301 /// of the stamp of the source capability (`self`).
302 ///
303 /// This method panics if no element of `self.stamp()` is less or equal to `new_time`.
304 pub fn delayed(&self, new_time: &T, output_port: usize) -> Capability<T> {
305 use crate::progress::timestamp::PathSummary;
306 if let Some(path) = self.summaries.get().expect("connectivity frozen at operator build").get(output_port) {
307 if self.stamp().iter().flat_map(|elem| path.iter().flat_map(move |summary| summary.results_in(elem))).any(|time| time.less_equal(new_time)) {
308 Capability::new(new_time.clone(), Rc::clone(&self.internal.borrow()[output_port]))
309 } else {
310 panic!("Attempted to delay to a time ({:?}) not greater or equal to the operators input-output summary ({:?}) applied to any element of the capability's stamp ({:?})", new_time, path, self.stamp());
311 }
312 }
313 else {
314 panic!("Attempted to delay a capability for a disconnected output");
315 }
316 }
317
318 /// Transforms to an owned capability for a specific output port.
319 ///
320 /// This method produces an owned capability which must be dropped to release the
321 /// capability. Users should take care that these capabilities are only stored for
322 /// as long as they are required, as failing to drop them may result in livelock.
323 ///
324 /// The capability is for the least element of the stamp, and so exists only for totally
325 /// ordered timestamps; partially ordered timestamps must be retained with
326 /// [`InputCapability::retain_stamp`], which holds a capability for each element. An
327 /// empty stamp yields no capability.
328 ///
329 /// This method panics if the timestamp summary to `output_port` strictly advances the time.
330 #[inline]
331 pub fn retain_least(&self, output_port: usize) -> Option<Capability<T>> where T: TotalOrder {
332 self.least().map(|time| self.delayed(time, output_port))
333 }
334
335 /// Transforms to an owned capability set for a specific output port, with one
336 /// capability for each element of the message's stamp.
337 ///
338 /// An empty stamp produces an empty capability set. Data sent through a session
339 /// keyed by an empty capability set makes no progress claims, and may be delivered
340 /// after downstream frontiers have advanced past all times in its contents.
341 ///
342 /// This method panics if the timestamp summary to `output_port` strictly advances
343 /// any element of the stamp.
344 pub fn retain_stamp(&self, output_port: usize) -> CapabilitySet<T> {
345 self.stamp().iter().map(|time| self.delayed(time, output_port)).collect()
346 }
347}
348
349impl<T: Timestamp> Debug for InputCapability<T> {
350 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351 f.debug_struct("InputCapability")
352 .field("stamp", self.stamp())
353 .field("internal", &"...")
354 .finish()
355 }
356}
357
358/// Capability that activates on drop or downgrade.
359#[derive(Clone, Debug)]
360pub struct ActivateCapability<T: Timestamp> {
361 pub(crate) capability: Capability<T>,
362 pub(crate) address: Rc<[usize]>,
363 pub(crate) activations: Rc<RefCell<Activations>>,
364}
365
366impl<T: Timestamp> CapabilityTrait<T> for ActivateCapability<T> {
367 fn stamp(&self) -> Stamp<T> { self.capability.stamp() }
368 fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
369 self.capability.valid_for_output(query_buffer, port)
370 }
371}
372
373impl<T: Timestamp> ActivateCapability<T> {
374 /// Creates a new activating capability.
375 pub fn new(capability: Capability<T>, address: Rc<[usize]>, activations: Rc<RefCell<Activations>>) -> Self {
376 Self {
377 capability,
378 address,
379 activations,
380 }
381 }
382
383 /// The timestamp associated with this capability.
384 pub fn time(&self) -> &T {
385 self.capability.time()
386 }
387
388 /// Creates a new delayed capability.
389 pub fn delayed(&self, time: &T) -> Self {
390 ActivateCapability {
391 capability: self.capability.delayed(time),
392 address: Rc::clone(&self.address),
393 activations: Rc::clone(&self.activations),
394 }
395 }
396
397 /// Downgrades this capability.
398 pub fn downgrade(&mut self, time: &T) {
399 self.capability.downgrade(time);
400 self.activations.borrow_mut().activate(&self.address);
401 }
402}
403
404impl<T: Timestamp> Drop for ActivateCapability<T> {
405 fn drop(&mut self) {
406 self.activations.borrow_mut().activate(&self.address);
407 }
408}
409
410/// A set of capabilities, for possibly incomparable times.
411#[derive(Clone, Debug)]
412pub struct CapabilitySet<T: Timestamp> {
413 elements: Vec<Capability<T>>,
414}
415
416impl<T: Timestamp> CapabilitySet<T> {
417
418 /// Allocates an empty capability set.
419 pub fn new() -> Self {
420 Self { elements: Vec::new() }
421 }
422
423 /// Allocates an empty capability set with space for `capacity` elements
424 pub fn with_capacity(capacity: usize) -> Self {
425 Self { elements: Vec::with_capacity(capacity) }
426 }
427
428 /// Allocates a capability set containing a single capability.
429 ///
430 /// # Examples
431 /// ```
432 /// use std::collections::HashMap;
433 /// use timely::dataflow::{
434 /// operators::{ToStream, generic::Operator},
435 /// channels::pact::Pipeline,
436 /// };
437 /// use timely::dataflow::operators::CapabilitySet;
438 ///
439 /// timely::example(|scope| {
440 /// vec![()]
441 /// .into_iter()
442 /// .to_stream(scope)
443 /// .container::<Vec<_>>()
444 /// .unary_frontier(Pipeline, "example", |default_cap, _info| {
445 /// let mut cap = CapabilitySet::from_elem(default_cap);
446 /// move |(input, frontier), output| {
447 /// cap.downgrade(&frontier.frontier());
448 /// input.for_each_stamp(|cap, data| {});
449 /// let a_cap = cap.first();
450 /// if let Some(a_cap) = a_cap.as_ref() {
451 /// output.session(a_cap).give(());
452 /// }
453 /// }
454 /// })
455 /// .container::<Vec<_>>();
456 /// });
457 /// ```
458 pub fn from_elem(cap: Capability<T>) -> Self {
459 Self { elements: vec![cap] }
460 }
461
462 /// Inserts `capability` into the set, discarding redundant capabilities.
463 pub fn insert(&mut self, capability: Capability<T>) {
464 if !self.elements.iter().any(|c| c.less_equal(&capability)) {
465 self.elements.retain(|c| !capability.less_equal(c));
466 self.elements.push(capability);
467 }
468 }
469
470 /// Creates a new capability to send data at `time`.
471 ///
472 /// This method panics if there does not exist a capability in `self.elements` less or equal to `time`.
473 pub fn delayed(&self, time: &T) -> Capability<T> {
474 /// Makes the panic branch cold & outlined to decrease code bloat & give
475 /// the inner function the best chance possible of being inlined with
476 /// minimal code bloat
477 #[cold]
478 #[inline(never)]
479 fn delayed_panic(invalid_time: &dyn Debug) -> ! {
480 // Formatting & panic machinery is relatively expensive in terms of code bloat, so
481 // we outline it
482 panic!(
483 "failed to create a delayed capability, the current set does not \
484 have an element less than or equal to {:?}",
485 invalid_time,
486 )
487 }
488
489 self.try_delayed(time)
490 .unwrap_or_else(|| delayed_panic(time))
491 }
492
493 /// Attempts to create a new capability to send data at `time`.
494 ///
495 /// Returns [`None`] if there does not exist a capability in `self.elements` less or equal to `time`.
496 pub fn try_delayed(&self, time: &T) -> Option<Capability<T>> {
497 self.elements
498 .iter()
499 .find(|capability| capability.time().less_equal(time))
500 .and_then(|capability| capability.try_delayed(time))
501 }
502
503 /// Downgrades the set of capabilities to correspond with the times in `frontier`.
504 ///
505 /// This method panics if any element of `frontier` is not greater or equal to some element of `self.elements`.
506 pub fn downgrade<B, F>(&mut self, frontier: F)
507 where
508 B: borrow::Borrow<T>,
509 F: IntoIterator<Item = B>,
510 {
511 /// Makes the panic branch cold & outlined to decrease code bloat & give
512 /// the inner function the best chance possible of being inlined with
513 /// minimal code bloat
514 #[cold]
515 #[inline(never)]
516 fn downgrade_panic() -> ! {
517 // Formatting & panic machinery is relatively expensive in terms of code bloat, so
518 // we outline it
519 panic!(
520 "Attempted to downgrade a CapabilitySet with a frontier containing an element \
521 that was not beyond an element within the set"
522 )
523 }
524
525 self.try_downgrade(frontier)
526 .unwrap_or_else(|_| downgrade_panic())
527 }
528
529 /// Attempts to downgrade the set of capabilities to correspond with the times in `frontier`.
530 ///
531 /// Returns [`None`] if any element of `frontier` is not greater or equal to some element of `self.elements`.
532 ///
533 /// **Warning**: If an error is returned the capability set may be in an inconsistent state and can easily
534 /// cause logic errors within the program if not properly handled.
535 ///
536 pub fn try_downgrade<B, F>(&mut self, frontier: F) -> Result<(), DowngradeError>
537 where
538 B: borrow::Borrow<T>,
539 F: IntoIterator<Item = B>,
540 {
541 let count = self.elements.len();
542 for time in frontier.into_iter() {
543 let capability = self.try_delayed(time.borrow()).ok_or(DowngradeError(()))?;
544 self.elements.push(capability);
545 }
546 self.elements.drain(..count);
547
548 Ok(())
549 }
550}
551
552impl<T: Timestamp> CapabilityTrait<T> for CapabilitySet<T> {
553 /// The stamp of the set's timestamps.
554 ///
555 /// An empty set stamps messages with an empty stamp: such messages make no
556 /// progress claims, are invisible to progress tracking, and may be delivered
557 /// after downstream frontiers have advanced past all times in their contents.
558 fn stamp(&self) -> Stamp<T> {
559 self.elements.iter().map(|capability| capability.time().clone()).collect()
560 }
561 /// Valid iff every member capability is valid for the output.
562 ///
563 /// An empty set is vacuously valid for any output: constructing an empty set
564 /// and opening a session with it is the deliberate mechanism for sending
565 /// messages that make no progress claims.
566 fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>, port: usize) -> bool {
567 self.elements.iter().all(|capability| capability.valid_for_output(query_buffer, port))
568 }
569}
570
571impl<T: Timestamp> CapabilitySet<T> {
572 /// Creates a new capability set to send data at each element of `stamp`.
573 ///
574 /// This method panics if any element of `stamp` is without a capability in
575 /// `self.elements` less or equal to it.
576 pub fn delayed_stamp(&self, stamp: &Stamp<T>) -> CapabilitySet<T> {
577 stamp.iter().map(|time| self.delayed(time)).collect()
578 }
579}
580
581impl<T: Timestamp> FromIterator<Capability<T>> for CapabilitySet<T> {
582 fn from_iter<I: IntoIterator<Item = Capability<T>>>(iter: I) -> Self {
583 let mut result = Self::new();
584 for capability in iter {
585 result.insert(capability);
586 }
587 result
588 }
589}
590
591impl<T> From<Vec<Capability<T>>> for CapabilitySet<T>
592where
593 T: Timestamp,
594{
595 fn from(capabilities: Vec<Capability<T>>) -> Self {
596 let mut this = Self::with_capacity(capabilities.len());
597 for capability in capabilities {
598 this.insert(capability);
599 }
600
601 this
602 }
603}
604
605impl<T: Timestamp> Default for CapabilitySet<T> {
606 fn default() -> Self {
607 Self::new()
608 }
609}
610
611impl<T: Timestamp> Deref for CapabilitySet<T> {
612 type Target=[Capability<T>];
613
614 fn deref(&self) -> &[Capability<T>] {
615 &self.elements
616 }
617}