From 9fa5ba49fe1ef3f8d53b9bc07da0430652efdc6d Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Sat, 12 Mar 2022 15:38:28 -0500 Subject: [PATCH] Add a tuple timestamp --- timely/src/order.rs | 192 +++++++++++++++++++++++++++----------------- 1 file changed, 120 insertions(+), 72 deletions(-) diff --git a/timely/src/order.rs b/timely/src/order.rs index 0564c0a62..6132c5e89 100644 --- a/timely/src/order.rs +++ b/timely/src/order.rs @@ -25,6 +25,15 @@ pub trait PartialOrder : Eq { /// and other sanity-maintaining operations. pub trait TotalOrder : PartialOrder { } +/// A type that does not affect total orderedness. +/// +/// This trait is not useful, but must be made public and documented or else Rust +/// complains about its existence in the constraints on the implementation of +/// public traits for public types. +pub trait Empty : PartialOrder { } + +impl Empty for () { } + macro_rules! implement_partial { ($($index_type:ty,)*) => ( $( @@ -47,93 +56,132 @@ macro_rules! implement_total { implement_partial!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, (), ::std::time::Duration,); implement_total!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, (), ::std::time::Duration,); +pub use product::Product; +/// A pair of timestamps, partially ordered by the product order. +mod product { + /// A nested pair of timestamps, one outer and one inner. + /// + /// We use `Product` rather than `(TOuter, TInner)` so that we can derive our own `PartialOrder`, + /// because Rust just uses the lexicographic total order. + #[derive(Abomonation, Copy, Clone, Hash, Eq, PartialEq, Default, Ord, PartialOrd, Serialize, Deserialize)] + pub struct Product { + /// Outer timestamp. + pub outer: TOuter, + /// Inner timestamp. + pub inner: TInner, + } -use std::fmt::{Formatter, Error, Debug}; - -use crate::progress::Timestamp; -use crate::progress::timestamp::Refines; - -impl Refines for Product { - fn to_inner(other: TOuter) -> Self { - Product::new(other, TInner::minimum()) + impl Product { + /// Creates a new product from outer and inner coordinates. + pub fn new(outer: TOuter, inner: TInner) -> Self { + Product { + outer, + inner, + } + } } - fn to_outer(self: Product) -> TOuter { - self.outer + + // Debug implementation to avoid seeing fully qualified path names. + use std::fmt::{Formatter, Error, Debug}; + impl Debug for Product { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + f.write_str(&format!("({:?}, {:?})", self.outer, self.inner)) + } } - fn summarize(path: ::Summary) -> ::Summary { - path.outer + + use super::PartialOrder; + impl PartialOrder for Product { + #[inline] + fn less_equal(&self, other: &Self) -> bool { + self.outer.less_equal(&other.outer) && self.inner.less_equal(&other.inner) + } } -} -/// A nested pair of timestamps, one outer and one inner. -/// -/// We use `Product` rather than `(TOuter, TInner)` so that we can derive our own `PartialOrd`, -/// because Rust just uses the lexicographic total order. -#[derive(Abomonation, Copy, Clone, Hash, Eq, PartialEq, Default, Ord, PartialOrd, Serialize, Deserialize)] -pub struct Product { - /// Outer timestamp. - pub outer: TOuter, - /// Inner timestamp. - pub inner: TInner, -} + use crate::progress::Timestamp; + impl Timestamp for Product { + type Summary = Product; + fn minimum() -> Self { Self { outer: TOuter::minimum(), inner: TInner::minimum() }} + } -impl Product { - /// Creates a new product from outer and inner coordinates. - pub fn new(outer: TOuter, inner: TInner) -> Product { - Product { - outer, - inner, + use crate::progress::timestamp::PathSummary; + impl PathSummary> for Product { + #[inline] + fn results_in(&self, product: &Product) -> Option> { + self.outer.results_in(&product.outer) + .and_then(|outer| + self.inner.results_in(&product.inner) + .map(|inner| Product::new(outer, inner)) + ) + } + #[inline] + fn followed_by(&self, other: &Self) -> Option { + self.outer.followed_by(&other.outer) + .and_then(|outer| + self.inner.followed_by(&other.inner) + .map(|inner| Product::new(outer, inner)) + ) } } -} -/// Debug implementation to avoid seeing fully qualified path names. -impl Debug for Product { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - f.write_str(&format!("({:?}, {:?})", self.outer, self.inner)) + use crate::progress::timestamp::Refines; + impl Refines for Product { + fn to_inner(other: TOuter) -> Self { + Product::new(other, TInner::minimum()) + } + fn to_outer(self: Product) -> TOuter { + self.outer + } + fn summarize(path: ::Summary) -> ::Summary { + path.outer + } } -} -impl PartialOrder for Product { - #[inline] - fn less_equal(&self, other: &Self) -> bool { - self.outer.less_equal(&other.outer) && self.inner.less_equal(&other.inner) - } + use super::{Empty, TotalOrder}; + impl Empty for Product { } + impl TotalOrder for Product where T1: Empty, T2: TotalOrder { } } -impl Timestamp for Product { - type Summary = Product; - fn minimum() -> Self { Product { outer: TOuter::minimum(), inner: TInner::minimum() }} -} +/// Rust tuple ordered by the lexicographic order. +mod tuple { -use crate::progress::timestamp::PathSummary; -impl PathSummary> for Product { - #[inline] - fn results_in(&self, product: &Product) -> Option> { - self.outer.results_in(&product.outer) - .and_then(|outer| - self.inner.results_in(&product.inner) - .map(|inner| Product::new(outer, inner)) - ) - } - #[inline] - fn followed_by(&self, other: &Product) -> Option> { - self.outer.followed_by(&other.outer) - .and_then(|outer| - self.inner.followed_by(&other.inner) - .map(|inner| Product::new(outer, inner)) - ) + use super::PartialOrder; + impl PartialOrder for (TOuter, TInner) { + #[inline] + fn less_equal(&self, other: &Self) -> bool { + // We avoid Rust's `PartialOrd` implementation, for reasons of correctness. + self.0.less_than(&other.0) || (self.0.eq(&other.0) && self.1.less_equal(&other.1)) + } } -} -/// A type that does not affect total orderedness. -/// -/// This trait is not useful, but must be made public and documented or else Rust -/// complains about its existence in the constraints on the implementation of -/// public traits for public types. -pub trait Empty : PartialOrder { } + use super::TotalOrder; + impl TotalOrder for (T1, T2) where T1: TotalOrder, T2: TotalOrder { } -impl Empty for () { } -impl Empty for Product { } + use crate::progress::Timestamp; + impl Timestamp for (TOuter, TInner) { + type Summary = (TOuter::Summary, TInner::Summary); + fn minimum() -> Self { (TOuter::minimum(), TInner::minimum()) } + } -impl TotalOrder for Product where T1: Empty, T2: TotalOrder { } + use crate::progress::timestamp::PathSummary; + impl PathSummary<(TOuter, TInner)> for (TOuter::Summary, TInner::Summary) { + #[inline] + fn results_in(&self, (outer, inner): &(TOuter, TInner)) -> Option<(TOuter, TInner)> { + self.0.results_in(outer) + .and_then(|outer| + self.1.results_in(inner) + .map(|inner| (outer, inner)) + ) + } + #[inline] + fn followed_by(&self, (outer, inner): &(TOuter::Summary, TInner::Summary)) -> Option<(TOuter::Summary, TInner::Summary)> { + self.0.followed_by(outer) + .and_then(|outer| + self.1.followed_by(inner) + .map(|inner| (outer, inner)) + ) + } + } + + use super::Empty; + impl Empty for (T1, T2) { } +}