Skip to main content

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