Skip to content

Commit 6a73600

Browse files
authored
timely: unconstrained lifetime for CapabilityRef (#491)
Since the merge of #429, `CapabilityRef`s have been made safe to hold onto across operator invocations because that PR made sure that they only decremented their progress counts on `Drop`. While this allowed `async`/`await` based operators to freely hold on to them, it was still very difficult for synchronous based operators to do the same thing, due to the lifetime attached to the `CapabilityRef`. We can observe that the lifetime no longer provides any benefits, which means it can be removed and turn `CapabilityRef`s into fully owned values. This allows any style of operator to easily hold on to them. The benefit of that isn't just performance (by avoiding the `retain()` dance), but also about deferring the decision of the output port a given input should flow to to a later time. After making this change, the name `CapabilityRef` felt wrong, since there is no reference to anything anymore. Instead, the main distinction between `CapabilityRef`s and `Capabilities` are that the former is associated with an input port and the latter is associated with an output port. As such, I have renamed `CapabilityRef` to `InputCapability` to signal to users that holding onto one of them represents holding onto a timestamp at the input for which we have not yet determined the output port that it should flow to. This nicely ties up the semantics of the `InputCapability::retain_for_output` and `InputCapability::delayed_for_output` methods, which make it clear by their name and signature that this is what "transfers" the capability from input ports to output ports. Signed-off-by: Petros Angelatos <[email protected]>
1 parent 51212fe commit 6a73600

File tree

4 files changed

+44
-36
lines changed

4 files changed

+44
-36
lines changed

timely/src/dataflow/channels/pullers/counter.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,23 @@ pub struct Counter<T: Ord+Clone+'static, D, P: Pull<BundleCore<T, D>>> {
1616
}
1717

1818
/// A guard type that updates the change batch counts on drop
19-
pub struct ConsumedGuard<'a, T: Ord + Clone + 'static> {
20-
consumed: &'a Rc<RefCell<ChangeBatch<T>>>,
19+
pub struct ConsumedGuard<T: Ord + Clone + 'static> {
20+
consumed: Rc<RefCell<ChangeBatch<T>>>,
2121
time: Option<T>,
2222
len: usize,
2323
}
2424

25-
impl<'a, T:Ord+Clone+'static> Drop for ConsumedGuard<'a, T> {
25+
impl<T:Ord+Clone+'static> ConsumedGuard<T> {
26+
pub(crate) fn time(&self) -> &T {
27+
&self.time.as_ref().unwrap()
28+
}
29+
}
30+
31+
impl<T:Ord+Clone+'static> Drop for ConsumedGuard<T> {
2632
fn drop(&mut self) {
27-
self.consumed.borrow_mut().update(self.time.take().unwrap(), self.len as i64);
33+
// SAFETY: we're in a Drop impl, so this runs at most once
34+
let time = self.time.take().unwrap();
35+
self.consumed.borrow_mut().update(time, self.len as i64);
2836
}
2937
}
3038

@@ -36,11 +44,11 @@ impl<T:Ord+Clone+'static, D: Container, P: Pull<BundleCore<T, D>>> Counter<T, D,
3644
}
3745

3846
#[inline]
39-
pub(crate) fn next_guarded(&mut self) -> Option<(ConsumedGuard<'_, T>, &mut BundleCore<T, D>)> {
47+
pub(crate) fn next_guarded(&mut self) -> Option<(ConsumedGuard<T>, &mut BundleCore<T, D>)> {
4048
if let Some(message) = self.pullable.pull() {
4149
if message.data.len() > 0 {
4250
let guard = ConsumedGuard {
43-
consumed: &self.consumed,
51+
consumed: Rc::clone(&self.consumed),
4452
time: Some(message.time.clone()),
4553
len: message.data.len(),
4654
};

timely/src/dataflow/operators/capability.rs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -225,39 +225,39 @@ impl Error for DowngradeError {}
225225

226226
type CapabilityUpdates<T> = Rc<RefCell<Vec<Rc<RefCell<ChangeBatch<T>>>>>>;
227227

228-
/// An unowned capability, which can be used but not retained.
228+
/// An capability of an input port. Holding onto this capability will implicitly holds onto a
229+
/// capability for all the outputs ports this input is connected to, after the connection summaries
230+
/// have been applied.
229231
///
230-
/// The capability reference supplies a `retain(self)` method which consumes the reference
231-
/// and turns it into an owned capability
232-
pub struct CapabilityRef<'cap, T: Timestamp+'cap> {
233-
time: &'cap T,
232+
/// This input capability supplies a `retain_for_output(self)` method which consumes the input
233+
/// capability and turns it into a [Capability] for a specific output port.
234+
pub struct InputCapability<T: Timestamp> {
234235
internal: CapabilityUpdates<T>,
235-
/// A drop guard that updates the consumed capability this CapabilityRef refers to on drop
236-
_consumed_guard: ConsumedGuard<'cap, T>,
236+
/// A drop guard that updates the consumed capability this InputCapability refers to on drop
237+
consumed_guard: ConsumedGuard<T>,
237238
}
238239

239-
impl<'cap, T: Timestamp+'cap> CapabilityTrait<T> for CapabilityRef<'cap, T> {
240-
fn time(&self) -> &T { self.time }
240+
impl<T: Timestamp> CapabilityTrait<T> for InputCapability<T> {
241+
fn time(&self) -> &T { self.time() }
241242
fn valid_for_output(&self, query_buffer: &Rc<RefCell<ChangeBatch<T>>>) -> bool {
242243
// let borrow = ;
243244
self.internal.borrow().iter().any(|rc| Rc::ptr_eq(rc, query_buffer))
244245
}
245246
}
246247

247-
impl<'cap, T: Timestamp + 'cap> CapabilityRef<'cap, T> {
248+
impl<T: Timestamp> InputCapability<T> {
248249
/// Creates a new capability reference at `time` while incrementing (and keeping a reference to)
249250
/// the provided [`ChangeBatch`].
250-
pub(crate) fn new(time: &'cap T, internal: CapabilityUpdates<T>, guard: ConsumedGuard<'cap, T>) -> Self {
251-
CapabilityRef {
252-
time,
251+
pub(crate) fn new(internal: CapabilityUpdates<T>, guard: ConsumedGuard<T>) -> Self {
252+
InputCapability {
253253
internal,
254-
_consumed_guard: guard,
254+
consumed_guard: guard,
255255
}
256256
}
257257

258258
/// The timestamp associated with this capability.
259259
pub fn time(&self) -> &T {
260-
self.time
260+
self.consumed_guard.time()
261261
}
262262

263263
/// Makes a new capability for a timestamp `new_time` greater or equal to the timestamp of
@@ -271,7 +271,7 @@ impl<'cap, T: Timestamp + 'cap> CapabilityRef<'cap, T> {
271271
/// Delays capability for a specific output port.
272272
pub fn delayed_for_output(&self, new_time: &T, output_port: usize) -> Capability<T> {
273273
// TODO : Test operator summary?
274-
if !self.time.less_equal(new_time) {
274+
if !self.time().less_equal(new_time) {
275275
panic!("Attempted to delay {:?} to {:?}, which is not beyond the capability's time.", self, new_time);
276276
}
277277
if output_port < self.internal.borrow().len() {
@@ -295,26 +295,26 @@ impl<'cap, T: Timestamp + 'cap> CapabilityRef<'cap, T> {
295295
/// Transforms to an owned capability for a specific output port.
296296
pub fn retain_for_output(self, output_port: usize) -> Capability<T> {
297297
if output_port < self.internal.borrow().len() {
298-
Capability::new(self.time.clone(), self.internal.borrow()[output_port].clone())
298+
Capability::new(self.time().clone(), self.internal.borrow()[output_port].clone())
299299
}
300300
else {
301301
panic!("Attempted to acquire a capability for a non-existent output port.");
302302
}
303303
}
304304
}
305305

306-
impl<'cap, T: Timestamp> Deref for CapabilityRef<'cap, T> {
306+
impl<T: Timestamp> Deref for InputCapability<T> {
307307
type Target = T;
308308

309309
fn deref(&self) -> &T {
310-
self.time
310+
self.time()
311311
}
312312
}
313313

314-
impl<'cap, T: Timestamp> Debug for CapabilityRef<'cap, T> {
314+
impl<T: Timestamp> Debug for InputCapability<T> {
315315
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
316-
f.debug_struct("CapabilityRef")
317-
.field("time", &self.time)
316+
f.debug_struct("InputCapability")
317+
.field("time", self.time())
318318
.field("internal", &"...")
319319
.finish()
320320
}

timely/src/dataflow/operators/generic/handles.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::communication::{Push, Pull, message::RefOrMut};
1717
use crate::Container;
1818
use crate::logging::TimelyLogger as Logger;
1919

20-
use crate::dataflow::operators::CapabilityRef;
20+
use crate::dataflow::operators::InputCapability;
2121
use crate::dataflow::operators::capability::CapabilityTrait;
2222

2323
/// Handle to an operator's input stream.
@@ -47,15 +47,15 @@ impl<'a, T: Timestamp, D: Container, P: Pull<BundleCore<T, D>>> InputHandleCore<
4747
/// The timestamp `t` of the input buffer can be retrieved by invoking `.time()` on the capability.
4848
/// Returns `None` when there's no more data available.
4949
#[inline]
50-
pub fn next(&mut self) -> Option<(CapabilityRef<T>, RefOrMut<D>)> {
50+
pub fn next(&mut self) -> Option<(InputCapability<T>, RefOrMut<D>)> {
5151
let internal = &self.internal;
5252
self.pull_counter.next_guarded().map(|(guard, bundle)| {
5353
match bundle.as_ref_or_mut() {
5454
RefOrMut::Ref(bundle) => {
55-
(CapabilityRef::new(&bundle.time, internal.clone(), guard), RefOrMut::Ref(&bundle.data))
55+
(InputCapability::new(internal.clone(), guard), RefOrMut::Ref(&bundle.data))
5656
},
5757
RefOrMut::Mut(bundle) => {
58-
(CapabilityRef::new(&bundle.time, internal.clone(), guard), RefOrMut::Mut(&mut bundle.data))
58+
(InputCapability::new(internal.clone(), guard), RefOrMut::Mut(&mut bundle.data))
5959
},
6060
}
6161
})
@@ -80,7 +80,7 @@ impl<'a, T: Timestamp, D: Container, P: Pull<BundleCore<T, D>>> InputHandleCore<
8080
/// });
8181
/// ```
8282
#[inline]
83-
pub fn for_each<F: FnMut(CapabilityRef<T>, RefOrMut<D>)>(&mut self, mut logic: F) {
83+
pub fn for_each<F: FnMut(InputCapability<T>, RefOrMut<D>)>(&mut self, mut logic: F) {
8484
let mut logging = self.logging.take();
8585
while let Some((cap, data)) = self.next() {
8686
logging.as_mut().map(|l| l.log(crate::logging::GuardedMessageEvent { is_start: true }));
@@ -105,7 +105,7 @@ impl<'a, T: Timestamp, D: Container, P: Pull<BundleCore<T, D>>+'a> FrontieredInp
105105
/// The timestamp `t` of the input buffer can be retrieved by invoking `.time()` on the capability.
106106
/// Returns `None` when there's no more data available.
107107
#[inline]
108-
pub fn next(&mut self) -> Option<(CapabilityRef<T>, RefOrMut<D>)> {
108+
pub fn next(&mut self) -> Option<(InputCapability<T>, RefOrMut<D>)> {
109109
self.handle.next()
110110
}
111111

@@ -128,7 +128,7 @@ impl<'a, T: Timestamp, D: Container, P: Pull<BundleCore<T, D>>+'a> FrontieredInp
128128
/// });
129129
/// ```
130130
#[inline]
131-
pub fn for_each<F: FnMut(CapabilityRef<T>, RefOrMut<D>)>(&mut self, logic: F) {
131+
pub fn for_each<F: FnMut(InputCapability<T>, RefOrMut<D>)>(&mut self, logic: F) {
132132
self.handle.for_each(logic)
133133
}
134134

timely/src/dataflow/operators/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,4 @@ pub mod count;
6262

6363
// keep "mint" module-private
6464
mod capability;
65-
pub use self::capability::{ActivateCapability, Capability, CapabilityRef, CapabilitySet, DowngradeError};
65+
pub use self::capability::{ActivateCapability, Capability, InputCapability, CapabilitySet, DowngradeError};

0 commit comments

Comments
 (0)