From 545b4ea075f405c25e991dca18ff418759fbe002 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Thu, 17 Aug 2023 13:09:42 -0700 Subject: [PATCH 01/75] encode locations as HList --- chorus_derive/src/lib.rs | 2 +- chorus_lib/examples/bookseller2.rs | 33 ++--- chorus_lib/examples/tic-tac-toe.rs | 20 +-- chorus_lib/src/core.rs | 210 +++++++++++++++++++++-------- 4 files changed, 185 insertions(+), 80 deletions(-) diff --git a/chorus_derive/src/lib.rs b/chorus_derive/src/lib.rs index 3f7278d..371427e 100644 --- a/chorus_derive/src/lib.rs +++ b/chorus_derive/src/lib.rs @@ -7,7 +7,7 @@ pub fn derive_choreography_location(input: TokenStream) -> TokenStream { let DeriveInput { ident, .. } = parse_macro_input!(input); let output = quote! { impl ChoreographyLocation for #ident { - fn name(&self) -> &'static str { + fn name() -> &'static str { stringify!(#ident) } } diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 9b73086..437452c 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -3,6 +3,7 @@ extern crate chorus_lib; use std::thread; use std::{collections::HashMap, sync::Arc}; +use chorus_lib::hlist; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, transport::local::LocalTransport, @@ -35,7 +36,8 @@ impl Decider for OneBuyerDecider { } impl Choreography> for OneBuyerDecider { - fn run(self, op: &impl ChoreoOp) -> Located { + type L = hlist!(Buyer1, Buyer2); + fn run(self, op: &impl ChoreoOp) -> Located { let price = op.broadcast(Buyer1, self.price); return op.locally(Buyer1, |_| { const BUYER1_BUDGET: i32 = 100; @@ -55,7 +57,8 @@ impl Decider for TwoBuyerDecider { } impl Choreography> for TwoBuyerDecider { - fn run(self, op: &impl ChoreoOp) -> Located { + type L = hlist!(Buyer1, Buyer2); + fn run(self, op: &impl ChoreoOp) -> Located { let remaining = op.locally(Buyer1, |un| { const BUYER1_BUDGET: i32 = 100; return un.unwrap(&self.price) - BUYER1_BUDGET; @@ -76,10 +79,11 @@ struct BooksellerChoreography>> { title: Located, } -impl> + Decider> +impl, L = hlist!(Buyer1, Buyer2)> + Decider> Choreography, Buyer1>> for BooksellerChoreography { - fn run(self, op: &impl ChoreoOp) -> Located, Buyer1> { + type L = hlist!(Buyer1, Buyer2, Seller); + fn run(self, op: &impl ChoreoOp) -> Located, Buyer1> { let title_at_seller = op.comm(Buyer1, Seller, &self.title); let price_at_seller = op.locally(Seller, |un| { let inventory = un.unwrap(&self.inventory); @@ -90,8 +94,7 @@ impl> + Decider> return i32::MAX; }); let price_at_buyer1 = op.comm(Seller, Buyer1, &price_at_seller); - let decision_at_buyer1 = - op.colocally(&[Buyer1.name(), Buyer2.name()], D::new(price_at_buyer1)); + let decision_at_buyer1 = op.colocally(D::new(price_at_buyer1)); struct GetDeliveryDateChoreography { inventory: Located, @@ -99,7 +102,8 @@ impl> + Decider> decision_at_buyer1: Located, } impl Choreography, Buyer1>> for GetDeliveryDateChoreography { - fn run(self, op: &impl ChoreoOp) -> Located, Buyer1> { + type L = hlist!(Buyer1, Seller); + fn run(self, op: &impl ChoreoOp) -> Located, Buyer1> { let decision = op.broadcast(Buyer1, self.decision_at_buyer1); if decision { let delivery_date_at_seller = op.locally(Seller, |un| { @@ -116,14 +120,11 @@ impl> + Decider> } } - return op.colocally( - &[Seller.name(), Buyer1.name()], - GetDeliveryDateChoreography { - inventory: self.inventory.clone(), - title_at_seller: title_at_seller.clone(), - decision_at_buyer1, - }, - ); + return op.colocally(GetDeliveryDateChoreography { + inventory: self.inventory.clone(), + title_at_seller: title_at_seller.clone(), + decision_at_buyer1, + }); } } @@ -141,7 +142,7 @@ fn main() { i }; - let transport = LocalTransport::from(&[Seller.name(), Buyer1.name(), Buyer2.name()]); + let transport = LocalTransport::from(&[Seller::name(), Buyer1::name(), Buyer2::name()]); let seller_projector = Arc::new(Projector::new(Seller, transport.clone())); let buyer1_projector = Arc::new(Projector::new(Buyer1, transport.clone())); let buyer2_projector = Arc::new(Projector::new(Buyer2, transport.clone())); diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index eab462a..456c643 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -2,7 +2,10 @@ extern crate chorus_lib; use chorus_lib::{ - core::{Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize}, + core::{ + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, + }, + hlist, transport::http::HttpTransport, }; use clap::Parser; @@ -225,7 +228,8 @@ struct TicTacToeChoreography { } impl Choreography for TicTacToeChoreography { - fn run(self, op: &impl chorus_lib::core::ChoreoOp) -> () { + type L = hlist!(PlayerX, PlayerO); + fn run(self, op: &impl ChoreoOp) -> () { let mut board = Board::new(); loop { let board_x = op.locally(PlayerX, |un| { @@ -289,12 +293,12 @@ fn main() { match args.player { 'X' => { let mut config = HashMap::new(); - config.insert(PlayerX.name(), (args.hostname.as_str(), args.port)); + config.insert(PlayerX::name(), (args.hostname.as_str(), args.port)); config.insert( - PlayerO.name(), + PlayerO::name(), (args.opponent_hostname.as_str(), args.opponent_port), ); - let transport = HttpTransport::new(PlayerX.name(), &config); + let transport = HttpTransport::new(PlayerX::name(), &config); let projector = Projector::new(PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.local(brain), @@ -303,12 +307,12 @@ fn main() { } 'O' => { let mut config = HashMap::new(); - config.insert(PlayerO.name(), (args.hostname.as_str(), args.port)); + config.insert(PlayerO::name(), (args.hostname.as_str(), args.port)); config.insert( - PlayerX.name(), + PlayerX::name(), (args.opponent_hostname.as_str(), args.opponent_port), ); - let transport = HttpTransport::new(PlayerO.name(), &config); + let transport = HttpTransport::new(PlayerO::name(), &config); let projector = Projector::new(PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.remote(PlayerX), diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 5a37956..b5a2f5c 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -11,7 +11,7 @@ pub use serde::{Deserialize, Serialize}; /// Represents a location. It can be derived using `#[derive(ChoreographyLocation)]`. pub trait ChoreographyLocation: Copy { /// Returns the name of the location as a string. - fn name(&self) -> &'static str; + fn name() -> &'static str; } /// Represents a value that can be used in a choreography. ChoRus uses [serde](https://serde.rs/) to serialize and deserialize values. @@ -89,6 +89,74 @@ where } } +// --- HList and Helpers --- + +/// heterogeneous list +pub trait HList { + /// returns + fn to_string_list() -> Vec<&'static str>; +} +/// end of HList +pub struct HNil; +/// An element of HList +pub struct HCons(Head, Tail); + +impl HList for HNil { + fn to_string_list() -> Vec<&'static str> { + Vec::new() + } +} +impl HList for HCons +where + Head: ChoreographyLocation, + Tail: HList, +{ + fn to_string_list() -> Vec<&'static str> { + let mut v = Tail::to_string_list(); + v.push(Head::name()); + v + } +} + +///x +#[macro_export] +macro_rules! hlist { + () => { $crate::core::HNil }; + ($head:ty $(,)*) => { $crate::core::HCons<$head, $crate::core::HNil> }; + ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, hlist!($($tail)*)> }; +} + +/// Marker +pub struct Here; +/// Marker +pub struct There(Index); + +/// Check membership +pub trait Member {} + +impl Member, Here> for Head {} + +impl Member>, There> + for X +where + X: Member, TailIndex>, +{ +} + +/// Check subset +pub trait Subset {} + +// Base case: HNil is a subset of any set +impl Subset for HNil {} + +// Recursive case +impl Subset> for HCons +where + Head: Member, + Tail: Subset, +{ +} + /// Provides a method to work with located values at the current location pub struct Unwrapper { phantom: PhantomData, @@ -105,7 +173,7 @@ impl Unwrapper { /// /// The trait provides methods to work with located values. An implementation of the trait is "injected" into /// a choreography at runtime and provides the actual implementation of the operators. -pub trait ChoreoOp { +pub trait ChoreoOp { /// Performs a computation at the specified location. /// /// `locally` performs a computation at a location, which are specified by `location` and `computation`, respectively. @@ -114,37 +182,49 @@ pub trait ChoreoOp { /// - `computation` is a function that takes an `Unwrapper`. Using the `Unwrapper`, the function can access located values at the location. /// /// The `computation` can return a value of type `V` and the value will be stored in a `Located` struct at the choreography level. - fn locally( + fn locally( &self, location: L1, computation: impl Fn(Unwrapper) -> V, - ) -> Located; + ) -> Located + where + L1: Member; /// Performs a communication between two locations. /// /// `comm` sends `data` from `sender` to `receiver`. The `data` must be a `Located` struct at the `sender` location /// and the value type must implement `Portable`. - fn comm( + fn comm( &self, sender: L1, receiver: L2, data: &Located, - ) -> Located; + ) -> Located + where + L1: Member, + L2: Member; /// Performs a broadcast from a location to all other locations. /// /// `broadcast` broadcasts `data` from `sender` to all other locations. The `data` must be a `Located` struct at the `sender` location. /// The method returns the non-located value. - fn broadcast( + fn broadcast( &self, sender: L1, data: Located, - ) -> V; + ) -> V + where + L1: Member; /// Calls a choreography. - fn call>(&self, choreo: C) -> R; + fn call>(&self, choreo: C) -> R; /// Calls a choreography on a subset of locations. - fn colocally>(&self, locations: &[&str], choreo: C) -> R; + fn colocally, Index>( + &self, + choreo: C, + ) -> R + where + S: Subset; } /// Represents a choreography. @@ -155,12 +235,14 @@ pub trait ChoreoOp { /// /// The trait provides a method `run` that takes an implementation of `ChoreoOp` and returns a value of type `R`. pub trait Choreography { + /// Locations + type L: HList; /// A method that executes a choreography. /// /// The method takes an implementation of `ChoreoOp`. Inside the method, you can use the operators provided by `ChoreoOp` to define a choreography. /// /// The method returns a value of type `R`, which is the return type of the choreography. - fn run(self, op: &impl ChoreoOp) -> R; + fn run(self, op: &impl ChoreoOp) -> R; } /// Provides methods to send and receive messages. @@ -204,7 +286,7 @@ impl Projector { /// Note that the method panics at runtime if the projection target and the location of the value are the same. pub fn remote(&self, l2: L2) -> Located { // NOTE(shumbo): Ideally, this check should be done at the type level. - if self.target.name() == l2.name() { + if L1::name() == L2::name() { panic!("Cannot create a remote value at the same location"); } Located::remote() @@ -218,19 +300,20 @@ impl Projector { } /// Performs end-point projection and runs a choreography. - pub fn epp_and_run<'a, V, C: Choreography>(&'a self, choreo: C) -> V { - struct EppOp<'a, B: Transport> { + pub fn epp_and_run<'a, V, L: HList, C: Choreography>(&'a self, choreo: C) -> V { + struct EppOp<'a, L: HList, B: Transport> { target: String, transport: &'a B, locations: Vec, + marker: PhantomData, } - impl<'a, B: Transport> ChoreoOp for EppOp<'a, B> { - fn locally( + impl<'a, L: HList, B: Transport> ChoreoOp for EppOp<'a, L, B> { + fn locally( &self, - location: L1, + _location: L1, computation: impl Fn(Unwrapper) -> V, ) -> Located { - if location.name() == self.target { + if L1::name() == self.target { let unwrapper = Unwrapper { phantom: PhantomData, }; @@ -241,33 +324,36 @@ impl Projector { } } - fn comm( + fn comm< + L1: ChoreographyLocation, + L2: ChoreographyLocation, + V: Portable, + Index1, + Index2, + >( &self, - sender: L1, - receiver: L2, + _sender: L1, + _receiver: L2, data: &Located, ) -> Located { - if sender.name() == self.target { - self.transport.send( - sender.name(), - receiver.name(), - data.value.as_ref().unwrap(), - ); + if L1::name() == self.target { + self.transport + .send(L1::name(), L2::name(), data.value.as_ref().unwrap()); Located::remote() - } else if receiver.name() == self.target { - let value = self.transport.receive(sender.name(), receiver.name()); + } else if L2::name() == self.target { + let value = self.transport.receive(L1::name(), L2::name()); Located::local(value) } else { Located::remote() } } - fn broadcast( + fn broadcast( &self, - sender: L1, + _sender: L1, data: Located, ) -> V { - if sender.name() == self.target { + if L1::name() == self.target { for dest in &self.locations { if self.target != *dest { self.transport.send(&self.target, &dest, &data.value); @@ -275,49 +361,56 @@ impl Projector { } return data.value.unwrap(); } else { - self.transport.receive(sender.name(), &self.target) + self.transport.receive(L1::name(), &self.target) } } - fn call>(&self, choreo: C) -> T { + fn call>(&self, choreo: C) -> T { choreo.run(self) } - fn colocally>( + fn colocally, Index>( &self, - locs: &[&str], choreo: C, - ) -> T { - let locs_vec = Vec::from_iter(locs.into_iter().map(|s| s.to_string())); + ) -> R { + let locs_vec = + Vec::from_iter(S::to_string_list().into_iter().map(|s| s.to_string())); + for location in &locs_vec { let op = EppOp { target: location.clone(), transport: self.transport, locations: locs_vec.clone(), + marker: PhantomData::, }; if *location == self.target.to_string() { return choreo.run(&op); } } - T::remote() + R::remote() } } - let op: EppOp<'a, B> = EppOp { - target: self.target.name().to_string(), + let op: EppOp<'a, L, B> = EppOp { + target: L1::name().to_string(), transport: &self.transport, locations: self.transport.locations(), + marker: PhantomData::, }; choreo.run(&op) } } /// Provides a method to run a choreography without end-point projection. -pub struct Runner; +pub struct Runner { + marker: PhantomData, +} -impl Runner { +impl Runner { /// Constructs a runner. pub fn new() -> Self { - Runner + Runner { + marker: PhantomData::, + } } /// Constructs a located value. @@ -335,10 +428,10 @@ impl Runner { } /// Runs a choreography directly - pub fn run<'a, V, C: Choreography>(&'a self, choreo: C) -> V { - struct RunOp; - impl ChoreoOp for RunOp { - fn locally( + pub fn run<'a, V, C: Choreography>(&'a self, choreo: C) -> V { + struct RunOp(PhantomData); + impl ChoreoOp for RunOp { + fn locally( &self, _location: L1, computation: impl Fn(Unwrapper) -> V, @@ -350,7 +443,13 @@ impl Runner { Located::local(value) } - fn comm( + fn comm< + L1: ChoreographyLocation, + L2: ChoreographyLocation, + V: Portable, + Index1, + Index2, + >( &self, _sender: L1, _receiver: L2, @@ -362,7 +461,7 @@ impl Runner { Located::local(serde_json::from_str(s.as_str()).unwrap()) } - fn broadcast( + fn broadcast( &self, _sender: L1, data: Located, @@ -370,19 +469,20 @@ impl Runner { data.value.unwrap() } - fn call>(&self, choreo: C) -> R { + fn call>(&self, choreo: C) -> R { choreo.run(self) } - fn colocally>( + fn colocally, Index>( &self, - _locations: &[&str], choreo: C, ) -> R { - choreo.run(self) + let op = RunOp::(PhantomData); + choreo.run(&op) } } - choreo.run(&RunOp) + let op: RunOp = RunOp(PhantomData); + choreo.run(&op) } } From b9518ad479dddff83f16566a0f4f41c9ff9711c6 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Tue, 22 Aug 2023 11:27:35 -0700 Subject: [PATCH 02/75] add location polymorphism example --- chorus_lib/examples/loc-poly.rs | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 chorus_lib/examples/loc-poly.rs diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs new file mode 100644 index 0000000..3b6c19b --- /dev/null +++ b/chorus_lib/examples/loc-poly.rs @@ -0,0 +1,71 @@ +extern crate chorus_lib; +use std::marker::PhantomData; +use std::thread; + +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, HList, Member, Projector}; +use chorus_lib::hlist; +use chorus_lib::transport::local::LocalTransport; + +#[derive(ChoreographyLocation)] +struct Alice; +#[derive(ChoreographyLocation)] +struct Bob; +#[derive(ChoreographyLocation)] +struct Carol; + +struct LocationPolymorphicChoreography +where + L1: Member, +{ + index: PhantomData, + phantom: PhantomData, + location: L1, +} + +impl Choreography + for LocationPolymorphicChoreography +where + L1: Member, +{ + type L = L; + fn run(self, op: &impl ChoreoOp) { + op.locally(self.location, |_| { + println!("Hello, World at {:?}", L1::name()); + }); + } +} + +fn main() { + let transport = LocalTransport::from(&[Alice::name(), Bob::name(), Carol::name()]); + + let mut handles = vec![]; + { + let transport = transport.clone(); + let alice_say_hello: LocationPolymorphicChoreography = + LocationPolymorphicChoreography { + location: Alice, + phantom: PhantomData, + index: PhantomData, + }; + handles.push(thread::spawn(|| { + let p = Projector::new(Alice, transport); + p.epp_and_run(alice_say_hello); + })); + } + { + let transport = transport.clone(); + let bob_say_hello: LocationPolymorphicChoreography = + LocationPolymorphicChoreography { + location: Bob, + phantom: PhantomData, + index: PhantomData, + }; + handles.push(thread::spawn(|| { + let p = Projector::new(Bob, transport); + p.epp_and_run(bob_say_hello); + })); + } + for h in handles { + h.join().unwrap(); + } +} From cd7ac293862f32dbfb8f4569edfc0a2633ce24bf Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 30 Aug 2023 21:13:00 -0700 Subject: [PATCH 03/75] add `Equal` trait, correct signature for `call` --- chorus_lib/src/core.rs | 97 ++++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 28 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index b5a2f5c..979e210 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -118,7 +118,9 @@ where } } -///x +// TODO(shumbo): Export the macro under the `core` module + +/// Macro to generate hlist #[macro_export] macro_rules! hlist { () => { $crate::core::HNil }; @@ -132,15 +134,24 @@ pub struct Here; pub struct There(Index); /// Check membership -pub trait Member {} - -impl Member, Here> for Head {} +pub trait Member { + /// Return HList of non-member + type Remainder: HList; +} +impl Member, Here> for Head +where + Tail: HList, +{ + type Remainder = Tail; +} impl Member>, There> for X where + Head: ChoreographyLocation, X: Member, TailIndex>, { + type Remainder = HCons; } /// Check subset @@ -156,6 +167,21 @@ where Tail: Subset, { } +/// Equal +pub trait Equal {} + +// Base case: HNil is equal to HNil +impl Equal for HNil {} + +// Recursive case: Head::Tail is equal to L if +// 1. Head is a member of L +// 2. Tail is equal to the remainder of L +impl Equal> for HCons +where + Head: Member, + Tail: Equal, +{ +} /// Provides a method to work with located values at the current location pub struct Unwrapper { @@ -216,7 +242,9 @@ pub trait ChoreoOp { L1: Member; /// Calls a choreography. - fn call>(&self, choreo: C) -> R; + fn call, Index>(&self, choreo: C) -> R + where + M: HList + Equal; /// Calls a choreography on a subset of locations. fn colocally, Index>( @@ -301,19 +329,19 @@ impl Projector { /// Performs end-point projection and runs a choreography. pub fn epp_and_run<'a, V, L: HList, C: Choreography>(&'a self, choreo: C) -> V { - struct EppOp<'a, L: HList, B: Transport> { - target: String, + struct EppOp<'a, L: HList, L1: ChoreographyLocation, B: Transport> { + target: PhantomData, transport: &'a B, locations: Vec, marker: PhantomData, } - impl<'a, L: HList, B: Transport> ChoreoOp for EppOp<'a, L, B> { + impl<'a, L: HList, T: ChoreographyLocation, B: Transport> ChoreoOp for EppOp<'a, L, T, B> { fn locally( &self, _location: L1, computation: impl Fn(Unwrapper) -> V, ) -> Located { - if L1::name() == self.target { + if L1::name() == T::name() { let unwrapper = Unwrapper { phantom: PhantomData, }; @@ -336,11 +364,11 @@ impl Projector { _receiver: L2, data: &Located, ) -> Located { - if L1::name() == self.target { + if L1::name() == T::name() { self.transport .send(L1::name(), L2::name(), data.value.as_ref().unwrap()); Located::remote() - } else if L2::name() == self.target { + } else if L2::name() == T::name() { let value = self.transport.receive(L1::name(), L2::name()); Located::local(value) } else { @@ -353,20 +381,29 @@ impl Projector { _sender: L1, data: Located, ) -> V { - if L1::name() == self.target { + if L1::name() == T::name() { for dest in &self.locations { - if self.target != *dest { - self.transport.send(&self.target, &dest, &data.value); + if T::name() != *dest { + self.transport.send(&T::name(), &dest, &data.value); } } return data.value.unwrap(); } else { - self.transport.receive(L1::name(), &self.target) + self.transport.receive(L1::name(), &T::name()) } } - fn call>(&self, choreo: C) -> T { - choreo.run(self) + fn call, Index>(&self, choreo: C) -> R + where + M: HList + Equal, + { + let op: EppOp<'a, M, T, B> = EppOp { + target: PhantomData::, + transport: &self.transport, + locations: self.transport.locations(), + marker: PhantomData::, + }; + choreo.run(&op) } fn colocally, Index>( @@ -377,21 +414,21 @@ impl Projector { Vec::from_iter(S::to_string_list().into_iter().map(|s| s.to_string())); for location in &locs_vec { - let op = EppOp { - target: location.clone(), - transport: self.transport, - locations: locs_vec.clone(), - marker: PhantomData::, - }; - if *location == self.target.to_string() { + if *location == T::name().to_string() { + let op = EppOp { + target: PhantomData::, + transport: self.transport, + locations: locs_vec.clone(), + marker: PhantomData::, + }; return choreo.run(&op); } } R::remote() } } - let op: EppOp<'a, L, B> = EppOp { - target: L1::name().to_string(), + let op: EppOp<'a, L, L1, B> = EppOp { + target: PhantomData::, transport: &self.transport, locations: self.transport.locations(), marker: PhantomData::, @@ -469,8 +506,12 @@ impl Runner { data.value.unwrap() } - fn call>(&self, choreo: C) -> R { - choreo.run(self) + fn call, Index>(&self, choreo: C) -> R + where + M: HList + Equal, + { + let op: RunOp = RunOp(PhantomData); + choreo.run(&op) } fn colocally, Index>( From ae97f02d429e4ef9c11d769b7aec202c51ad09fa Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 30 Aug 2023 21:32:39 -0700 Subject: [PATCH 04/75] update location polymorphic example --- chorus_lib/examples/loc-poly.rs | 70 +++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 3b6c19b..7b46924 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -1,8 +1,10 @@ extern crate chorus_lib; -use std::marker::PhantomData; +use std::fmt::Debug; use std::thread; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, HList, Member, Projector}; +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, +}; use chorus_lib::hlist; use chorus_lib::transport::local::LocalTransport; @@ -13,24 +15,43 @@ struct Bob; #[derive(ChoreographyLocation)] struct Carol; -struct LocationPolymorphicChoreography -where - L1: Member, -{ - index: PhantomData, - phantom: PhantomData, - location: L1, +struct CommAndPrint { + sender: L1, + receiver: L2, + data: Located, } -impl Choreography - for LocationPolymorphicChoreography +impl Choreography> for CommAndPrint where - L1: Member, + V: Portable + Debug, + L1: ChoreographyLocation, + L2: ChoreographyLocation, { - type L = L; - fn run(self, op: &impl ChoreoOp) { - op.locally(self.location, |_| { - println!("Hello, World at {:?}", L1::name()); + type L = hlist!(L1, L2); + fn run(self, op: &impl ChoreoOp) -> Located { + let v = op.comm(self.sender, self.receiver, &self.data); + op.locally(self.receiver, |un| println!("{:?}", un.unwrap(&v))); + v + } +} + +struct MainChoreography; + +impl Choreography> for MainChoreography { + type L = hlist!(Alice, Bob); + + fn run(self, op: &impl ChoreoOp) -> Located { + let v1 = op.locally(Alice, |_| 100); + let v2 = op.call(CommAndPrint { + sender: Alice, + receiver: Bob, + data: v1, + }); + let v2 = op.locally(Bob, |un| un.unwrap(&v2) + 10); + return op.colocally(CommAndPrint { + sender: Bob, + receiver: Alice, + data: v2, }); } } @@ -41,28 +62,17 @@ fn main() { let mut handles = vec![]; { let transport = transport.clone(); - let alice_say_hello: LocationPolymorphicChoreography = - LocationPolymorphicChoreography { - location: Alice, - phantom: PhantomData, - index: PhantomData, - }; handles.push(thread::spawn(|| { let p = Projector::new(Alice, transport); - p.epp_and_run(alice_say_hello); + let v = p.epp_and_run(MainChoreography); + assert_eq!(p.unwrap(v), 110); })); } { let transport = transport.clone(); - let bob_say_hello: LocationPolymorphicChoreography = - LocationPolymorphicChoreography { - location: Bob, - phantom: PhantomData, - index: PhantomData, - }; handles.push(thread::spawn(|| { let p = Projector::new(Bob, transport); - p.epp_and_run(bob_say_hello); + p.epp_and_run(MainChoreography); })); } for h in handles { From 1a83cf8c486d170e8e06d652e9a70c19f71dd05c Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 14 Sep 2023 04:52:40 -0700 Subject: [PATCH 05/75] Remove Equal trait --- chorus_lib/src/core.rs | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 979e210..6153286 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -167,21 +167,6 @@ where Tail: Subset, { } -/// Equal -pub trait Equal {} - -// Base case: HNil is equal to HNil -impl Equal for HNil {} - -// Recursive case: Head::Tail is equal to L if -// 1. Head is a member of L -// 2. Tail is equal to the remainder of L -impl Equal> for HCons -where - Head: Member, - Tail: Equal, -{ -} /// Provides a method to work with located values at the current location pub struct Unwrapper { @@ -242,9 +227,9 @@ pub trait ChoreoOp { L1: Member; /// Calls a choreography. - fn call, Index>(&self, choreo: C) -> R + fn call>(&self, choreo: C) -> R where - M: HList + Equal; + M: HList; /// Calls a choreography on a subset of locations. fn colocally, Index>( @@ -393,9 +378,9 @@ impl Projector { } } - fn call, Index>(&self, choreo: C) -> R + fn call>(&self, choreo: C) -> R where - M: HList + Equal, + M: HList, { let op: EppOp<'a, M, T, B> = EppOp { target: PhantomData::, @@ -506,9 +491,9 @@ impl Runner { data.value.unwrap() } - fn call, Index>(&self, choreo: C) -> R + fn call>(&self, choreo: C) -> R where - M: HList + Equal, + M: HList, { let op: RunOp = RunOp(PhantomData); choreo.run(&op) From e2cab46863adc38c4c9f352f21012b768d0e2097 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 14 Sep 2023 04:58:58 -0700 Subject: [PATCH 06/75] Update examples --- chorus_lib/examples/bookseller.rs | 6 ++++-- chorus_lib/examples/hello.rs | 6 ++++-- chorus_lib/examples/input-output.rs | 5 +++-- chorus_lib/examples/runner.rs | 11 ++++++----- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 4db81fa..3b1bcfa 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -3,6 +3,7 @@ extern crate chorus_lib; use std::io; use std::thread; +use chorus_lib::hlist; use chrono::NaiveDate; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; @@ -26,7 +27,8 @@ struct Buyer; struct BooksellerChoreography; impl Choreography for BooksellerChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Seller, Buyer); + fn run(self, op: &impl ChoreoOp) { let title_at_buyer = op.locally(Buyer, |_| { println!("Enter the title of the book to buy (TAPL or HoTT)"); let mut title = String::new(); @@ -71,7 +73,7 @@ impl Choreography for BooksellerChoreography { } fn main() { - let transport = LocalTransport::from(&[Seller.name(), Buyer.name()]); + let transport = LocalTransport::from(&[Seller::name(), Buyer::name()]); let seller_projector = Projector::new(Seller, transport.clone()); let buyer_projector = Projector::new(Buyer, transport.clone()); diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index 70b65dc..4050505 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -3,6 +3,7 @@ extern crate chorus_lib; use std::thread; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; +use chorus_lib::hlist; use chorus_lib::transport::local::LocalTransport; // --- Define two locations (Alice and Bob) --- @@ -18,7 +19,8 @@ struct HelloWorldChoreography; // Implement the `Choreography` trait for `HelloWorldChoreography` impl Choreography for HelloWorldChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice, Bob); + fn run(self, op: &impl ChoreoOp) { // Create a located value at Alice let msg_at_alice = op.locally(Alice, |_| { println!("Hello from Alice!"); @@ -38,7 +40,7 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a local transport - let transport = LocalTransport::from(&[Alice.name(), Bob.name()]); + let transport = LocalTransport::from(&[Alice::name() , Bob::name()]); // Run the choreography in two threads { let transport = transport.clone(); diff --git a/chorus_lib/examples/input-output.rs b/chorus_lib/examples/input-output.rs index 47c875d..993005a 100644 --- a/chorus_lib/examples/input-output.rs +++ b/chorus_lib/examples/input-output.rs @@ -1,5 +1,5 @@ extern crate chorus_lib; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Located}; +use chorus_lib::{core::{ChoreoOp, Choreography, ChoreographyLocation, Located}, hlist}; #[derive(ChoreographyLocation)] struct Alice; #[derive(ChoreographyLocation)] @@ -12,7 +12,8 @@ struct DemoChoreography { } impl Choreography for DemoChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice); + fn run(self, op: &impl ChoreoOp) { op.locally(Alice, |un| { let s = un.unwrap(&self.input); println!("Alice received: {}", s); diff --git a/chorus_lib/examples/runner.rs b/chorus_lib/examples/runner.rs index 0727c44..ceeae40 100644 --- a/chorus_lib/examples/runner.rs +++ b/chorus_lib/examples/runner.rs @@ -1,7 +1,7 @@ extern crate chorus_lib; -use chorus_lib::core::{ +use chorus_lib::{core::{ ChoreoOp, Choreography, ChoreographyLocation, Located, Runner, Superposition, -}; +}, hlist}; #[derive(ChoreographyLocation)] struct Alice; @@ -25,7 +25,8 @@ struct BobCarolChoreography { } impl Choreography for BobCarolChoreography { - fn run(self, op: &impl ChoreoOp) -> BobCarolResult { + type L = hlist!(Bob, Carol); + fn run(self, op: &impl ChoreoOp) -> BobCarolResult { let is_even_at_bob: Located = op.locally(Bob, |un| { let x = un.unwrap(&self.x_at_bob); x % 2 == 0 @@ -48,14 +49,14 @@ impl Choreography for BobCarolChoreography { struct MainChoreography; impl Choreography for MainChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| get_random_number()); let x_at_bob = op.comm(Alice, Bob, &x_at_alice); let BobCarolResult { is_even_at_bob, is_even_at_carol, } = op.colocally( - &[Bob.name(), Carol.name()], BobCarolChoreography { x_at_bob }, ); op.locally(Bob, |un| { From 3548b474c4a88616bb261e7a9e02267f3e675e98 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 14 Sep 2023 04:59:08 -0700 Subject: [PATCH 07/75] Update book --- chorus_book/src/guide-choreography.md | 46 +++++++++++++++---- chorus_book/src/guide-colocally.md | 22 +++++---- .../src/guide-higher-order-choreography.md | 6 ++- chorus_book/src/guide-input-and-output.md | 24 ++++++---- .../src/guide-location-polymorphism.md | 3 +- chorus_book/src/guide-locations.md | 2 +- chorus_book/src/guide-projector.md | 9 ++-- chorus_book/src/guide-runner.md | 6 ++- chorus_book/src/guide-transport.md | 13 +++--- chorus_book/src/header.txt | 5 +- 10 files changed, 93 insertions(+), 43 deletions(-) diff --git a/chorus_book/src/guide-choreography.md b/chorus_book/src/guide-choreography.md index 1a5db7f..58dec09 100644 --- a/chorus_book/src/guide-choreography.md +++ b/chorus_book/src/guide-choreography.md @@ -9,7 +9,8 @@ struct HelloWorldChoreography; // 2. Implement the `Choreography` trait impl Choreography for HelloWorldChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice); + fn run(self, op: &impl ChoreoOp) { // 3. Use the `op` parameter to access operators op.locally(Alice, |_| { println!("Hello, World!"); @@ -20,6 +21,27 @@ impl Choreography for HelloWorldChoreography { `Choreography` must implement the `run` method which defines the behavior of the system. The `run` method takes a reference to an object that implements the `ChoreoOp` trait. The `ChoreoOp` trait provides choreographic operators such as `locally` and `comm`. +Also, each `Choreography` has an associated type `L`, which is the the list of `ChoreographyLocation`s it can operate on. You'll get a compile error if you try to work with a `ChoreographyLocation` that is not a member of `L`. To build a set of locations, you can use the macro `hlist!`. + +```rust, compile_fail +# {{#include ./header.txt}} +# // 1. Define a struct +# struct HelloWorldChoreography; + +# // 2. Implement the `Choreography` trait +// ... +impl Choreography for HelloWorldChoreography { + type L = hlist!(Alice); + fn run(self, op: &impl ChoreoOp) { + // this will fail + op.locally(Bob, |_| { + println!("Hello, World!"); + }); + } +} +``` + + ## Choreographic Operators Inside the `run` method, you can use the `op` parameter to access choreographic operators. @@ -33,7 +55,8 @@ The `locally` operator is used to perform a computation at a single location. It # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Alice); +# fn run(self, op: &impl ChoreoOp) { op.locally(Alice, |_| { println!("Hello, World!"); }); @@ -48,7 +71,8 @@ The closure can return a value to create a located value. Located values are val # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Alice); +# fn run(self, op: &impl ChoreoOp) { // This value is only available at Alice let num_at_alice: Located = op.locally(Alice, |_| { 42 @@ -64,7 +88,8 @@ The computation closure takes `Unwrapper`. Using the `Unwrapper`, you can get a # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Alice); +# fn run(self, op: &impl ChoreoOp) { let num_at_alice: Located = op.locally(Alice, |_| { 42 }); @@ -79,12 +104,13 @@ op.locally(Alice, |un| { Note that you can unwrap a located value only at the location where the located value is available. If you try to unwrap a located value at a different location, the program will fail to compile. -```rust,compile_fail +```rust, compile_fail {{#include ./header.txt}} # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Alice, Bob); +# fn run(self, op: &impl ChoreoOp) { // This code will fail to compile let num_at_alice = op.locally(Alice, |_| { 42 }); op.locally(Bob, |un| { @@ -106,7 +132,8 @@ The `comm` operator is used to perform a communication between two locations. It # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Alice, Bob); +# fn run(self, op: &impl ChoreoOp) { // This value is only available at Alice let num_at_alice: Located = op.locally(Alice, |_| { 42 @@ -131,7 +158,8 @@ The `broadcast` operator is used to perform a broadcast from a single location t # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Alice); +# fn run(self, op: &impl ChoreoOp) { // This value is only available at Alice let num_at_alice: Located = op.locally(Alice, |_| { 42 @@ -144,7 +172,7 @@ let num: i32 = op.broadcast(Alice, num_at_alice); Because all locations receive the value, the return type of the `broadcast` operator is a normal value, not a located value. This means that the value can be used for control flow. -```rust,ignore +```rust, ignore if num == 42 { println!("The number is 42!"); } else { diff --git a/chorus_book/src/guide-colocally.md b/chorus_book/src/guide-colocally.md index e65beb8..cccf0d6 100644 --- a/chorus_book/src/guide-colocally.md +++ b/chorus_book/src/guide-colocally.md @@ -20,7 +20,8 @@ This protocol can be implemented as follows: struct DemoChoreography; impl Choreography for DemoChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| { get_random_number() }); @@ -51,7 +52,8 @@ struct BobCarolChoreography { x_at_bob: Located, }; impl Choreography for BobCarolChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Bob, Carol); + fn run(self, op: &impl ChoreoOp) { let is_even_at_bob: Located = op.locally(Bob, |un| { let x = un.unwrap(&self.x_at_bob); x % 2 == 0 @@ -79,7 +81,8 @@ Notice that the `BobCarolChoreography` only describes the behavior of Bob and Ca # x_at_bob: Located, # }; # impl Choreography for BobCarolChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Bob, Carol); +# fn run(self, op: &impl ChoreoOp) { # let is_even_at_bob: Located = op.locally(Bob, |un| { # let x = un.unwrap(&self.x_at_bob); # x % 2 == 0 @@ -96,12 +99,13 @@ Notice that the `BobCarolChoreography` only describes the behavior of Bob and Ca # } struct MainChoreography; impl Choreography for MainChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| { get_random_number() }); let x_at_bob = op.comm(Alice, Bob, &x_at_alice); - op.colocally(&[Bob.name(), Carol.name()], BobCarolChoreography { + op.colocally(BobCarolChoreography { x_at_bob, }); } @@ -131,7 +135,8 @@ struct BobCarolChoreography { }; impl Choreography for BobCarolChoreography { - fn run(self, op: &impl ChoreoOp) -> BobCarolResult { + type L = hlist!(Bob, Carol); + fn run(self, op: &impl ChoreoOp) -> BobCarolResult { let is_even_at_bob: Located = op.locally(Bob, |un| { let x = un.unwrap(&self.x_at_bob); x % 2 == 0 @@ -154,7 +159,8 @@ impl Choreography for BobCarolChoreography { struct MainChoreography; impl Choreography for MainChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| { get_random_number() }); @@ -162,7 +168,7 @@ impl Choreography for MainChoreography { let BobCarolResult { is_even_at_bob, is_even_at_carol, - } = op.colocally(&[Bob.name(), Carol.name()], BobCarolChoreography { + } = op.colocally( BobCarolChoreography { x_at_bob, }); // can access is_even_at_bob and is_even_at_carol using `locally` on Bob and Carol diff --git a/chorus_book/src/guide-higher-order-choreography.md b/chorus_book/src/guide-higher-order-choreography.md index 0d75d77..26ee6f1 100644 --- a/chorus_book/src/guide-higher-order-choreography.md +++ b/chorus_book/src/guide-higher-order-choreography.md @@ -23,7 +23,8 @@ When you implement the `Choreography` trait, you have access to the `sub_choreo` # sub_choreo: C, # }; impl Choreography for HigherOrderChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = HNil; + fn run(self, op: &impl ChoreoOp) { op.call(self.sub_choreo); } } @@ -46,7 +47,8 @@ struct HigherOrderChoreography> + SubChoreo }; impl> + SubChoreography> Choreography for HigherOrderChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice); + fn run(self, op: &impl ChoreoOp) { let num_at_alice = op.locally(Alice, |_| { 42 }); diff --git a/chorus_book/src/guide-input-and-output.md b/chorus_book/src/guide-input-and-output.md index 24d177d..69bfb6d 100644 --- a/chorus_book/src/guide-input-and-output.md +++ b/chorus_book/src/guide-input-and-output.md @@ -18,7 +18,8 @@ struct DemoChoreography { } impl Choreography for DemoChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = HNil; + fn run(self, op: &impl ChoreoOp) { println!("Input: {}", self.input); } } @@ -32,7 +33,8 @@ You can construct an instance of the choreography with the input and pass it to # input: String, # } # impl Choreography for DemoChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = HNil; +# fn run(self, op: &impl ChoreoOp) { # println!("Input: {}", self.input); # } # } @@ -55,7 +57,8 @@ struct DemoChoreography { } impl Choreography for DemoChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(Alice); + fn run(self, op: &impl ChoreoOp) { op.locally(Alice, |un| { let input = un.unwrap(&self.input); println!("Input at Alice: {}", input); @@ -81,7 +84,8 @@ To run the sample choreography above at Alice, we use the `local` method to cons # } # # impl Choreography for DemoChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Alice); +# fn run(self, op: &impl ChoreoOp) { # op.locally(Alice, |un| { # let input = un.unwrap(&self.input); # println!("Input at Alice: {}", input); @@ -107,7 +111,8 @@ For Bob, we use the `remote` method to construct the located value. # } # # impl Choreography for DemoChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = hlist!(Alice); +# fn run(self, op: &impl ChoreoOp) { # op.locally(Alice, |un| { # let input = un.unwrap(&self.input); # println!("Input at Alice: {}", input); @@ -135,7 +140,8 @@ To do so, we specify the output type to the `Choreography` trait and return the struct DemoChoreography; impl Choreography for DemoChoreography { - fn run(self, op: &impl ChoreoOp) -> String { + type L = HNil; + fn run(self, op: &impl ChoreoOp) -> String { "Hello, World!".to_string() } } @@ -148,7 +154,8 @@ impl Choreography for DemoChoreography { # struct DemoChoreography; # # impl Choreography for DemoChoreography { -# fn run(self, op: &impl ChoreoOp) -> String { +# type L = hlist!(Alice); +# fn run(self, op: &impl ChoreoOp) -> String { # "Hello, World!".to_string() # } # } @@ -167,7 +174,8 @@ You can use the `Located` as a return type of the `run` method to return struct DemoChoreography; impl Choreography> for DemoChoreography { - fn run(self, op: &impl ChoreoOp) -> Located { + type L = hlist!(Alice); + fn run(self, op: &impl ChoreoOp) -> Located { op.locally(Alice, |_| { "Hello, World!".to_string() }) diff --git a/chorus_book/src/guide-location-polymorphism.md b/chorus_book/src/guide-location-polymorphism.md index 057aec7..ebe7d6a 100644 --- a/chorus_book/src/guide-location-polymorphism.md +++ b/chorus_book/src/guide-location-polymorphism.md @@ -11,7 +11,8 @@ struct LocationPolymorphicChoreography { } impl Choreography for LocationPolymorphicChoreography { - fn run(self, op: &impl ChoreoOp) { + type L = hlist!(L1); + fn run(self, op: &impl ChoreoOp) { op.locally(self.location, |_| { println!("Hello, World!"); }); diff --git a/chorus_book/src/guide-locations.md b/chorus_book/src/guide-locations.md index 8de42e9..3e5c2b8 100644 --- a/chorus_book/src/guide-locations.md +++ b/chorus_book/src/guide-locations.md @@ -26,6 +26,6 @@ The `ChoreographyLocation` trait provides the `name` method, which returns the n # #[derive(ChoreographyLocation)] # struct Bob; # -let name = Alice.name(); +let name = Alice::name(); assert_eq!(name, "Alice"); ``` diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index ba78909..9e41bea 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -10,7 +10,7 @@ To create a `Projector`, you need to provide the location and the transport. # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use chorus_lib::core::{ChoreographyLocation, Projector}; -# let transport = LocalTransport::from(&[Alice.name(), Bob.name()]); +# let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -28,15 +28,16 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro ```rust # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# let transport = LocalTransport::from(&[Alice.name(), Bob.name()]); +# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp, HNil}; +# let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = HNil; +# fn run(self, op: &impl ChoreoOp) { # } # } # diff --git a/chorus_book/src/guide-runner.md b/chorus_book/src/guide-runner.md index f92428c..ffc66ce 100644 --- a/chorus_book/src/guide-runner.md +++ b/chorus_book/src/guide-runner.md @@ -8,7 +8,8 @@ To use `Runner`, construct an instance using the `new` constructor, and then cal {{#include ./header.txt}} # struct DemoChoreography; # impl Choreography for DemoChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = HNil; +# fn run(self, op: &impl ChoreoOp) { # } # } let runner = Runner::new(); @@ -26,7 +27,8 @@ struct SumChoreography { y_at_bob: Located, } impl Choreography> for SumChoreography { - fn run(self, op: &impl ChoreoOp) -> Located { + type L = hlist!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) -> Located { let x_at_carol = op.comm(Alice, Carol, &self.x_at_alice); let y_at_carol = op.comm(Bob, Carol, &self.y_at_bob); op.locally(Carol, |un| { diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index e4ec268..a3ae384 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -25,18 +25,19 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use std::thread; -# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector}; +# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector, HNil}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# fn run(self, op: &impl ChoreoOp) { +# type L = HNil; +# fn run(self, op: &impl ChoreoOp) { # } # } let mut handles: Vec> = Vec::new(); -let transport = LocalTransport::from(&[Alice.name(), Bob.name()]); +let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); { // create a clone for Alice let transport = transport.clone(); @@ -73,9 +74,9 @@ The `new` constructor takes the name of the projection target and "configuration # use chorus_lib::transport::http::HttpTransport; # use std::collections::HashMap; let mut config = HashMap::new(); -config.insert(Alice.name(), ("localhost", 8080)); -config.insert(Bob.name(), ("localhost", 8081)); -let transport = HttpTransport::new(Alice.name(), &config); +config.insert(Alice::name(), ("localhost", 8080)); +config.insert(Bob::name(), ("localhost", 8081)); +let transport = HttpTransport::new(Alice::name(), &config); ``` In the above example, the transport will start the HTTP server on port 8080 on localhost. If Alice needs to send a message to Bob, it will use `http://localhost:8081` as the destination. diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index c5ecb20..5b81e2f 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,10 +1,11 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner, HNil}; # use chorus_lib::transport::local::LocalTransport; +# use chorus_lib::hlist; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # #[derive(ChoreographyLocation)] # struct Carol; -# let transport = LocalTransport::from(&[Alice.name(), Bob.name(), Carol.name()]); \ No newline at end of file +# let transport = LocalTransport::from(&[Alice::name(), Bob::name(), Carol::name()]); \ No newline at end of file From e9b0cf4685aaf347117c6580bc6202d70843a3ac Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 15 Sep 2023 15:54:25 -0700 Subject: [PATCH 08/75] Fix some formatting issues --- chorus_book/src/guide-colocally.md | 2 +- chorus_lib/examples/hello.rs | 2 +- chorus_lib/examples/input-output.rs | 5 ++++- chorus_lib/examples/runner.rs | 11 +++++------ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/chorus_book/src/guide-colocally.md b/chorus_book/src/guide-colocally.md index cccf0d6..7191124 100644 --- a/chorus_book/src/guide-colocally.md +++ b/chorus_book/src/guide-colocally.md @@ -168,7 +168,7 @@ impl Choreography for MainChoreography { let BobCarolResult { is_even_at_bob, is_even_at_carol, - } = op.colocally( BobCarolChoreography { + } = op.colocally(BobCarolChoreography { x_at_bob, }); // can access is_even_at_bob and is_even_at_carol using `locally` on Bob and Carol diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index 4050505..cbf0597 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -40,7 +40,7 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a local transport - let transport = LocalTransport::from(&[Alice::name() , Bob::name()]); + let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); // Run the choreography in two threads { let transport = transport.clone(); diff --git a/chorus_lib/examples/input-output.rs b/chorus_lib/examples/input-output.rs index 993005a..7b77bab 100644 --- a/chorus_lib/examples/input-output.rs +++ b/chorus_lib/examples/input-output.rs @@ -1,5 +1,8 @@ extern crate chorus_lib; -use chorus_lib::{core::{ChoreoOp, Choreography, ChoreographyLocation, Located}, hlist}; +use chorus_lib::{ + core::{ChoreoOp, Choreography, ChoreographyLocation, Located}, + hlist, +}; #[derive(ChoreographyLocation)] struct Alice; #[derive(ChoreographyLocation)] diff --git a/chorus_lib/examples/runner.rs b/chorus_lib/examples/runner.rs index ceeae40..29c9f55 100644 --- a/chorus_lib/examples/runner.rs +++ b/chorus_lib/examples/runner.rs @@ -1,7 +1,8 @@ extern crate chorus_lib; -use chorus_lib::{core::{ - ChoreoOp, Choreography, ChoreographyLocation, Located, Runner, Superposition, -}, hlist}; +use chorus_lib::{ + core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Runner, Superposition}, + hlist, +}; #[derive(ChoreographyLocation)] struct Alice; @@ -56,9 +57,7 @@ impl Choreography for MainChoreography { let BobCarolResult { is_even_at_bob, is_even_at_carol, - } = op.colocally( - BobCarolChoreography { x_at_bob }, - ); + } = op.colocally(BobCarolChoreography { x_at_bob }); op.locally(Bob, |un| { let is_even = un.unwrap(&is_even_at_bob); assert!(is_even); From c94b7257c9bc91428822785e3f71510765a316b1 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 15 Sep 2023 20:53:14 -0700 Subject: [PATCH 09/75] Add subset requirement for call method --- chorus_lib/src/core.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 6153286..f3889ba 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -227,9 +227,9 @@ pub trait ChoreoOp { L1: Member; /// Calls a choreography. - fn call>(&self, choreo: C) -> R + fn call>(&self, choreo: C) -> R where - M: HList; + M: HList + Subset; /// Calls a choreography on a subset of locations. fn colocally, Index>( @@ -378,9 +378,9 @@ impl Projector { } } - fn call>(&self, choreo: C) -> R + fn call>(&self, choreo: C) -> R where - M: HList, + M: HList + Subset, { let op: EppOp<'a, M, T, B> = EppOp { target: PhantomData::, @@ -491,9 +491,9 @@ impl Runner { data.value.unwrap() } - fn call>(&self, choreo: C) -> R + fn call>(&self, choreo: C) -> R where - M: HList, + M: HList + Subset, { let op: RunOp = RunOp(PhantomData); choreo.run(&op) From 1830bb9e1aaac2d42cf8e00dba0a66acaf27feff Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 15 Sep 2023 20:55:52 -0700 Subject: [PATCH 10/75] Fix examples of higher order choreography --- chorus_book/src/guide-higher-order-choreography.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chorus_book/src/guide-higher-order-choreography.md b/chorus_book/src/guide-higher-order-choreography.md index 26ee6f1..588f9e5 100644 --- a/chorus_book/src/guide-higher-order-choreography.md +++ b/chorus_book/src/guide-higher-order-choreography.md @@ -22,8 +22,8 @@ When you implement the `Choreography` trait, you have access to the `sub_choreo` # struct HigherOrderChoreography { # sub_choreo: C, # }; -impl Choreography for HigherOrderChoreography { - type L = HNil; +impl> Choreography for HigherOrderChoreography { + type L = hlist!(Alice, Bob); fn run(self, op: &impl ChoreoOp) { op.call(self.sub_choreo); } @@ -46,7 +46,7 @@ struct HigherOrderChoreography> + SubChoreo _marker: PhantomData, }; -impl> + SubChoreography> Choreography for HigherOrderChoreography { +impl, L = hlist!(Alice)> + SubChoreography> Choreography for HigherOrderChoreography { type L = hlist!(Alice); fn run(self, op: &impl ChoreoOp) { let num_at_alice = op.locally(Alice, |_| { From d5054f45baf8984d7e313caaf88557066d8f1b7c Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 15 Sep 2023 21:03:41 -0700 Subject: [PATCH 11/75] Update choreography guide --- chorus_book/src/guide-choreography.md | 44 +++++++++++++++------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/chorus_book/src/guide-choreography.md b/chorus_book/src/guide-choreography.md index 58dec09..5474ccb 100644 --- a/chorus_book/src/guide-choreography.md +++ b/chorus_book/src/guide-choreography.md @@ -21,26 +21,7 @@ impl Choreography for HelloWorldChoreography { `Choreography` must implement the `run` method which defines the behavior of the system. The `run` method takes a reference to an object that implements the `ChoreoOp` trait. The `ChoreoOp` trait provides choreographic operators such as `locally` and `comm`. -Also, each `Choreography` has an associated type `L`, which is the the list of `ChoreographyLocation`s it can operate on. You'll get a compile error if you try to work with a `ChoreographyLocation` that is not a member of `L`. To build a set of locations, you can use the macro `hlist!`. - -```rust, compile_fail -# {{#include ./header.txt}} -# // 1. Define a struct -# struct HelloWorldChoreography; - -# // 2. Implement the `Choreography` trait -// ... -impl Choreography for HelloWorldChoreography { - type L = hlist!(Alice); - fn run(self, op: &impl ChoreoOp) { - // this will fail - op.locally(Bob, |_| { - println!("Hello, World!"); - }); - } -} -``` - +Also, each `Choreography` has an associated type `L`, which is the set of `ChoreographyLocation`s it can operate on. To build a set of locations, you can use the macro `hlist!`. ## Choreographic Operators @@ -179,3 +160,26 @@ if num == 42 { println!("The number is not 42!"); } ``` + +### Note on invalid values for Choreography::L + +You'll get a compile error if you try to work with a `ChoreographyLocation` that is not a member of `L`. + +```rust, compile_fail +# {{#include ./header.txt}} +# // 1. Define a struct +# struct HelloWorldChoreography; + +# // 2. Implement the `Choreography` trait +// ... +impl Choreography for HelloWorldChoreography { + type L = hlist!(Alice); + fn run(self, op: &impl ChoreoOp) { + // this will fail + op.locally(Bob, |_| { + println!("Hello, World!"); + }); + } +} +``` + From d239c35f24e8aca6eab3e10562557e1756784d94 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 15 Sep 2023 21:06:30 -0700 Subject: [PATCH 12/75] Use name as an associated function instead of method --- chorus_lib/src/transport/http.rs | 24 ++++++++++++------------ chorus_lib/src/transport/local.rs | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index a253c4b..ef98f84 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -138,23 +138,23 @@ mod tests { let v = 42; let mut config = HashMap::new(); let (signal, wait) = mpsc::channel::<()>(); - config.insert(Alice.name(), ("localhost", 9010)); - config.insert(Bob.name(), ("localhost", 9011)); + config.insert(Alice::name(), ("localhost", 9010)); + config.insert(Bob::name(), ("localhost", 9011)); let mut handles = Vec::new(); { let config = config.clone(); handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start - let transport = HttpTransport::new(Alice.name(), &config); - transport.send::(Alice.name(), Bob.name(), &v); + let transport = HttpTransport::new(Alice::name(), &config); + transport.send::(Alice::name(), Bob::name(), &v); })); } { let config = config.clone(); handles.push(thread::spawn(move || { - let transport = HttpTransport::new(Bob.name(), &config); + let transport = HttpTransport::new(Bob::name(), &config); signal.send(()).unwrap(); - let v2 = transport.receive::(Alice.name(), Bob.name()); + let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); })); } @@ -168,15 +168,15 @@ mod tests { let v = 42; let mut config = HashMap::new(); let (signal, wait) = mpsc::channel::<()>(); - config.insert(Alice.name(), ("localhost", 9020)); - config.insert(Bob.name(), ("localhost", 9021)); + config.insert(Alice::name(), ("localhost", 9020)); + config.insert(Bob::name(), ("localhost", 9021)); let mut handles = Vec::new(); { let config = config.clone(); handles.push(thread::spawn(move || { signal.send(()).unwrap(); - let transport = HttpTransport::new(Alice.name(), &config); - transport.send::(Alice.name(), Bob.name(), &v); + let transport = HttpTransport::new(Alice::name(), &config); + transport.send::(Alice::name(), Bob::name(), &v); })); } { @@ -185,8 +185,8 @@ mod tests { // wait for Alice to start, which forces Alice to retry wait.recv().unwrap(); sleep(Duration::from_millis(100)); - let transport = HttpTransport::new(Bob.name(), &config); - let v2 = transport.receive::(Alice.name(), Bob.name()); + let transport = HttpTransport::new(Bob::name(), &config); + let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); })); } diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 724c434..ac08261 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -79,18 +79,18 @@ mod tests { #[test] fn test_local_transport() { let v = 42; - let transport = LocalTransport::from(&[Alice.name(), Bob.name()]); + let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); let mut handles = Vec::new(); { let transport = transport.clone(); handles.push(thread::spawn(move || { - transport.send::(Alice.name(), Bob.name(), &v); + transport.send::(Alice::name(), Bob::name(), &v); })); } { let transport = transport.clone(); handles.push(thread::spawn(move || { - let v2 = transport.receive::(Alice.name(), Bob.name()); + let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); })); } From 0d40d186cd26be92c4ce4a338aa25adb302cb443 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 15 Sep 2023 21:15:51 -0700 Subject: [PATCH 13/75] Use hlist!() instead of HNil --- chorus_book/src/guide-input-and-output.md | 6 +++--- chorus_book/src/guide-runner.md | 2 +- chorus_book/src/header.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chorus_book/src/guide-input-and-output.md b/chorus_book/src/guide-input-and-output.md index 69bfb6d..8ac7ca5 100644 --- a/chorus_book/src/guide-input-and-output.md +++ b/chorus_book/src/guide-input-and-output.md @@ -18,7 +18,7 @@ struct DemoChoreography { } impl Choreography for DemoChoreography { - type L = HNil; + type L = hlist!(); fn run(self, op: &impl ChoreoOp) { println!("Input: {}", self.input); } @@ -33,7 +33,7 @@ You can construct an instance of the choreography with the input and pass it to # input: String, # } # impl Choreography for DemoChoreography { -# type L = HNil; +# type L = hlist!(); # fn run(self, op: &impl ChoreoOp) { # println!("Input: {}", self.input); # } @@ -140,7 +140,7 @@ To do so, we specify the output type to the `Choreography` trait and return the struct DemoChoreography; impl Choreography for DemoChoreography { - type L = HNil; + type L = hlist!(); fn run(self, op: &impl ChoreoOp) -> String { "Hello, World!".to_string() } diff --git a/chorus_book/src/guide-runner.md b/chorus_book/src/guide-runner.md index ffc66ce..6d43d1e 100644 --- a/chorus_book/src/guide-runner.md +++ b/chorus_book/src/guide-runner.md @@ -8,7 +8,7 @@ To use `Runner`, construct an instance using the `new` constructor, and then cal {{#include ./header.txt}} # struct DemoChoreography; # impl Choreography for DemoChoreography { -# type L = HNil; +# type L = hlist!(); # fn run(self, op: &impl ChoreoOp) { # } # } diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 5b81e2f..3b12443 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,5 +1,5 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner, HNil}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; # use chorus_lib::transport::local::LocalTransport; # use chorus_lib::hlist; # #[derive(ChoreographyLocation)] @@ -8,4 +8,4 @@ # struct Bob; # #[derive(ChoreographyLocation)] # struct Carol; -# let transport = LocalTransport::from(&[Alice::name(), Bob::name(), Carol::name()]); \ No newline at end of file +# let transport = LocalTransport::from(&[Alice::name(), Bob::name(), Carol::name()]); From cd3a288fee32dd2bed27729ad82db508215a24b0 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Fri, 15 Sep 2023 21:55:49 -0700 Subject: [PATCH 14/75] rewrite colocally document --- chorus_book/src/guide-colocally.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chorus_book/src/guide-colocally.md b/chorus_book/src/guide-colocally.md index 7191124..8ad18fd 100644 --- a/chorus_book/src/guide-colocally.md +++ b/chorus_book/src/guide-colocally.md @@ -70,7 +70,7 @@ impl Choreography for BobCarolChoreography { } ``` -Notice that the `BobCarolChoreography` only describes the behavior of Bob and Carol. Since Alice does not appear in this choreography, we can use the `colocally` operator in the main choreography to execute the sub-choreography only on Bob and Carol. +Notice that `BobCarolChoreography` only describes the behavior of Bob and Carol (see its location set `L`). `colocally` is an operator to execute a choreography only at locations that is included in the location set. In this case, if we invoke `BobCarolChoreography` with `colocally` in the main choreography, it will only be executed at Bob and Carol and not at Alice. ```rust {{#include ./header.txt}} From 8e5416feac18993bfdda3f36a8a3fdaf4a3b4b13 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Fri, 15 Sep 2023 21:56:01 -0700 Subject: [PATCH 15/75] fix warnings --- chorus_lib/src/core.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index f3889ba..972c639 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -272,7 +272,7 @@ pub trait Transport { /// Provides a method to perform end-point projection. pub struct Projector { - target: L1, + target: PhantomData, transport: T, } @@ -282,7 +282,11 @@ impl Projector { /// - `target` is the projection target of the choreography. /// - `transport` is an implementation of `Transport`. pub fn new(target: L1, transport: B) -> Self { - Projector { target, transport } + let _ = target; + Projector { + target: PhantomData, + transport, + } } /// Constructs a `Located` struct located at the projection target using the actual value. @@ -298,6 +302,7 @@ impl Projector { /// /// Note that the method panics at runtime if the projection target and the location of the value are the same. pub fn remote(&self, l2: L2) -> Located { + let _ = l2; // NOTE(shumbo): Ideally, this check should be done at the type level. if L1::name() == L2::name() { panic!("Cannot create a remote value at the same location"); From 6f4b9139483b3166aba061ecfefc0360a24b92fe Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 06:45:28 -0700 Subject: [PATCH 16/75] Add available-locations to Projector --- chorus_lib/examples/bookseller.rs | 7 +-- chorus_lib/examples/bookseller2.rs | 18 ++++++-- chorus_lib/examples/hello.rs | 9 ++-- chorus_lib/examples/loc-poly.rs | 9 ++-- chorus_lib/examples/tic-tac-toe.rs | 10 +++-- chorus_lib/src/core.rs | 68 ++++++++++++++++++++++++------ 6 files changed, 93 insertions(+), 28 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 3b1bcfa..d55a2f2 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -6,7 +6,7 @@ use std::thread; use chorus_lib::hlist; use chrono::NaiveDate; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, ProjectorForAL}; use chorus_lib::transport::local::LocalTransport; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { @@ -74,8 +74,9 @@ impl Choreography for BooksellerChoreography { fn main() { let transport = LocalTransport::from(&[Seller::name(), Buyer::name()]); - let seller_projector = Projector::new(Seller, transport.clone()); - let buyer_projector = Projector::new(Buyer, transport.clone()); + type AL = hlist!(Buyer, Seller); + let seller_projector = ProjectorForAL::::new(Seller, transport.clone()); + let buyer_projector = ProjectorForAL::::new(Buyer, transport.clone()); let mut handles: Vec> = Vec::new(); handles.push(thread::spawn(move || { diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 437452c..badd67f 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -5,7 +5,7 @@ use std::{collections::HashMap, sync::Arc}; use chorus_lib::hlist; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, + core::{ChoreoOp, Choreography, ChoreographyLocation, Located, ProjectorForAL}, transport::local::LocalTransport, }; use chrono::NaiveDate; @@ -142,10 +142,20 @@ fn main() { i }; + type AL = hlist!(Seller, Buyer1, Buyer2); let transport = LocalTransport::from(&[Seller::name(), Buyer1::name(), Buyer2::name()]); - let seller_projector = Arc::new(Projector::new(Seller, transport.clone())); - let buyer1_projector = Arc::new(Projector::new(Buyer1, transport.clone())); - let buyer2_projector = Arc::new(Projector::new(Buyer2, transport.clone())); + let seller_projector = Arc::new(ProjectorForAL::::new( + Seller, + transport.clone() + )); + let buyer1_projector = Arc::new(ProjectorForAL::::new( + Buyer1, + transport.clone(), + )); + let buyer2_projector = Arc::new(ProjectorForAL::::new( + Buyer2, + transport.clone(), + )); println!("Tries to buy HoTT with one buyer"); type OneBuyerBooksellerChoreography = BooksellerChoreography; diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index cbf0597..cb51867 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -2,7 +2,7 @@ extern crate chorus_lib; use std::thread; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, ProjectorForAL}; use chorus_lib::hlist; use chorus_lib::transport::local::LocalTransport; @@ -41,18 +41,21 @@ fn main() { let mut handles: Vec> = Vec::new(); // Create a local transport let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); + // Crate available locations for Projector + type AL = hlist!(Alice, Bob); + // Run the choreography in two threads { let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = Projector::new(Alice, transport); + let p = ProjectorForAL::::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } { let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = Projector::new(Bob, transport); + let p = ProjectorForAL::::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); })); } diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 7b46924..5b5613c 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::thread; use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, + ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, ProjectorForAL, }; use chorus_lib::hlist; use chorus_lib::transport::local::LocalTransport; @@ -59,11 +59,14 @@ impl Choreography> for MainChoreography { fn main() { let transport = LocalTransport::from(&[Alice::name(), Bob::name(), Carol::name()]); + // Crate available locations for Projector + type AL = hlist!(Alice, Bob); + let mut handles = vec![]; { let transport = transport.clone(); handles.push(thread::spawn(|| { - let p = Projector::new(Alice, transport); + let p = ProjectorForAL::::new(Alice, transport); let v = p.epp_and_run(MainChoreography); assert_eq!(p.unwrap(v), 110); })); @@ -71,7 +74,7 @@ fn main() { { let transport = transport.clone(); handles.push(thread::spawn(|| { - let p = Projector::new(Bob, transport); + let p = ProjectorForAL::::new(Bob, transport); p.epp_and_run(MainChoreography); })); } diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 456c643..5211518 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -3,7 +3,7 @@ extern crate chorus_lib; use chorus_lib::{ core::{ - ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, ProjectorForAL, Serialize, }, hlist, transport::http::HttpTransport, @@ -290,6 +290,10 @@ fn main() { } else { Box::new(UserBrain::new(args.player)) }; + + // Crate available locations for Projector + type AL = hlist!(PlayerX, PlayerO); + match args.player { 'X' => { let mut config = HashMap::new(); @@ -299,7 +303,7 @@ fn main() { (args.opponent_hostname.as_str(), args.opponent_port), ); let transport = HttpTransport::new(PlayerX::name(), &config); - let projector = Projector::new(PlayerX, transport); + let projector = ProjectorForAL::::new(PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.local(brain), brain_for_o: projector.remote(PlayerO), @@ -313,7 +317,7 @@ fn main() { (args.opponent_hostname.as_str(), args.opponent_port), ); let transport = HttpTransport::new(PlayerO::name(), &config); - let projector = Projector::new(PlayerO, transport); + let projector = ProjectorForAL::::new(PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.remote(PlayerX), brain_for_o: projector.local(brain), diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 972c639..f047cd0 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -168,6 +168,22 @@ where { } +/// Equal + pub trait Equal {} + + // Base case: HNil is equal to HNil + impl Equal for HNil {} + + // Recursive case: Head::Tail is equal to L if + // 1. Head is a member of L + // 2. Tail is equal to the remainder of L + impl Equal> for HCons + where + Head: Member, + Tail: Equal, + { + } + /// Provides a method to work with located values at the current location pub struct Unwrapper { phantom: PhantomData, @@ -271,28 +287,55 @@ pub trait Transport { } /// Provides a method to perform end-point projection. -pub struct Projector { +pub struct Projector where + L1: Member{ target: PhantomData, transport: T, + available_locations: PhantomData, + index: PhantomData, +} + +/// Provides a wrapper struct for users so that they can only specify AL; since it can't be inferred. +pub struct ProjectorForAL(PhantomData); + +impl ProjectorForAL { + /// Constructs a `Projector` struct. + /// + /// - `target` is the projection target of the choreography. + /// - `transport` is an implementation of `Transport`. + pub fn new( + target: L1, + transport: B, + ) -> Projector + where + L1: Member, + { + Projector::new(target, transport) + } } -impl Projector { +impl Projector + where L1: Member +{ /// Constructs a `Projector` struct. /// /// - `target` is the projection target of the choreography. /// - `transport` is an implementation of `Transport`. - pub fn new(target: L1, transport: B) -> Self { - let _ = target; + pub fn new(_target: L1, transport: B) -> Self + { Projector { target: PhantomData, transport, + available_locations: PhantomData, + index: PhantomData, } } /// Constructs a `Located` struct located at the projection target using the actual value. /// /// Use this method to run a choreography that takes a located value as an input. - pub fn local(&self, value: V) -> Located { + pub fn local(&self, value: V) -> Located + { Located::local(value) } @@ -301,12 +344,10 @@ impl Projector { /// Use this method to run a choreography that takes a located value as an input. /// /// Note that the method panics at runtime if the projection target and the location of the value are the same. - pub fn remote(&self, l2: L2) -> Located { - let _ = l2; - // NOTE(shumbo): Ideally, this check should be done at the type level. - if L1::name() == L2::name() { - panic!("Cannot create a remote value at the same location"); - } + pub fn remote(&self, _l2: L2) -> Located + where + L2: Member<>::Remainder, Index2>, + { Located::remote() } @@ -318,7 +359,10 @@ impl Projector { } /// Performs end-point projection and runs a choreography. - pub fn epp_and_run<'a, V, L: HList, C: Choreography>(&'a self, choreo: C) -> V { + pub fn epp_and_run<'a, V, L: HList, C: Choreography, IndexSet>(&'a self, choreo: C) -> V + where + L: Equal, + { struct EppOp<'a, L: HList, L1: ChoreographyLocation, B: Transport> { target: PhantomData, transport: &'a B, From 66fd8914eb9a5b6677eede900b3712f0e90d2d2b Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 07:00:13 -0700 Subject: [PATCH 17/75] Update the book to use available locations for Projector --- chorus_book/src/guide-input-and-output.md | 14 +++++++------- chorus_book/src/guide-projector.md | 12 +++++++----- chorus_book/src/guide-transport.md | 13 +++++++++---- chorus_book/src/header.txt | 2 +- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/chorus_book/src/guide-input-and-output.md b/chorus_book/src/guide-input-and-output.md index 8ac7ca5..924ab98 100644 --- a/chorus_book/src/guide-input-and-output.md +++ b/chorus_book/src/guide-input-and-output.md @@ -33,7 +33,7 @@ You can construct an instance of the choreography with the input and pass it to # input: String, # } # impl Choreography for DemoChoreography { -# type L = hlist!(); +# type L = hlist!(Alice); # fn run(self, op: &impl ChoreoOp) { # println!("Input: {}", self.input); # } @@ -42,7 +42,7 @@ You can construct an instance of the choreography with the input and pass it to let choreo = DemoChoreography { input: "World".to_string(), }; -let projector = Projector::new(Alice, transport); +let projector = ProjectorForAL::::new(Alice, transport); projector.epp_and_run(choreo); ``` @@ -92,7 +92,7 @@ To run the sample choreography above at Alice, we use the `local` method to cons # }); # } # } -let projector_for_alice = Projector::new(Alice, transport); +let projector_for_alice = ProjectorForAL::::new(Alice, transport); // Because the target of the projector is Alice, the located value is available at Alice. let string_at_alice: Located = projector_for_alice.local("Hello, World!".to_string()); // Instantiate the choreography with the located value @@ -111,7 +111,7 @@ For Bob, we use the `remote` method to construct the located value. # } # # impl Choreography for DemoChoreography { -# type L = hlist!(Alice); +# type L = hlist!(Alice, Bob); # fn run(self, op: &impl ChoreoOp) { # op.locally(Alice, |un| { # let input = un.unwrap(&self.input); @@ -119,7 +119,7 @@ For Bob, we use the `remote` method to construct the located value. # }); # } # } -let projector_for_bob = Projector::new(Bob, transport); +let projector_for_bob = ProjectorForAL::::new(Bob, transport); // Construct a remote located value at Alice. The actual value is not required. let string_at_alice = projector_for_bob.remote(Alice); // Instantiate the choreography with the located value @@ -160,7 +160,7 @@ impl Choreography for DemoChoreography { # } # } let choreo = DemoChoreography; -let projector = Projector::new(Alice, transport); +let projector = ProjectorForAL::::new(Alice, transport); let output = projector.epp_and_run(choreo); assert_eq!(output, "Hello, World!".to_string()); ``` @@ -182,7 +182,7 @@ impl Choreography> for DemoChoreography { } } -let projector = Projector::new(Alice, transport); +let projector = ProjectorForAL::::new(Alice, transport); let output = projector.epp_and_run(DemoChoreography); let string_at_alice = projector.unwrap(output); assert_eq!(string_at_alice, "Hello, World!".to_string()); diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 9e41bea..c56e632 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -9,14 +9,15 @@ To create a `Projector`, you need to provide the location and the transport. ```rust # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::core::{ChoreographyLocation, Projector}; +# use chorus_lib::core::{ChoreographyLocation, ProjectorForAL}; +# use chorus_lib::hlist; # let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # -let projector = Projector::new(Alice, transport); +let projector = ProjectorForAL::::new(Alice, transport); ``` Notice that the `Projector` is parameterized by the location type. You will need one projector for each location to execute choreography. @@ -28,7 +29,8 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro ```rust # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp, HNil}; +# use chorus_lib::core::{ChoreographyLocation, ProjectorForAL, Choreography, ChoreoOp}; +# use chorus_lib::hlist; # let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; @@ -36,12 +38,12 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # struct Bob; # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = HNil; +# type L = hlist!(Alice); # fn run(self, op: &impl ChoreoOp) { # } # } # -# let projector = Projector::new(Alice, transport); +# let projector = ProjectorForAL::::new(Alice, transport); projector.epp_and_run(HelloWorldChoreography); ``` diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index a3ae384..01c7b90 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -25,24 +25,29 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use std::thread; -# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector, HNil}; +# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, ProjectorForAL}; +# use chorus_lib::hlist; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = HNil; +# type L = hlist!(Alice, Bob); # fn run(self, op: &impl ChoreoOp) { # } # } + +// Crate available locations for Projector +type AL = hlist!(Alice, Bob); + let mut handles: Vec> = Vec::new(); let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); { // create a clone for Alice let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = Projector::new(Alice, transport); + let p = ProjectorForAL::::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } @@ -50,7 +55,7 @@ let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); // create another for Bob let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = Projector::new(Bob, transport); + let p = ProjectorForAL::::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); })); } diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 3b12443..6c1f207 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,5 +1,5 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, ProjectorForAL, Located, Superposition, Runner}; # use chorus_lib::transport::local::LocalTransport; # use chorus_lib::hlist; # #[derive(ChoreographyLocation)] From a2696b5dbe11bd4608fad6c28db6e06641fb110d Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 07:00:58 -0700 Subject: [PATCH 18/75] Format code --- chorus_lib/examples/bookseller.rs | 2 +- chorus_lib/examples/bookseller2.rs | 17 +++-------- chorus_lib/examples/tic-tac-toe.rs | 3 +- chorus_lib/src/core.rs | 46 ++++++++++++++++-------------- 4 files changed, 32 insertions(+), 36 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index d55a2f2..16d1316 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -74,7 +74,7 @@ impl Choreography for BooksellerChoreography { fn main() { let transport = LocalTransport::from(&[Seller::name(), Buyer::name()]); - type AL = hlist!(Buyer, Seller); + type AL = hlist!(Buyer, Seller); let seller_projector = ProjectorForAL::::new(Seller, transport.clone()); let buyer_projector = ProjectorForAL::::new(Buyer, transport.clone()); diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index badd67f..c72e94d 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -142,20 +142,11 @@ fn main() { i }; - type AL = hlist!(Seller, Buyer1, Buyer2); + type AL = hlist!(Seller, Buyer1, Buyer2); let transport = LocalTransport::from(&[Seller::name(), Buyer1::name(), Buyer2::name()]); - let seller_projector = Arc::new(ProjectorForAL::::new( - Seller, - transport.clone() - )); - let buyer1_projector = Arc::new(ProjectorForAL::::new( - Buyer1, - transport.clone(), - )); - let buyer2_projector = Arc::new(ProjectorForAL::::new( - Buyer2, - transport.clone(), - )); + let seller_projector = Arc::new(ProjectorForAL::::new(Seller, transport.clone())); + let buyer1_projector = Arc::new(ProjectorForAL::::new(Buyer1, transport.clone())); + let buyer2_projector = Arc::new(ProjectorForAL::::new(Buyer2, transport.clone())); println!("Tries to buy HoTT with one buyer"); type OneBuyerBooksellerChoreography = BooksellerChoreography; diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 5211518..d53b6fc 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -3,7 +3,8 @@ extern crate chorus_lib; use chorus_lib::{ core::{ - ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, ProjectorForAL, Serialize, + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, ProjectorForAL, + Serialize, }, hlist, transport::http::HttpTransport, diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index f047cd0..5cc0a79 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -169,20 +169,20 @@ where } /// Equal - pub trait Equal {} +pub trait Equal {} - // Base case: HNil is equal to HNil - impl Equal for HNil {} +// Base case: HNil is equal to HNil +impl Equal for HNil {} - // Recursive case: Head::Tail is equal to L if - // 1. Head is a member of L - // 2. Tail is equal to the remainder of L - impl Equal> for HCons - where - Head: Member, - Tail: Equal, - { - } +// Recursive case: Head::Tail is equal to L if +// 1. Head is a member of L +// 2. Tail is equal to the remainder of L +impl Equal> for HCons +where + Head: Member, + Tail: Equal, +{ +} /// Provides a method to work with located values at the current location pub struct Unwrapper { @@ -287,8 +287,10 @@ pub trait Transport { } /// Provides a method to perform end-point projection. -pub struct Projector where - L1: Member{ +pub struct Projector +where + L1: Member, +{ target: PhantomData, transport: T, available_locations: PhantomData, @@ -314,15 +316,15 @@ impl ProjectorForAL { } } -impl Projector - where L1: Member +impl Projector +where + L1: Member, { /// Constructs a `Projector` struct. /// /// - `target` is the projection target of the choreography. /// - `transport` is an implementation of `Transport`. - pub fn new(_target: L1, transport: B) -> Self - { + pub fn new(_target: L1, transport: B) -> Self { Projector { target: PhantomData, transport, @@ -334,8 +336,7 @@ impl Projector(&self, value: V) -> Located - { + pub fn local(&self, value: V) -> Located { Located::local(value) } @@ -359,7 +360,10 @@ impl Projector, IndexSet>(&'a self, choreo: C) -> V + pub fn epp_and_run<'a, V, L: HList, C: Choreography, IndexSet>( + &'a self, + choreo: C, + ) -> V where L: Equal, { From ee676e55d6aeafdc396ef4a871a713db16d21d3f Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 07:25:17 -0700 Subject: [PATCH 19/75] Remove old comment --- chorus_lib/src/core.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 5cc0a79..5ae9dd6 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -343,8 +343,6 @@ where /// Constructs a `Located` struct *NOT* located at the projection target. /// /// Use this method to run a choreography that takes a located value as an input. - /// - /// Note that the method panics at runtime if the projection target and the location of the value are the same. pub fn remote(&self, _l2: L2) -> Located where L2: Member<>::Remainder, Index2>, From fbc4cab4a87d0078f4788ac1de2cc440835ef512 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 09:30:26 -0700 Subject: [PATCH 20/75] Make the set of available-locations of Choreography be a subset of the Projector that uses it --- chorus_lib/src/core.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 5ae9dd6..6f53666 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -168,22 +168,6 @@ where { } -/// Equal -pub trait Equal {} - -// Base case: HNil is equal to HNil -impl Equal for HNil {} - -// Recursive case: Head::Tail is equal to L if -// 1. Head is a member of L -// 2. Tail is equal to the remainder of L -impl Equal> for HCons -where - Head: Member, - Tail: Equal, -{ -} - /// Provides a method to work with located values at the current location pub struct Unwrapper { phantom: PhantomData, @@ -363,7 +347,7 @@ where choreo: C, ) -> V where - L: Equal, + L: Subset, { struct EppOp<'a, L: HList, L1: ChoreographyLocation, B: Transport> { target: PhantomData, From 2f49ded1dd628b2a3befe622fdf2e29123930619 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 11:32:13 -0700 Subject: [PATCH 21/75] Use a macro (projector) to instantiate Projectors --- chorus_lib/examples/bookseller.rs | 8 ++++---- chorus_lib/examples/bookseller2.rs | 10 +++++----- chorus_lib/examples/hello.rs | 8 ++++---- chorus_lib/examples/loc-poly.rs | 8 ++++---- chorus_lib/examples/tic-tac-toe.rs | 8 ++++---- chorus_lib/src/core.rs | 23 ++++++----------------- 6 files changed, 27 insertions(+), 38 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 16d1316..b93fc7a 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -3,10 +3,10 @@ extern crate chorus_lib; use std::io; use std::thread; -use chorus_lib::hlist; use chrono::NaiveDate; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, ProjectorForAL}; +use chorus_lib::{hlist, projector}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; use chorus_lib::transport::local::LocalTransport; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { @@ -75,8 +75,8 @@ impl Choreography for BooksellerChoreography { fn main() { let transport = LocalTransport::from(&[Seller::name(), Buyer::name()]); type AL = hlist!(Buyer, Seller); - let seller_projector = ProjectorForAL::::new(Seller, transport.clone()); - let buyer_projector = ProjectorForAL::::new(Buyer, transport.clone()); + let seller_projector = projector!(AL, Seller, transport.clone()); + let buyer_projector = projector!(AL, Buyer, transport.clone()); let mut handles: Vec> = Vec::new(); handles.push(thread::spawn(move || { diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index c72e94d..b20a512 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -3,9 +3,9 @@ extern crate chorus_lib; use std::thread; use std::{collections::HashMap, sync::Arc}; -use chorus_lib::hlist; +use chorus_lib::{hlist, projector}; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, ProjectorForAL}, + core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, transport::local::LocalTransport, }; use chrono::NaiveDate; @@ -144,9 +144,9 @@ fn main() { type AL = hlist!(Seller, Buyer1, Buyer2); let transport = LocalTransport::from(&[Seller::name(), Buyer1::name(), Buyer2::name()]); - let seller_projector = Arc::new(ProjectorForAL::::new(Seller, transport.clone())); - let buyer1_projector = Arc::new(ProjectorForAL::::new(Buyer1, transport.clone())); - let buyer2_projector = Arc::new(ProjectorForAL::::new(Buyer2, transport.clone())); + let seller_projector = Arc::new(projector!(AL, Seller, transport.clone())); + let buyer1_projector = Arc::new(projector!(AL, Buyer1, transport.clone())); + let buyer2_projector = Arc::new(projector!(AL, Buyer2, transport.clone())); println!("Tries to buy HoTT with one buyer"); type OneBuyerBooksellerChoreography = BooksellerChoreography; diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index cb51867..dbf283d 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -2,8 +2,8 @@ extern crate chorus_lib; use std::thread; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, ProjectorForAL}; -use chorus_lib::hlist; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; +use chorus_lib::{hlist, projector}; use chorus_lib::transport::local::LocalTransport; // --- Define two locations (Alice and Bob) --- @@ -48,14 +48,14 @@ fn main() { { let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = ProjectorForAL::::new(Alice, transport); + let p = projector!(AL, Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } { let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = ProjectorForAL::::new(Bob, transport); + let p = projector!(AL, Bob, transport); p.epp_and_run(HelloWorldChoreography); })); } diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 5b5613c..13e3bcd 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -3,9 +3,9 @@ use std::fmt::Debug; use std::thread; use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, ProjectorForAL, + ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, }; -use chorus_lib::hlist; +use chorus_lib::{hlist, projector}; use chorus_lib::transport::local::LocalTransport; #[derive(ChoreographyLocation)] @@ -66,7 +66,7 @@ fn main() { { let transport = transport.clone(); handles.push(thread::spawn(|| { - let p = ProjectorForAL::::new(Alice, transport); + let p = projector!(AL, Alice, transport); let v = p.epp_and_run(MainChoreography); assert_eq!(p.unwrap(v), 110); })); @@ -74,7 +74,7 @@ fn main() { { let transport = transport.clone(); handles.push(thread::spawn(|| { - let p = ProjectorForAL::::new(Bob, transport); + let p = projector!(AL, Bob, transport); p.epp_and_run(MainChoreography); })); } diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index d53b6fc..65ba20e 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -3,10 +3,10 @@ extern crate chorus_lib; use chorus_lib::{ core::{ - ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, ProjectorForAL, + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, }, - hlist, + hlist, projector, transport::http::HttpTransport, }; use clap::Parser; @@ -304,7 +304,7 @@ fn main() { (args.opponent_hostname.as_str(), args.opponent_port), ); let transport = HttpTransport::new(PlayerX::name(), &config); - let projector = ProjectorForAL::::new(PlayerX, transport); + let projector = projector!(AL, PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.local(brain), brain_for_o: projector.remote(PlayerO), @@ -318,7 +318,7 @@ fn main() { (args.opponent_hostname.as_str(), args.opponent_port), ); let transport = HttpTransport::new(PlayerO::name(), &config); - let projector = ProjectorForAL::::new(PlayerO, transport); + let projector = projector!(AL, PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.remote(PlayerX), brain_for_o: projector.local(brain), diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 6f53666..3f44deb 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -281,23 +281,12 @@ where index: PhantomData, } -/// Provides a wrapper struct for users so that they can only specify AL; since it can't be inferred. -pub struct ProjectorForAL(PhantomData); - -impl ProjectorForAL { - /// Constructs a `Projector` struct. - /// - /// - `target` is the projection target of the choreography. - /// - `transport` is an implementation of `Transport`. - pub fn new( - target: L1, - transport: B, - ) -> Projector - where - L1: Member, - { - Projector::new(target, transport) - } +/// Macro to make Projector +#[macro_export] +macro_rules! projector { + ($al_type:ty, $target:expr, $transport:expr) => { + Projector::<$al_type, _, _, _>::new($target, $transport) + }; } impl Projector From 0df1edce201ad26ddf43fc56a938308ee3db3de6 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 11:38:17 -0700 Subject: [PATCH 22/75] Update the book to use the new projector macro --- chorus_book/src/guide-input-and-output.md | 15 ++++++++++----- chorus_book/src/guide-projector.md | 16 ++++++++++------ chorus_book/src/guide-transport.md | 8 ++++---- chorus_book/src/header.txt | 4 ++-- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/chorus_book/src/guide-input-and-output.md b/chorus_book/src/guide-input-and-output.md index 924ab98..e02b7f0 100644 --- a/chorus_book/src/guide-input-and-output.md +++ b/chorus_book/src/guide-input-and-output.md @@ -42,7 +42,8 @@ You can construct an instance of the choreography with the input and pass it to let choreo = DemoChoreography { input: "World".to_string(), }; -let projector = ProjectorForAL::::new(Alice, transport); +type AL = hlist!(Alice); +let projector = projector!(AL, Alice, transport); projector.epp_and_run(choreo); ``` @@ -92,7 +93,8 @@ To run the sample choreography above at Alice, we use the `local` method to cons # }); # } # } -let projector_for_alice = ProjectorForAL::::new(Alice, transport); +type AL = hlist!(Alice); +let projector_for_alice = projector!(AL, Alice, transport); // Because the target of the projector is Alice, the located value is available at Alice. let string_at_alice: Located = projector_for_alice.local("Hello, World!".to_string()); // Instantiate the choreography with the located value @@ -119,7 +121,8 @@ For Bob, we use the `remote` method to construct the located value. # }); # } # } -let projector_for_bob = ProjectorForAL::::new(Bob, transport); +type AL = hlist!(Alice, Bob); +let projector_for_bob = projector!(AL, Bob, transport); // Construct a remote located value at Alice. The actual value is not required. let string_at_alice = projector_for_bob.remote(Alice); // Instantiate the choreography with the located value @@ -160,7 +163,8 @@ impl Choreography for DemoChoreography { # } # } let choreo = DemoChoreography; -let projector = ProjectorForAL::::new(Alice, transport); +type AL = hlist!(Alice); +let projector = projector!(AL, Alice, transport); let output = projector.epp_and_run(choreo); assert_eq!(output, "Hello, World!".to_string()); ``` @@ -182,7 +186,8 @@ impl Choreography> for DemoChoreography { } } -let projector = ProjectorForAL::::new(Alice, transport); +type AL = hlist!(Alice); +let projector = projector!(AL, Alice, transport); let output = projector.epp_and_run(DemoChoreography); let string_at_alice = projector.unwrap(output); assert_eq!(string_at_alice, "Hello, World!".to_string()); diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index c56e632..d9bbba3 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -9,15 +9,17 @@ To create a `Projector`, you need to provide the location and the transport. ```rust # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::core::{ChoreographyLocation, ProjectorForAL}; -# use chorus_lib::hlist; +# use chorus_lib::core::{ChoreographyLocation, Projector}; +# use chorus_lib::{hlist, projector}; # let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # -let projector = ProjectorForAL::::new(Alice, transport); + +type AL = hlist!(Alice, Bob); +let projector = projector!(AL, Alice, transport); ``` Notice that the `Projector` is parameterized by the location type. You will need one projector for each location to execute choreography. @@ -29,8 +31,8 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro ```rust # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::core::{ChoreographyLocation, ProjectorForAL, Choreography, ChoreoOp}; -# use chorus_lib::hlist; +# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; +# use chorus_lib::{hlist, projector}; # let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; @@ -43,7 +45,9 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # } # } # -# let projector = ProjectorForAL::::new(Alice, transport); + +# type AL = hlist!(Alice); +# let projector = projector!(AL, Alice, transport); projector.epp_and_run(HelloWorldChoreography); ``` diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 01c7b90..5d86e5a 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -25,8 +25,8 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use std::thread; -# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, ProjectorForAL}; -# use chorus_lib::hlist; +# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector}; +# use chorus_lib::{hlist, projector}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -47,7 +47,7 @@ let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); // create a clone for Alice let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = ProjectorForAL::::new(Alice, transport); + let p = projector!(AL, Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } @@ -55,7 +55,7 @@ let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); // create another for Bob let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = ProjectorForAL::::new(Bob, transport); + let p = projector!(AL, Bob, transport); p.epp_and_run(HelloWorldChoreography); })); } diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 6c1f207..4a0c7eb 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,7 +1,7 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, ProjectorForAL, Located, Superposition, Runner}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; # use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::hlist; +# use chorus_lib::{hlist, projector}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] From a18ee9201069a90e19753f01a1ce0a477ba38402 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 11:38:48 -0700 Subject: [PATCH 23/75] Format code --- chorus_lib/examples/bookseller.rs | 2 +- chorus_lib/examples/bookseller2.rs | 2 +- chorus_lib/examples/hello.rs | 2 +- chorus_lib/examples/loc-poly.rs | 2 +- chorus_lib/examples/tic-tac-toe.rs | 3 +-- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index b93fc7a..ead5e2a 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -5,9 +5,9 @@ use std::thread; use chrono::NaiveDate; -use chorus_lib::{hlist, projector}; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; use chorus_lib::transport::local::LocalTransport; +use chorus_lib::{hlist, projector}; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { match title.trim() { diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index b20a512..89b9e76 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -3,11 +3,11 @@ extern crate chorus_lib; use std::thread; use std::{collections::HashMap, sync::Arc}; -use chorus_lib::{hlist, projector}; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, transport::local::LocalTransport, }; +use chorus_lib::{hlist, projector}; use chrono::NaiveDate; #[derive(ChoreographyLocation)] diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index dbf283d..41fb50b 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -3,8 +3,8 @@ extern crate chorus_lib; use std::thread; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; -use chorus_lib::{hlist, projector}; use chorus_lib::transport::local::LocalTransport; +use chorus_lib::{hlist, projector}; // --- Define two locations (Alice and Bob) --- diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 13e3bcd..3b216a7 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -5,8 +5,8 @@ use std::thread; use chorus_lib::core::{ ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, }; -use chorus_lib::{hlist, projector}; use chorus_lib::transport::local::LocalTransport; +use chorus_lib::{hlist, projector}; #[derive(ChoreographyLocation)] struct Alice; diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 65ba20e..aae2dc8 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -3,8 +3,7 @@ extern crate chorus_lib; use chorus_lib::{ core::{ - ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, - Serialize, + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, }, hlist, projector, transport::http::HttpTransport, From 03262eae2e636a9e80f82dbb0417900a7a669791 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 11:52:36 -0700 Subject: [PATCH 24/75] Update the book for Projector --- chorus_book/src/guide-projector.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index d9bbba3..6679012 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -4,7 +4,7 @@ Projector is responsible for performing the end-point projection and executing t ## Creating a Projector -To create a `Projector`, you need to provide the location and the transport. +To create a `Projector`, you need to provide the set of locations it can work with, the target location, and the transport. ```rust # extern crate chorus_lib; @@ -52,3 +52,30 @@ projector.epp_and_run(HelloWorldChoreography); ``` If the choreography has a return value, the `epp_and_run` method will return the value. We will discuss the return values in the [Input and Output](./guide-input-and-output.md) section. + +### Note on the available location set of the Choreography + +Keep in mind that when calling `epp_and_run`, you will get a compile error if the set of available locations of the `Choreography` is not a subset of the available locations of the `Projector`. In other words, the `Projector` should be allowed to do end-point projection into every `ChoreographyLocation` there is in the `Choreography`. So this will fail: + +```rust, compile_fail +# extern crate chorus_lib; +# use chorus_lib::transport::local::LocalTransport; +# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; +# use chorus_lib::{hlist, projector}; +# let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); +# #[derive(ChoreographyLocation)] +# struct Alice; +# #[derive(ChoreographyLocation)] +# struct Bob; +struct HelloWorldChoreography; +impl Choreography for HelloWorldChoreography { + type L = hlist!(Alice, Bob); + fn run(self, op: &impl ChoreoOp) { + } +} + + +type AL = hlist!(Alice); +let projector = projector!(AL, Alice, transport); +projector.epp_and_run(HelloWorldChoreography); +``` From 11d74a1d76af97357c3e936b7b2ae36572135a55 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 14:02:46 -0700 Subject: [PATCH 25/75] Change the the wording from available locations to location set --- chorus_book/src/guide-choreography.md | 18 +++++----- chorus_book/src/guide-colocally.md | 12 +++---- .../src/guide-higher-order-choreography.md | 8 ++--- chorus_book/src/guide-input-and-output.md | 32 ++++++++---------- .../src/guide-location-polymorphism.md | 2 +- chorus_book/src/guide-projector.md | 24 ++++++-------- chorus_book/src/guide-runner.md | 4 +-- chorus_book/src/guide-transport.md | 10 +++--- chorus_book/src/header.txt | 2 +- chorus_lib/examples/bookseller.rs | 11 +++---- chorus_lib/examples/bookseller2.rs | 33 ++++++++++++------- chorus_lib/examples/hello.rs | 12 +++---- chorus_lib/examples/input-output.rs | 4 +-- chorus_lib/examples/loc-poly.rs | 18 ++++------ chorus_lib/examples/runner.rs | 6 ++-- chorus_lib/examples/tic-tac-toe.rs | 16 ++++----- chorus_lib/src/core.rs | 22 ++++++------- 17 files changed, 111 insertions(+), 123 deletions(-) diff --git a/chorus_book/src/guide-choreography.md b/chorus_book/src/guide-choreography.md index 5474ccb..32003c3 100644 --- a/chorus_book/src/guide-choreography.md +++ b/chorus_book/src/guide-choreography.md @@ -9,7 +9,7 @@ struct HelloWorldChoreography; // 2. Implement the `Choreography` trait impl Choreography for HelloWorldChoreography { - type L = hlist!(Alice); + type L = LocationSet!(Alice); fn run(self, op: &impl ChoreoOp) { // 3. Use the `op` parameter to access operators op.locally(Alice, |_| { @@ -21,7 +21,7 @@ impl Choreography for HelloWorldChoreography { `Choreography` must implement the `run` method which defines the behavior of the system. The `run` method takes a reference to an object that implements the `ChoreoOp` trait. The `ChoreoOp` trait provides choreographic operators such as `locally` and `comm`. -Also, each `Choreography` has an associated type `L`, which is the set of `ChoreographyLocation`s it can operate on. To build a set of locations, you can use the macro `hlist!`. +Also, each `Choreography` has an associated type `L`, called it's location set; this is the set of `ChoreographyLocation`s that the `Choreography` can operate on. To build a location set, you can use the macro `LocationSet!`. ## Choreographic Operators @@ -36,7 +36,7 @@ The `locally` operator is used to perform a computation at a single location. It # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = hlist!(Alice); +# type L = LocationSet!(Alice); # fn run(self, op: &impl ChoreoOp) { op.locally(Alice, |_| { println!("Hello, World!"); @@ -52,7 +52,7 @@ The closure can return a value to create a located value. Located values are val # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = hlist!(Alice); +# type L = LocationSet!(Alice); # fn run(self, op: &impl ChoreoOp) { // This value is only available at Alice let num_at_alice: Located = op.locally(Alice, |_| { @@ -69,7 +69,7 @@ The computation closure takes `Unwrapper`. Using the `Unwrapper`, you can get a # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = hlist!(Alice); +# type L = LocationSet!(Alice); # fn run(self, op: &impl ChoreoOp) { let num_at_alice: Located = op.locally(Alice, |_| { 42 @@ -90,7 +90,7 @@ Note that you can unwrap a located value only at the location where the located # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = hlist!(Alice, Bob); +# type L = LocationSet!(Alice, Bob); # fn run(self, op: &impl ChoreoOp) { // This code will fail to compile let num_at_alice = op.locally(Alice, |_| { 42 }); @@ -113,7 +113,7 @@ The `comm` operator is used to perform a communication between two locations. It # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = hlist!(Alice, Bob); +# type L = LocationSet!(Alice, Bob); # fn run(self, op: &impl ChoreoOp) { // This value is only available at Alice let num_at_alice: Located = op.locally(Alice, |_| { @@ -139,7 +139,7 @@ The `broadcast` operator is used to perform a broadcast from a single location t # # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = hlist!(Alice); +# type L = LocationSet!(Alice); # fn run(self, op: &impl ChoreoOp) { // This value is only available at Alice let num_at_alice: Located = op.locally(Alice, |_| { @@ -173,7 +173,7 @@ You'll get a compile error if you try to work with a `ChoreographyLocation` that # // 2. Implement the `Choreography` trait // ... impl Choreography for HelloWorldChoreography { - type L = hlist!(Alice); + type L = LocationSet!(Alice); fn run(self, op: &impl ChoreoOp) { // this will fail op.locally(Bob, |_| { diff --git a/chorus_book/src/guide-colocally.md b/chorus_book/src/guide-colocally.md index 8ad18fd..d563bc5 100644 --- a/chorus_book/src/guide-colocally.md +++ b/chorus_book/src/guide-colocally.md @@ -20,7 +20,7 @@ This protocol can be implemented as follows: struct DemoChoreography; impl Choreography for DemoChoreography { - type L = hlist!(Alice, Bob, Carol); + type L = LocationSet!(Alice, Bob, Carol); fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| { get_random_number() @@ -52,7 +52,7 @@ struct BobCarolChoreography { x_at_bob: Located, }; impl Choreography for BobCarolChoreography { - type L = hlist!(Bob, Carol); + type L = LocationSet!(Bob, Carol); fn run(self, op: &impl ChoreoOp) { let is_even_at_bob: Located = op.locally(Bob, |un| { let x = un.unwrap(&self.x_at_bob); @@ -81,7 +81,7 @@ Notice that `BobCarolChoreography` only describes the behavior of Bob and Carol # x_at_bob: Located, # }; # impl Choreography for BobCarolChoreography { -# type L = hlist!(Bob, Carol); +# type L = LocationSet!(Bob, Carol); # fn run(self, op: &impl ChoreoOp) { # let is_even_at_bob: Located = op.locally(Bob, |un| { # let x = un.unwrap(&self.x_at_bob); @@ -99,7 +99,7 @@ Notice that `BobCarolChoreography` only describes the behavior of Bob and Carol # } struct MainChoreography; impl Choreography for MainChoreography { - type L = hlist!(Alice, Bob, Carol); + type L = LocationSet!(Alice, Bob, Carol); fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| { get_random_number() @@ -135,7 +135,7 @@ struct BobCarolChoreography { }; impl Choreography for BobCarolChoreography { - type L = hlist!(Bob, Carol); + type L = LocationSet!(Bob, Carol); fn run(self, op: &impl ChoreoOp) -> BobCarolResult { let is_even_at_bob: Located = op.locally(Bob, |un| { let x = un.unwrap(&self.x_at_bob); @@ -159,7 +159,7 @@ impl Choreography for BobCarolChoreography { struct MainChoreography; impl Choreography for MainChoreography { - type L = hlist!(Alice, Bob, Carol); + type L = LocationSet!(Alice, Bob, Carol); fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| { get_random_number() diff --git a/chorus_book/src/guide-higher-order-choreography.md b/chorus_book/src/guide-higher-order-choreography.md index 588f9e5..d7c1750 100644 --- a/chorus_book/src/guide-higher-order-choreography.md +++ b/chorus_book/src/guide-higher-order-choreography.md @@ -22,8 +22,8 @@ When you implement the `Choreography` trait, you have access to the `sub_choreo` # struct HigherOrderChoreography { # sub_choreo: C, # }; -impl> Choreography for HigherOrderChoreography { - type L = hlist!(Alice, Bob); +impl> Choreography for HigherOrderChoreography { + type L = LocationSet!(Alice, Bob); fn run(self, op: &impl ChoreoOp) { op.call(self.sub_choreo); } @@ -46,8 +46,8 @@ struct HigherOrderChoreography> + SubChoreo _marker: PhantomData, }; -impl, L = hlist!(Alice)> + SubChoreography> Choreography for HigherOrderChoreography { - type L = hlist!(Alice); +impl, L = LocationSet!(Alice)> + SubChoreography> Choreography for HigherOrderChoreography { + type L = LocationSet!(Alice); fn run(self, op: &impl ChoreoOp) { let num_at_alice = op.locally(Alice, |_| { 42 diff --git a/chorus_book/src/guide-input-and-output.md b/chorus_book/src/guide-input-and-output.md index e02b7f0..25bd8ea 100644 --- a/chorus_book/src/guide-input-and-output.md +++ b/chorus_book/src/guide-input-and-output.md @@ -18,7 +18,7 @@ struct DemoChoreography { } impl Choreography for DemoChoreography { - type L = hlist!(); + type L = LocationSet!(); fn run(self, op: &impl ChoreoOp) { println!("Input: {}", self.input); } @@ -33,7 +33,7 @@ You can construct an instance of the choreography with the input and pass it to # input: String, # } # impl Choreography for DemoChoreography { -# type L = hlist!(Alice); +# type L = LocationSet!(Alice); # fn run(self, op: &impl ChoreoOp) { # println!("Input: {}", self.input); # } @@ -42,8 +42,8 @@ You can construct an instance of the choreography with the input and pass it to let choreo = DemoChoreography { input: "World".to_string(), }; -type AL = hlist!(Alice); -let projector = projector!(AL, Alice, transport); + +let projector = projector!(LocationSet!(Alice), Alice, transport); projector.epp_and_run(choreo); ``` @@ -58,7 +58,7 @@ struct DemoChoreography { } impl Choreography for DemoChoreography { - type L = hlist!(Alice); + type L = LocationSet!(Alice); fn run(self, op: &impl ChoreoOp) { op.locally(Alice, |un| { let input = un.unwrap(&self.input); @@ -85,7 +85,7 @@ To run the sample choreography above at Alice, we use the `local` method to cons # } # # impl Choreography for DemoChoreography { -# type L = hlist!(Alice); +# type L = LocationSet!(Alice); # fn run(self, op: &impl ChoreoOp) { # op.locally(Alice, |un| { # let input = un.unwrap(&self.input); @@ -93,8 +93,7 @@ To run the sample choreography above at Alice, we use the `local` method to cons # }); # } # } -type AL = hlist!(Alice); -let projector_for_alice = projector!(AL, Alice, transport); +let projector_for_alice = projector!(LocationSet!(Alice), Alice, transport); // Because the target of the projector is Alice, the located value is available at Alice. let string_at_alice: Located = projector_for_alice.local("Hello, World!".to_string()); // Instantiate the choreography with the located value @@ -113,7 +112,7 @@ For Bob, we use the `remote` method to construct the located value. # } # # impl Choreography for DemoChoreography { -# type L = hlist!(Alice, Bob); +# type L = LocationSet!(Alice, Bob); # fn run(self, op: &impl ChoreoOp) { # op.locally(Alice, |un| { # let input = un.unwrap(&self.input); @@ -121,8 +120,7 @@ For Bob, we use the `remote` method to construct the located value. # }); # } # } -type AL = hlist!(Alice, Bob); -let projector_for_bob = projector!(AL, Bob, transport); +let projector_for_bob = projector!(LocationSet!(Alice, Bob), Bob, transport); // Construct a remote located value at Alice. The actual value is not required. let string_at_alice = projector_for_bob.remote(Alice); // Instantiate the choreography with the located value @@ -143,7 +141,7 @@ To do so, we specify the output type to the `Choreography` trait and return the struct DemoChoreography; impl Choreography for DemoChoreography { - type L = hlist!(); + type L = LocationSet!(); fn run(self, op: &impl ChoreoOp) -> String { "Hello, World!".to_string() } @@ -157,14 +155,13 @@ impl Choreography for DemoChoreography { # struct DemoChoreography; # # impl Choreography for DemoChoreography { -# type L = hlist!(Alice); +# type L = LocationSet!(Alice); # fn run(self, op: &impl ChoreoOp) -> String { # "Hello, World!".to_string() # } # } let choreo = DemoChoreography; -type AL = hlist!(Alice); -let projector = projector!(AL, Alice, transport); +let projector = projector!(LocationSet!(Alice), Alice, transport); let output = projector.epp_and_run(choreo); assert_eq!(output, "Hello, World!".to_string()); ``` @@ -178,7 +175,7 @@ You can use the `Located` as a return type of the `run` method to return struct DemoChoreography; impl Choreography> for DemoChoreography { - type L = hlist!(Alice); + type L = LocationSet!(Alice); fn run(self, op: &impl ChoreoOp) -> Located { op.locally(Alice, |_| { "Hello, World!".to_string() @@ -186,8 +183,7 @@ impl Choreography> for DemoChoreography { } } -type AL = hlist!(Alice); -let projector = projector!(AL, Alice, transport); +let projector = projector!(LocationSet!(Alice), Alice, transport); let output = projector.epp_and_run(DemoChoreography); let string_at_alice = projector.unwrap(output); assert_eq!(string_at_alice, "Hello, World!".to_string()); diff --git a/chorus_book/src/guide-location-polymorphism.md b/chorus_book/src/guide-location-polymorphism.md index ebe7d6a..ef0ef1f 100644 --- a/chorus_book/src/guide-location-polymorphism.md +++ b/chorus_book/src/guide-location-polymorphism.md @@ -11,7 +11,7 @@ struct LocationPolymorphicChoreography { } impl Choreography for LocationPolymorphicChoreography { - type L = hlist!(L1); + type L = LocationSet!(L1); fn run(self, op: &impl ChoreoOp) { op.locally(self.location, |_| { println!("Hello, World!"); diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 6679012..8b32821 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -10,7 +10,7 @@ To create a `Projector`, you need to provide the set of locations it can work wi # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use chorus_lib::core::{ChoreographyLocation, Projector}; -# use chorus_lib::{hlist, projector}; +# use chorus_lib::{LocationSet, projector}; # let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; @@ -18,8 +18,7 @@ To create a `Projector`, you need to provide the set of locations it can work wi # struct Bob; # -type AL = hlist!(Alice, Bob); -let projector = projector!(AL, Alice, transport); +let projector = projector!(LocationSet!(Alice, Bob), Alice, transport); ``` Notice that the `Projector` is parameterized by the location type. You will need one projector for each location to execute choreography. @@ -32,7 +31,7 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# use chorus_lib::{hlist, projector}; +# use chorus_lib::{LocationSet, projector}; # let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; @@ -40,28 +39,27 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # struct Bob; # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = hlist!(Alice); +# type L = LocationSet!(Alice); # fn run(self, op: &impl ChoreoOp) { # } # } # -# type AL = hlist!(Alice); -# let projector = projector!(AL, Alice, transport); +# let projector = projector!(LocationSet!(Alice), Alice, transport); projector.epp_and_run(HelloWorldChoreography); ``` If the choreography has a return value, the `epp_and_run` method will return the value. We will discuss the return values in the [Input and Output](./guide-input-and-output.md) section. -### Note on the available location set of the Choreography +### Note on the location set of the Choreography -Keep in mind that when calling `epp_and_run`, you will get a compile error if the set of available locations of the `Choreography` is not a subset of the available locations of the `Projector`. In other words, the `Projector` should be allowed to do end-point projection into every `ChoreographyLocation` there is in the `Choreography`. So this will fail: +Keep in mind that when calling `epp_and_run`, you will get a compile error if the location set of the `Choreography` is not a subset of the location set of the `Projector`. In other words, the `Projector` should be allowed to do end-point projection into every `ChoreographyLocation` that `Choreography` can talk about. So this will fail: ```rust, compile_fail # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# use chorus_lib::{hlist, projector}; +# use chorus_lib::{LocationSet, projector}; # let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); # #[derive(ChoreographyLocation)] # struct Alice; @@ -69,13 +67,11 @@ Keep in mind that when calling `epp_and_run`, you will get a compile error if th # struct Bob; struct HelloWorldChoreography; impl Choreography for HelloWorldChoreography { - type L = hlist!(Alice, Bob); + type L = LocationSet!(Alice, Bob); fn run(self, op: &impl ChoreoOp) { } } - -type AL = hlist!(Alice); -let projector = projector!(AL, Alice, transport); +let projector = projector!(LocationSet!(Alice), Alice, transport); projector.epp_and_run(HelloWorldChoreography); ``` diff --git a/chorus_book/src/guide-runner.md b/chorus_book/src/guide-runner.md index 6d43d1e..1f501ff 100644 --- a/chorus_book/src/guide-runner.md +++ b/chorus_book/src/guide-runner.md @@ -8,7 +8,7 @@ To use `Runner`, construct an instance using the `new` constructor, and then cal {{#include ./header.txt}} # struct DemoChoreography; # impl Choreography for DemoChoreography { -# type L = hlist!(); +# type L = LocationSet!(); # fn run(self, op: &impl ChoreoOp) { # } # } @@ -27,7 +27,7 @@ struct SumChoreography { y_at_bob: Located, } impl Choreography> for SumChoreography { - type L = hlist!(Alice, Bob, Carol); + type L = LocationSet!(Alice, Bob, Carol); fn run(self, op: &impl ChoreoOp) -> Located { let x_at_carol = op.comm(Alice, Carol, &self.x_at_alice); let y_at_carol = op.comm(Bob, Carol, &self.y_at_bob); diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 5d86e5a..310a533 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -26,20 +26,18 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # use chorus_lib::transport::local::LocalTransport; # use std::thread; # use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector}; -# use chorus_lib::{hlist, projector}; +# use chorus_lib::{LocationSet, projector}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # struct HelloWorldChoreography; # impl Choreography for HelloWorldChoreography { -# type L = hlist!(Alice, Bob); +# type L = LocationSet!(Alice, Bob); # fn run(self, op: &impl ChoreoOp) { # } # } -// Crate available locations for Projector -type AL = hlist!(Alice, Bob); let mut handles: Vec> = Vec::new(); let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); @@ -47,7 +45,7 @@ let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); // create a clone for Alice let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = projector!(AL, Alice, transport); + let p = projector!(LocationSet!(Alice, Bob), Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } @@ -55,7 +53,7 @@ let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); // create another for Bob let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = projector!(AL, Bob, transport); + let p = projector!(LocationSet!(Alice, Bob), Bob, transport); p.epp_and_run(HelloWorldChoreography); })); } diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 4a0c7eb..2115638 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,7 +1,7 @@ # extern crate chorus_lib; # use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; # use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::{hlist, projector}; +# use chorus_lib::{LocationSet, projector}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index ead5e2a..39d7c20 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -5,9 +5,9 @@ use std::thread; use chrono::NaiveDate; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation}; use chorus_lib::transport::local::LocalTransport; -use chorus_lib::{hlist, projector}; +use chorus_lib::{projector, LocationSet}; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { match title.trim() { @@ -27,7 +27,7 @@ struct Buyer; struct BooksellerChoreography; impl Choreography for BooksellerChoreography { - type L = hlist!(Seller, Buyer); + type L = LocationSet!(Seller, Buyer); fn run(self, op: &impl ChoreoOp) { let title_at_buyer = op.locally(Buyer, |_| { println!("Enter the title of the book to buy (TAPL or HoTT)"); @@ -74,9 +74,8 @@ impl Choreography for BooksellerChoreography { fn main() { let transport = LocalTransport::from(&[Seller::name(), Buyer::name()]); - type AL = hlist!(Buyer, Seller); - let seller_projector = projector!(AL, Seller, transport.clone()); - let buyer_projector = projector!(AL, Buyer, transport.clone()); + let seller_projector = projector!(LocationSet!(Buyer, Seller), Seller, transport.clone()); + let buyer_projector = projector!(LocationSet!(Buyer, Seller), Buyer, transport.clone()); let mut handles: Vec> = Vec::new(); handles.push(thread::spawn(move || { diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 89b9e76..ca6f68f 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -4,10 +4,10 @@ use std::thread; use std::{collections::HashMap, sync::Arc}; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, + core::{ChoreoOp, Choreography, ChoreographyLocation, Located}, transport::local::LocalTransport, }; -use chorus_lib::{hlist, projector}; +use chorus_lib::{projector, LocationSet}; use chrono::NaiveDate; #[derive(ChoreographyLocation)] @@ -36,7 +36,7 @@ impl Decider for OneBuyerDecider { } impl Choreography> for OneBuyerDecider { - type L = hlist!(Buyer1, Buyer2); + type L = LocationSet!(Buyer1, Buyer2); fn run(self, op: &impl ChoreoOp) -> Located { let price = op.broadcast(Buyer1, self.price); return op.locally(Buyer1, |_| { @@ -57,7 +57,7 @@ impl Decider for TwoBuyerDecider { } impl Choreography> for TwoBuyerDecider { - type L = hlist!(Buyer1, Buyer2); + type L = LocationSet!(Buyer1, Buyer2); fn run(self, op: &impl ChoreoOp) -> Located { let remaining = op.locally(Buyer1, |un| { const BUYER1_BUDGET: i32 = 100; @@ -79,10 +79,10 @@ struct BooksellerChoreography>> { title: Located, } -impl, L = hlist!(Buyer1, Buyer2)> + Decider> +impl, L = LocationSet!(Buyer1, Buyer2)> + Decider> Choreography, Buyer1>> for BooksellerChoreography { - type L = hlist!(Buyer1, Buyer2, Seller); + type L = LocationSet!(Buyer1, Buyer2, Seller); fn run(self, op: &impl ChoreoOp) -> Located, Buyer1> { let title_at_seller = op.comm(Buyer1, Seller, &self.title); let price_at_seller = op.locally(Seller, |un| { @@ -102,7 +102,7 @@ impl, L = hlist!(Buyer1, Buyer2)> + Decide decision_at_buyer1: Located, } impl Choreography, Buyer1>> for GetDeliveryDateChoreography { - type L = hlist!(Buyer1, Seller); + type L = LocationSet!(Buyer1, Seller); fn run(self, op: &impl ChoreoOp) -> Located, Buyer1> { let decision = op.broadcast(Buyer1, self.decision_at_buyer1); if decision { @@ -142,11 +142,22 @@ fn main() { i }; - type AL = hlist!(Seller, Buyer1, Buyer2); let transport = LocalTransport::from(&[Seller::name(), Buyer1::name(), Buyer2::name()]); - let seller_projector = Arc::new(projector!(AL, Seller, transport.clone())); - let buyer1_projector = Arc::new(projector!(AL, Buyer1, transport.clone())); - let buyer2_projector = Arc::new(projector!(AL, Buyer2, transport.clone())); + let seller_projector = Arc::new(projector!( + LocationSet!(Seller, Buyer1, Buyer2), + Seller, + transport.clone() + )); + let buyer1_projector = Arc::new(projector!( + LocationSet!(Seller, Buyer1, Buyer2), + Buyer1, + transport.clone() + )); + let buyer2_projector = Arc::new(projector!( + LocationSet!(Seller, Buyer1, Buyer2), + Buyer2, + transport.clone() + )); println!("Tries to buy HoTT with one buyer"); type OneBuyerBooksellerChoreography = BooksellerChoreography; diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index 41fb50b..bac0aff 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -2,9 +2,9 @@ extern crate chorus_lib; use std::thread; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation}; use chorus_lib::transport::local::LocalTransport; -use chorus_lib::{hlist, projector}; +use chorus_lib::{projector, LocationSet}; // --- Define two locations (Alice and Bob) --- @@ -19,7 +19,7 @@ struct HelloWorldChoreography; // Implement the `Choreography` trait for `HelloWorldChoreography` impl Choreography for HelloWorldChoreography { - type L = hlist!(Alice, Bob); + type L = LocationSet!(Alice, Bob); fn run(self, op: &impl ChoreoOp) { // Create a located value at Alice let msg_at_alice = op.locally(Alice, |_| { @@ -41,21 +41,19 @@ fn main() { let mut handles: Vec> = Vec::new(); // Create a local transport let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); - // Crate available locations for Projector - type AL = hlist!(Alice, Bob); // Run the choreography in two threads { let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = projector!(AL, Alice, transport); + let p = projector!(LocationSet!(Alice, Bob), Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } { let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = projector!(AL, Bob, transport); + let p = projector!(LocationSet!(Alice, Bob), Bob, transport); p.epp_and_run(HelloWorldChoreography); })); } diff --git a/chorus_lib/examples/input-output.rs b/chorus_lib/examples/input-output.rs index 7b77bab..a02844b 100644 --- a/chorus_lib/examples/input-output.rs +++ b/chorus_lib/examples/input-output.rs @@ -1,7 +1,7 @@ extern crate chorus_lib; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Located}, - hlist, + LocationSet, }; #[derive(ChoreographyLocation)] struct Alice; @@ -15,7 +15,7 @@ struct DemoChoreography { } impl Choreography for DemoChoreography { - type L = hlist!(Alice); + type L = LocationSet!(Alice); fn run(self, op: &impl ChoreoOp) { op.locally(Alice, |un| { let s = un.unwrap(&self.input); diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 3b216a7..88a56a9 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -2,11 +2,9 @@ extern crate chorus_lib; use std::fmt::Debug; use std::thread; -use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, -}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Portable}; use chorus_lib::transport::local::LocalTransport; -use chorus_lib::{hlist, projector}; +use chorus_lib::{projector, LocationSet}; #[derive(ChoreographyLocation)] struct Alice; @@ -27,7 +25,7 @@ where L1: ChoreographyLocation, L2: ChoreographyLocation, { - type L = hlist!(L1, L2); + type L = LocationSet!(L1, L2); fn run(self, op: &impl ChoreoOp) -> Located { let v = op.comm(self.sender, self.receiver, &self.data); op.locally(self.receiver, |un| println!("{:?}", un.unwrap(&v))); @@ -38,7 +36,7 @@ where struct MainChoreography; impl Choreography> for MainChoreography { - type L = hlist!(Alice, Bob); + type L = LocationSet!(Alice, Bob); fn run(self, op: &impl ChoreoOp) -> Located { let v1 = op.locally(Alice, |_| 100); @@ -58,15 +56,11 @@ impl Choreography> for MainChoreography { fn main() { let transport = LocalTransport::from(&[Alice::name(), Bob::name(), Carol::name()]); - - // Crate available locations for Projector - type AL = hlist!(Alice, Bob); - let mut handles = vec![]; { let transport = transport.clone(); handles.push(thread::spawn(|| { - let p = projector!(AL, Alice, transport); + let p = projector!(LocationSet!(Alice, Bob), Alice, transport); let v = p.epp_and_run(MainChoreography); assert_eq!(p.unwrap(v), 110); })); @@ -74,7 +68,7 @@ fn main() { { let transport = transport.clone(); handles.push(thread::spawn(|| { - let p = projector!(AL, Bob, transport); + let p = projector!(LocationSet!(Alice, Bob), Bob, transport); p.epp_and_run(MainChoreography); })); } diff --git a/chorus_lib/examples/runner.rs b/chorus_lib/examples/runner.rs index 29c9f55..83dd4a7 100644 --- a/chorus_lib/examples/runner.rs +++ b/chorus_lib/examples/runner.rs @@ -1,7 +1,7 @@ extern crate chorus_lib; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Runner, Superposition}, - hlist, + LocationSet, }; #[derive(ChoreographyLocation)] @@ -26,7 +26,7 @@ struct BobCarolChoreography { } impl Choreography for BobCarolChoreography { - type L = hlist!(Bob, Carol); + type L = LocationSet!(Bob, Carol); fn run(self, op: &impl ChoreoOp) -> BobCarolResult { let is_even_at_bob: Located = op.locally(Bob, |un| { let x = un.unwrap(&self.x_at_bob); @@ -50,7 +50,7 @@ impl Choreography for BobCarolChoreography { struct MainChoreography; impl Choreography for MainChoreography { - type L = hlist!(Alice, Bob, Carol); + type L = LocationSet!(Alice, Bob, Carol); fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| get_random_number()); let x_at_bob = op.comm(Alice, Bob, &x_at_alice); diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index aae2dc8..a92da03 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -2,11 +2,10 @@ extern crate chorus_lib; use chorus_lib::{ - core::{ - ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, - }, - hlist, projector, + core::{ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Serialize}, + projector, transport::http::HttpTransport, + LocationSet, }; use clap::Parser; use std::{collections::HashMap, io::Write}; @@ -228,7 +227,7 @@ struct TicTacToeChoreography { } impl Choreography for TicTacToeChoreography { - type L = hlist!(PlayerX, PlayerO); + type L = LocationSet!(PlayerX, PlayerO); fn run(self, op: &impl ChoreoOp) -> () { let mut board = Board::new(); loop { @@ -291,9 +290,6 @@ fn main() { Box::new(UserBrain::new(args.player)) }; - // Crate available locations for Projector - type AL = hlist!(PlayerX, PlayerO); - match args.player { 'X' => { let mut config = HashMap::new(); @@ -303,7 +299,7 @@ fn main() { (args.opponent_hostname.as_str(), args.opponent_port), ); let transport = HttpTransport::new(PlayerX::name(), &config); - let projector = projector!(AL, PlayerX, transport); + let projector = projector!(LocationSet!(PlayerX, PlayerO), PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.local(brain), brain_for_o: projector.remote(PlayerO), @@ -317,7 +313,7 @@ fn main() { (args.opponent_hostname.as_str(), args.opponent_port), ); let transport = HttpTransport::new(PlayerO::name(), &config); - let projector = projector!(AL, PlayerO, transport); + let projector = projector!(LocationSet!(PlayerX, PlayerO), PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.remote(PlayerX), brain_for_o: projector.local(brain), diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 3f44deb..d59bf41 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -122,10 +122,10 @@ where /// Macro to generate hlist #[macro_export] -macro_rules! hlist { +macro_rules! LocationSet { () => { $crate::core::HNil }; ($head:ty $(,)*) => { $crate::core::HCons<$head, $crate::core::HNil> }; - ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, hlist!($($tail)*)> }; + ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, LocationSet!($($tail)*)> }; } /// Marker @@ -271,13 +271,13 @@ pub trait Transport { } /// Provides a method to perform end-point projection. -pub struct Projector +pub struct Projector where - L1: Member, + L1: Member, { target: PhantomData, transport: T, - available_locations: PhantomData, + location_set: PhantomData, index: PhantomData, } @@ -285,13 +285,13 @@ where #[macro_export] macro_rules! projector { ($al_type:ty, $target:expr, $transport:expr) => { - Projector::<$al_type, _, _, _>::new($target, $transport) + $crate::core::Projector::<$al_type, _, _, _>::new($target, $transport) }; } -impl Projector +impl Projector where - L1: Member, + L1: Member, { /// Constructs a `Projector` struct. /// @@ -301,7 +301,7 @@ where Projector { target: PhantomData, transport, - available_locations: PhantomData, + location_set: PhantomData, index: PhantomData, } } @@ -318,7 +318,7 @@ where /// Use this method to run a choreography that takes a located value as an input. pub fn remote(&self, _l2: L2) -> Located where - L2: Member<>::Remainder, Index2>, + L2: Member<>::Remainder, Index2>, { Located::remote() } @@ -336,7 +336,7 @@ where choreo: C, ) -> V where - L: Subset, + L: Subset, { struct EppOp<'a, L: HList, L1: ChoreographyLocation, B: Transport> { target: PhantomData, From 07730daf8592710121323d40a20bc172a3c14231 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Sat, 16 Sep 2023 18:51:54 -0700 Subject: [PATCH 26/75] Update projector guide --- chorus_book/src/guide-projector.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 8b32821..d8abf63 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -4,7 +4,7 @@ Projector is responsible for performing the end-point projection and executing t ## Creating a Projector -To create a `Projector`, you need to provide the set of locations it can work with, the target location, and the transport. +To create a `Projector`, you need to provide the set of locations it can work with, the target location, and the transport. You should use the `projector!` macro instead of directly instantiating a Projector. ```rust # extern crate chorus_lib; @@ -21,7 +21,7 @@ To create a `Projector`, you need to provide the set of locations it can work wi let projector = projector!(LocationSet!(Alice, Bob), Alice, transport); ``` -Notice that the `Projector` is parameterized by the location type. You will need one projector for each location to execute choreography. +Notice that the `Projector` is parameterized by its target location type. You will need one projector for each location to execute choreography. ## Executing a Choreography From 2bd32d53b40d400045143a88de7346f84327d214 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 04:08:16 -0700 Subject: [PATCH 27/75] Add location-set as a generic type to Transport --- chorus_lib/examples/bookseller.rs | 10 +-- chorus_lib/examples/bookseller2.rs | 24 ++------ chorus_lib/examples/hello.rs | 10 +-- chorus_lib/examples/loc-poly.rs | 12 ++-- chorus_lib/examples/tic-tac-toe.rs | 44 ++++++++------ chorus_lib/src/core.rs | 33 +++++----- chorus_lib/src/transport/http.rs | 98 ++++++++++++++++++++++-------- chorus_lib/src/transport/local.rs | 24 +++++--- 8 files changed, 154 insertions(+), 101 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 39d7c20..05e3906 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -5,9 +5,9 @@ use std::thread; use chrono::NaiveDate; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; use chorus_lib::transport::local::LocalTransport; -use chorus_lib::{projector, LocationSet}; +use chorus_lib::LocationSet; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { match title.trim() { @@ -73,9 +73,9 @@ impl Choreography for BooksellerChoreography { } fn main() { - let transport = LocalTransport::from(&[Seller::name(), Buyer::name()]); - let seller_projector = projector!(LocationSet!(Buyer, Seller), Seller, transport.clone()); - let buyer_projector = projector!(LocationSet!(Buyer, Seller), Buyer, transport.clone()); + let transport = LocalTransport::::new(); + let seller_projector = Projector::new(Seller, transport.clone()); + let buyer_projector = Projector::new(Buyer, transport.clone()); let mut handles: Vec> = Vec::new(); handles.push(thread::spawn(move || { diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index ca6f68f..3355fda 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -3,11 +3,11 @@ extern crate chorus_lib; use std::thread; use std::{collections::HashMap, sync::Arc}; +use chorus_lib::LocationSet; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located}, + core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, transport::local::LocalTransport, }; -use chorus_lib::{projector, LocationSet}; use chrono::NaiveDate; #[derive(ChoreographyLocation)] @@ -142,22 +142,10 @@ fn main() { i }; - let transport = LocalTransport::from(&[Seller::name(), Buyer1::name(), Buyer2::name()]); - let seller_projector = Arc::new(projector!( - LocationSet!(Seller, Buyer1, Buyer2), - Seller, - transport.clone() - )); - let buyer1_projector = Arc::new(projector!( - LocationSet!(Seller, Buyer1, Buyer2), - Buyer1, - transport.clone() - )); - let buyer2_projector = Arc::new(projector!( - LocationSet!(Seller, Buyer1, Buyer2), - Buyer2, - transport.clone() - )); + let transport = LocalTransport::::new(); + let seller_projector = Arc::new(Projector::new(Seller, transport.clone())); + let buyer1_projector = Arc::new(Projector::new(Buyer1, transport.clone())); + let buyer2_projector = Arc::new(Projector::new(Buyer2, transport.clone())); println!("Tries to buy HoTT with one buyer"); type OneBuyerBooksellerChoreography = BooksellerChoreography; diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index bac0aff..dd2a416 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -2,9 +2,9 @@ extern crate chorus_lib; use std::thread; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; use chorus_lib::transport::local::LocalTransport; -use chorus_lib::{projector, LocationSet}; +use chorus_lib::LocationSet; // --- Define two locations (Alice and Bob) --- @@ -40,20 +40,20 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a local transport - let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); + let transport = LocalTransport::::new(); // Run the choreography in two threads { let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = projector!(LocationSet!(Alice, Bob), Alice, transport); + let p = Projector::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } { let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = projector!(LocationSet!(Alice, Bob), Bob, transport); + let p = Projector::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); })); } diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 88a56a9..2ee491f 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -2,9 +2,11 @@ extern crate chorus_lib; use std::fmt::Debug; use std::thread; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Portable}; +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, +}; use chorus_lib::transport::local::LocalTransport; -use chorus_lib::{projector, LocationSet}; +use chorus_lib::LocationSet; #[derive(ChoreographyLocation)] struct Alice; @@ -55,12 +57,12 @@ impl Choreography> for MainChoreography { } fn main() { - let transport = LocalTransport::from(&[Alice::name(), Bob::name(), Carol::name()]); + let transport = LocalTransport::::new(); let mut handles = vec![]; { let transport = transport.clone(); handles.push(thread::spawn(|| { - let p = projector!(LocationSet!(Alice, Bob), Alice, transport); + let p = Projector::new(Alice, transport); let v = p.epp_and_run(MainChoreography); assert_eq!(p.unwrap(v), 110); })); @@ -68,7 +70,7 @@ fn main() { { let transport = transport.clone(); handles.push(thread::spawn(|| { - let p = projector!(LocationSet!(Alice, Bob), Bob, transport); + let p = Projector::new(Bob, transport); p.epp_and_run(MainChoreography); })); } diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index a92da03..581d86b 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -2,13 +2,15 @@ extern crate chorus_lib; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Serialize}, - projector, + core::{ + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, + }, + http_config, transport::http::HttpTransport, LocationSet, }; use clap::Parser; -use std::{collections::HashMap, io::Write}; +use std::io::Write; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; #[derive(Serialize, Deserialize, Debug)] @@ -292,28 +294,32 @@ fn main() { match args.player { 'X' => { - let mut config = HashMap::new(); - config.insert(PlayerX::name(), (args.hostname.as_str(), args.port)); - config.insert( - PlayerO::name(), - (args.opponent_hostname.as_str(), args.opponent_port), - ); - let transport = HttpTransport::new(PlayerX::name(), &config); - let projector = projector!(LocationSet!(PlayerX, PlayerO), PlayerX, transport); + // let mut config = HttpConfig::::new(); + // config.insert(PlayerX, (args.hostname.as_str(), args.port)); + // config.insert( + // PlayerO, + // (args.opponent_hostname.as_str(), args.opponent_port), + // ); + let config = http_config!(PlayerX: (args.hostname.as_str(), args.port), + PlayerO: (args.opponent_hostname.as_str(), args.opponent_port)); + let transport = HttpTransport::new(PlayerX, &config); + let projector = Projector::new(PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.local(brain), brain_for_o: projector.remote(PlayerO), }); } 'O' => { - let mut config = HashMap::new(); - config.insert(PlayerO::name(), (args.hostname.as_str(), args.port)); - config.insert( - PlayerX::name(), - (args.opponent_hostname.as_str(), args.opponent_port), - ); - let transport = HttpTransport::new(PlayerO::name(), &config); - let projector = projector!(LocationSet!(PlayerX, PlayerO), PlayerO, transport); + // let mut config = HttpConfig::::new(); + // config.insert(PlayerO, (args.hostname.as_str(), args.port)); + // config.insert( + // PlayerX, + // (args.opponent_hostname.as_str(), args.opponent_port), + // ); + let config = http_config!(PlayerO: (args.hostname.as_str(), args.port), + PlayerX: (args.opponent_hostname.as_str(), args.opponent_port)); + let transport = HttpTransport::new(PlayerO, &config); + let projector = Projector::new(PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.remote(PlayerX), brain_for_o: projector.local(brain), diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index d59bf41..e7a985e 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -97,8 +97,11 @@ pub trait HList { fn to_string_list() -> Vec<&'static str>; } /// end of HList +#[derive(Clone)] pub struct HNil; /// An element of HList + +#[derive(Clone)] pub struct HCons(Head, Tail); impl HList for HNil { @@ -125,7 +128,7 @@ where macro_rules! LocationSet { () => { $crate::core::HNil }; ($head:ty $(,)*) => { $crate::core::HCons<$head, $crate::core::HNil> }; - ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, LocationSet!($($tail)*)> }; + ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, $crate::LocationSet!($($tail)*)> }; } /// Marker @@ -261,7 +264,7 @@ pub trait Choreography { /// Provides methods to send and receive messages. /// /// The trait provides methods to send and receive messages between locations. Implement this trait to define a custom transport. -pub trait Transport { +pub trait Transport { /// Returns a list of locations. fn locations(&self) -> Vec; /// Sends a message from `from` to `to`. @@ -271,7 +274,7 @@ pub trait Transport { } /// Provides a method to perform end-point projection. -pub struct Projector +pub struct Projector, Index> where L1: Member, { @@ -281,15 +284,7 @@ where index: PhantomData, } -/// Macro to make Projector -#[macro_export] -macro_rules! projector { - ($al_type:ty, $target:expr, $transport:expr) => { - $crate::core::Projector::<$al_type, _, _, _>::new($target, $transport) - }; -} - -impl Projector +impl, Index> Projector where L1: Member, { @@ -338,13 +333,16 @@ where where L: Subset, { - struct EppOp<'a, L: HList, L1: ChoreographyLocation, B: Transport> { + struct EppOp<'a, L: HList, L1: ChoreographyLocation, LS: HList, B: Transport> { target: PhantomData, transport: &'a B, locations: Vec, marker: PhantomData, + location_set: PhantomData, } - impl<'a, L: HList, T: ChoreographyLocation, B: Transport> ChoreoOp for EppOp<'a, L, T, B> { + impl<'a, L: HList, T: ChoreographyLocation, LS: HList, B: Transport> ChoreoOp + for EppOp<'a, L, T, LS, B> + { fn locally( &self, _location: L1, @@ -406,11 +404,12 @@ where where M: HList + Subset, { - let op: EppOp<'a, M, T, B> = EppOp { + let op: EppOp<'a, M, T, LS, B> = EppOp { target: PhantomData::, transport: &self.transport, locations: self.transport.locations(), marker: PhantomData::, + location_set: PhantomData::, }; choreo.run(&op) } @@ -429,6 +428,7 @@ where transport: self.transport, locations: locs_vec.clone(), marker: PhantomData::, + location_set: PhantomData::, }; return choreo.run(&op); } @@ -436,11 +436,12 @@ where R::remote() } } - let op: EppOp<'a, L, L1, B> = EppOp { + let op: EppOp<'a, L, L1, LS, B> = EppOp { target: PhantomData::, transport: &self.transport, locations: self.transport.locations(), marker: PhantomData::, + location_set: PhantomData::, }; choreo.run(&op) } diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index ef98f84..4406444 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -3,6 +3,8 @@ use std::thread; use std::{collections::HashMap, sync::Arc}; +use core::marker::PhantomData; + use retry::{ delay::{jitter, Fixed}, retry, @@ -11,31 +13,72 @@ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; use crate::{ - core::{Portable, Transport}, + core::{ChoreographyLocation, HList, Member, Portable, Transport}, utils::queue::BlockingQueue, }; /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; +/// A wrapper for HashMap +#[derive(Clone)] +pub struct HttpConfig { + info: HashMap, + location_set: PhantomData, +} + +impl HttpConfig { + /// Creates a new `HttpConfig`. + pub fn new() -> Self { + Self { + info: HashMap::new(), + location_set: PhantomData, + } + } + + /// Inserts new information about a location into the config. + pub fn insert(&mut self, _loc: C, (host, port): (&str, u16)) + where + C: Member, + { + self.info + .insert(C::name().to_string(), (host.to_string(), port)); + } +} + +/// This macro makes a `HttpConfig`. +#[macro_export] +macro_rules! http_config { + ( $( $loc:ident : ( $host:expr, $port:expr ) ),* $(,)? ) => { + { + let mut config = $crate::transport::http::HttpConfig::<$crate::LocationSet!($( $loc ),*)>::new(); + $( + config.insert($loc, ($host, $port)); + )* + config + } + }; +} + /// The HTTP transport. -pub struct HttpTransport { +pub struct HttpTransport { config: HashMap, agent: Agent, queue_map: Arc>>, server: Arc, join_handle: Option>, + location_set: PhantomData, } -impl HttpTransport { +impl HttpTransport { /// Creates a new `HttpTransport` instance from the projection target and a configuration. - pub fn new(at: &'static str, config: &HashMap<&str, (&str, u16)>) -> Self { - let config = HashMap::from_iter( - config - .iter() - .map(|(k, (hostname, port))| (k.to_string(), (hostname.to_string(), *port))), - ); - let locs = Vec::from_iter(config.keys().map(|s| s.clone())); + pub fn new(_loc: C, http_config: &HttpConfig) -> Self + where + C: Member, + { + let info = &http_config.info; + let at = C::name(); + let locs = Vec::from_iter(info.keys().map(|s| s.clone())); let queue_map = { let mut m = HashMap::new(); @@ -45,7 +88,7 @@ impl HttpTransport { Arc::new(m) }; - let (hostname, port) = config.get(at).unwrap(); + let (hostname, port) = info.get(at).unwrap(); let server = Arc::new(Server::http(format!("{}:{}", hostname, port)).unwrap()); let join_handle = Some({ let server = server.clone(); @@ -80,23 +123,24 @@ impl HttpTransport { let agent = AgentBuilder::new().build(); Self { - config, + config: info.clone(), agent, queue_map, join_handle, server, + location_set: PhantomData, } } } -impl Drop for HttpTransport { +impl Drop for HttpTransport { fn drop(&mut self) { self.server.unblock(); self.join_handle.take().map(thread::JoinHandle::join); } } -impl Transport for HttpTransport { +impl Transport for HttpTransport { fn locations(&self) -> Vec { Vec::from_iter(self.config.keys().map(|s| s.clone())) } @@ -136,23 +180,26 @@ mod tests { #[test] fn test_http_transport() { let v = 42; - let mut config = HashMap::new(); + let (signal, wait) = mpsc::channel::<()>(); - config.insert(Alice::name(), ("localhost", 9010)); - config.insert(Bob::name(), ("localhost", 9011)); + let config = http_config!( + Alice: ("localhost", 9010), + Bob: ("localhost", 9011) + ); + let mut handles = Vec::new(); { let config = config.clone(); handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start - let transport = HttpTransport::new(Alice::name(), &config); + let transport = HttpTransport::new(Alice, &config); transport.send::(Alice::name(), Bob::name(), &v); })); } { let config = config.clone(); handles.push(thread::spawn(move || { - let transport = HttpTransport::new(Bob::name(), &config); + let transport = HttpTransport::new(Bob, &config); signal.send(()).unwrap(); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); @@ -166,16 +213,19 @@ mod tests { #[test] fn test_http_transport_retry() { let v = 42; - let mut config = HashMap::new(); let (signal, wait) = mpsc::channel::<()>(); - config.insert(Alice::name(), ("localhost", 9020)); - config.insert(Bob::name(), ("localhost", 9021)); + + let config = http_config!( + Alice: ("localhost", 9020), + Bob: ("localhost", 9021) + ); + let mut handles = Vec::new(); { let config = config.clone(); handles.push(thread::spawn(move || { signal.send(()).unwrap(); - let transport = HttpTransport::new(Alice::name(), &config); + let transport = HttpTransport::new(Alice, &config); transport.send::(Alice::name(), Bob::name(), &v); })); } @@ -185,7 +235,7 @@ mod tests { // wait for Alice to start, which forces Alice to retry wait.recv().unwrap(); sleep(Duration::from_millis(100)); - let transport = HttpTransport::new(Bob::name(), &config); + let transport = HttpTransport::new(Bob, &config); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); })); diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index ac08261..154cfdc 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -5,7 +5,9 @@ use std::sync::Arc; use serde_json; -use crate::core::{Portable, Transport}; +use core::marker::PhantomData; + +use crate::core::{HList, Portable, Transport}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; @@ -16,34 +18,38 @@ type QueueMap = HashMap>>; /// /// Unlike network-based transports, all locations must share the same `LocalTransport` instance. The struct implements `Clone` so that it can be shared across threads. #[derive(Clone)] -pub struct LocalTransport { +pub struct LocalTransport { internal_locations: Vec, queue_map: Arc, + location_set: PhantomData, } -impl LocalTransport { +impl LocalTransport { /// Creates a new `LocalTransport` instance from a list of locations. - pub fn from(locations: &[&str]) -> Self { + pub fn new() -> Self { let mut queue_map: QueueMap = HashMap::new(); - for sender in locations.clone() { + let locations_list = L::to_string_list(); + + for sender in locations_list.clone() { let mut n = HashMap::new(); - for receiver in locations.clone() { + for receiver in locations_list.clone() { n.insert(receiver.to_string(), BlockingQueue::new()); } queue_map.insert(sender.to_string(), n); } let mut locations_vec = Vec::new(); - for loc in locations.clone() { + for loc in locations_list.clone() { locations_vec.push(loc.to_string()); } LocalTransport { queue_map: Arc::new(queue_map), internal_locations: locations_vec, + location_set: PhantomData, } } } -impl Transport for LocalTransport { +impl Transport for LocalTransport { fn locations(&self) -> Vec { return self.internal_locations.clone(); } @@ -79,7 +85,7 @@ mod tests { #[test] fn test_local_transport() { let v = 42; - let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); + let transport = LocalTransport::::new(); let mut handles = Vec::new(); { let transport = transport.clone(); From 24ad1990ad3c8fd127d10b5eea1b25ccc6d73b08 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 04:51:03 -0700 Subject: [PATCH 28/75] Change the book examples. The text itself still needs to be updated --- chorus_book/src/guide-input-and-output.md | 10 +++++----- chorus_book/src/guide-projector.md | 12 ++++++------ chorus_book/src/guide-transport.md | 18 +++++++++--------- chorus_book/src/header.txt | 4 ++-- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/chorus_book/src/guide-input-and-output.md b/chorus_book/src/guide-input-and-output.md index 25bd8ea..483af04 100644 --- a/chorus_book/src/guide-input-and-output.md +++ b/chorus_book/src/guide-input-and-output.md @@ -43,7 +43,7 @@ let choreo = DemoChoreography { input: "World".to_string(), }; -let projector = projector!(LocationSet!(Alice), Alice, transport); +let projector = Projector::new(Alice, transport); projector.epp_and_run(choreo); ``` @@ -93,7 +93,7 @@ To run the sample choreography above at Alice, we use the `local` method to cons # }); # } # } -let projector_for_alice = projector!(LocationSet!(Alice), Alice, transport); +let projector_for_alice = Projector::new(Alice, transport); // Because the target of the projector is Alice, the located value is available at Alice. let string_at_alice: Located = projector_for_alice.local("Hello, World!".to_string()); // Instantiate the choreography with the located value @@ -120,7 +120,7 @@ For Bob, we use the `remote` method to construct the located value. # }); # } # } -let projector_for_bob = projector!(LocationSet!(Alice, Bob), Bob, transport); +let projector_for_bob = Projector::new(Bob, transport); // Construct a remote located value at Alice. The actual value is not required. let string_at_alice = projector_for_bob.remote(Alice); // Instantiate the choreography with the located value @@ -161,7 +161,7 @@ impl Choreography for DemoChoreography { # } # } let choreo = DemoChoreography; -let projector = projector!(LocationSet!(Alice), Alice, transport); +let projector = Projector::new(Alice, transport); let output = projector.epp_and_run(choreo); assert_eq!(output, "Hello, World!".to_string()); ``` @@ -183,7 +183,7 @@ impl Choreography> for DemoChoreography { } } -let projector = projector!(LocationSet!(Alice), Alice, transport); +let projector = Projector::new(Alice, transport); let output = projector.epp_and_run(DemoChoreography); let string_at_alice = projector.unwrap(output); assert_eq!(string_at_alice, "Hello, World!".to_string()); diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index d8abf63..2dc87c2 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -10,15 +10,15 @@ To create a `Projector`, you need to provide the set of locations it can work wi # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use chorus_lib::core::{ChoreographyLocation, Projector}; -# use chorus_lib::{LocationSet, projector}; -# let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); +# use chorus_lib::{LocationSet}; +# let transport = LocalTransport::::new(); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # -let projector = projector!(LocationSet!(Alice, Bob), Alice, transport); +let projector = Projector::new(Alice, transport); ``` Notice that the `Projector` is parameterized by its target location type. You will need one projector for each location to execute choreography. @@ -31,8 +31,8 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # extern crate chorus_lib; # use chorus_lib::transport::local::LocalTransport; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# use chorus_lib::{LocationSet, projector}; -# let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); +# use chorus_lib::{LocationSet}; +# let transport = LocalTransport::::new(); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -45,7 +45,7 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # } # -# let projector = projector!(LocationSet!(Alice), Alice, transport); +# let projector = Projector::new(Alice, transport); projector.epp_and_run(HelloWorldChoreography); ``` diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 310a533..dfe5144 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -26,7 +26,7 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # use chorus_lib::transport::local::LocalTransport; # use std::thread; # use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector}; -# use chorus_lib::{LocationSet, projector}; +# use chorus_lib::{LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -40,12 +40,12 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran let mut handles: Vec> = Vec::new(); -let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); +let transport = LocalTransport::::new(); { // create a clone for Alice let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = projector!(LocationSet!(Alice, Bob), Alice, transport); + let p = Projector::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } @@ -53,7 +53,7 @@ let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); // create another for Bob let transport = transport.clone(); handles.push(thread::spawn(move || { - let p = projector!(LocationSet!(Alice, Bob), Bob, transport); + let p = Projector::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); })); } @@ -74,12 +74,12 @@ The `new` constructor takes the name of the projection target and "configuration ```rust {{#include ./header.txt}} -# use chorus_lib::transport::http::HttpTransport; +# use chorus_lib::transport::http::{HttpTransport}; +# use chorus_lib::http_config; # use std::collections::HashMap; -let mut config = HashMap::new(); -config.insert(Alice::name(), ("localhost", 8080)); -config.insert(Bob::name(), ("localhost", 8081)); -let transport = HttpTransport::new(Alice::name(), &config); + +let config = http_config!(Alice: ("localhost", 8080), Bob: ("localhost", 8081)); +let transport = HttpTransport::new(Alice, &config); ``` In the above example, the transport will start the HTTP server on port 8080 on localhost. If Alice needs to send a message to Bob, it will use `http://localhost:8081` as the destination. diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 2115638..72d6873 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,11 +1,11 @@ # extern crate chorus_lib; # use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; # use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::{LocationSet, projector}; +# use chorus_lib::{LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # #[derive(ChoreographyLocation)] # struct Carol; -# let transport = LocalTransport::from(&[Alice::name(), Bob::name(), Carol::name()]); +# let transport = LocalTransport::::new(); From 3fee76001d30e7dc5288665469d80c9b741febc8 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 05:04:19 -0700 Subject: [PATCH 29/75] Update the book --- chorus_book/src/guide-projector.md | 27 +------------------------ chorus_book/src/guide-transport.md | 32 ++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 2dc87c2..e887e52 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -4,7 +4,7 @@ Projector is responsible for performing the end-point projection and executing t ## Creating a Projector -To create a `Projector`, you need to provide the set of locations it can work with, the target location, and the transport. You should use the `projector!` macro instead of directly instantiating a Projector. +To create a `Projector`, you need to provide the target location and the transport. ```rust # extern crate chorus_lib; @@ -50,28 +50,3 @@ projector.epp_and_run(HelloWorldChoreography); ``` If the choreography has a return value, the `epp_and_run` method will return the value. We will discuss the return values in the [Input and Output](./guide-input-and-output.md) section. - -### Note on the location set of the Choreography - -Keep in mind that when calling `epp_and_run`, you will get a compile error if the location set of the `Choreography` is not a subset of the location set of the `Projector`. In other words, the `Projector` should be allowed to do end-point projection into every `ChoreographyLocation` that `Choreography` can talk about. So this will fail: - -```rust, compile_fail -# extern crate chorus_lib; -# use chorus_lib::transport::local::LocalTransport; -# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# use chorus_lib::{LocationSet, projector}; -# let transport = LocalTransport::from(&[Alice::name(), Bob::name()]); -# #[derive(ChoreographyLocation)] -# struct Alice; -# #[derive(ChoreographyLocation)] -# struct Bob; -struct HelloWorldChoreography; -impl Choreography for HelloWorldChoreography { - type L = LocationSet!(Alice, Bob); - fn run(self, op: &impl ChoreoOp) { - } -} - -let projector = projector!(LocationSet!(Alice), Alice, transport); -projector.epp_and_run(HelloWorldChoreography); -``` diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index dfe5144..f3de634 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -63,14 +63,15 @@ let transport = LocalTransport::::new(); The `http` transport is used to execute choreographies on different machines. This is useful for executing choreographies in a distributed system. -To use the `http` transport, import the `HttpTransport` struct from the `chorus_lib` crate. +To use the `http` transport, import the `HttpTransport` struct and the `http_config` macro from the `chorus_lib` crate. ```rust # extern crate chorus_lib; use chorus_lib::transport::http::HttpTransport; +use chorus_lib::http_config; ``` -The `new` constructor takes the name of the projection target and "configuration" of type `std::collections::HashMap<&'static str, (&'static str, u32)>`. The configuration is a map from location names to the hostname and port of the location. +The `new` constructor takes the name of the projection target and "configuration" of type `HttpConfig`. To build the `HttpConfig`, you should use the macro `http_config` and give it a comma separatedlist of key: values where each key is a `ChoreographyLocation` and each value is a tuple of (host_name, port). You can think of configuration as a map from locations to the hostname and port of the location. ```rust {{#include ./header.txt}} @@ -87,3 +88,30 @@ In the above example, the transport will start the HTTP server on port 8080 on l ## Creating a Custom Transport You can also create your own transport by implementing the `Transport` trait. See the API documentation for more details. + + +### Note on the location set of the Choreography + +Note that when calling `epp_and_run` on a `Projector`, you will get a compile error if the location set of the `Choreography` is not a subset of the location set of the `Transport`. In other words, the `Transport` should have information about every `ChoreographyLocation` that `Choreography` can talk about. So this will fail: + +```rust, compile_fail +# extern crate chorus_lib; +# use chorus_lib::transport::local::LocalTransport; +# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; +# use chorus_lib::{LocationSet}; + +# #[derive(ChoreographyLocation)] +# struct Alice; +# #[derive(ChoreographyLocation)] +# struct Bob; +struct HelloWorldChoreography; +impl Choreography for HelloWorldChoreography { + type L = LocationSet!(Alice, Bob); + fn run(self, op: &impl ChoreoOp) { + } +} + +let transport = LocalTransport::::new(); +let projector = Projector::new(Alice, transport); +projector.epp_and_run(HelloWorldChoreography); +``` From 140e431f56e8f2c6df181a0a96dc45a14c193247 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 08:07:09 -0700 Subject: [PATCH 30/75] Refactor HttpConfig --- chorus_lib/src/transport/http.rs | 36 +++++++++++--------------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 4406444..81cc034 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -15,6 +15,7 @@ use ureq::{Agent, AgentBuilder}; use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, utils::queue::BlockingQueue, + LocationSet, }; /// The header name for the source location. @@ -23,27 +24,10 @@ const HEADER_SRC: &str = "X-CHORUS-SOURCE"; /// A wrapper for HashMap #[derive(Clone)] pub struct HttpConfig { - info: HashMap, - location_set: PhantomData, -} - -impl HttpConfig { - /// Creates a new `HttpConfig`. - pub fn new() -> Self { - Self { - info: HashMap::new(), - location_set: PhantomData, - } - } - - /// Inserts new information about a location into the config. - pub fn insert(&mut self, _loc: C, (host, port): (&str, u16)) - where - C: Member, - { - self.info - .insert(C::name().to_string(), (host.to_string(), port)); - } + /// The information about locations + pub info: HashMap, + /// The struct is parametrized by the location set (`L`). + pub location_set: PhantomData, } /// This macro makes a `HttpConfig`. @@ -51,11 +35,15 @@ impl HttpConfig { macro_rules! http_config { ( $( $loc:ident : ( $host:expr, $port:expr ) ),* $(,)? ) => { { - let mut config = $crate::transport::http::HttpConfig::<$crate::LocationSet!($( $loc ),*)>::new(); + let mut config = std::collections::HashMap::new(); $( - config.insert($loc, ($host, $port)); + config.insert($loc::name().to_string(), ($host.to_string(), $port)); )* - config + + $crate::transport::http::HttpConfig::<$crate::LocationSet!($( $loc ),*)> { + info: config, + location_set: core::marker::PhantomData + } } }; } From f992cf01ae9b1122a9942b822a0aa99d88010fe3 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 12:53:40 -0700 Subject: [PATCH 31/75] Fix import for PhantomData --- chorus_lib/src/transport/http.rs | 3 +-- chorus_lib/src/transport/local.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 81cc034..7602a41 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -3,7 +3,7 @@ use std::thread; use std::{collections::HashMap, sync::Arc}; -use core::marker::PhantomData; +use std::marker::PhantomData; use retry::{ delay::{jitter, Fixed}, @@ -15,7 +15,6 @@ use ureq::{Agent, AgentBuilder}; use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, utils::queue::BlockingQueue, - LocationSet, }; /// The header name for the source location. diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 154cfdc..d7629ba 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use serde_json; -use core::marker::PhantomData; +use std::marker::PhantomData; use crate::core::{HList, Portable, Transport}; use crate::utils::queue::BlockingQueue; From c4feb9cba31f0eaf906fd193043361cc035ad99a Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 18:23:54 -0700 Subject: [PATCH 32/75] Simplify User-API for building TransportConfig --- chorus_lib/examples/bookseller.rs | 5 ++- chorus_lib/examples/bookseller2.rs | 4 +- chorus_lib/examples/hello.rs | 5 ++- chorus_lib/examples/loc-poly.rs | 4 +- chorus_lib/examples/tic-tac-toe.rs | 24 ++++------- chorus_lib/src/transport.rs | 27 +++++++++++++ chorus_lib/src/transport/http.rs | 64 ++++++++++++++++++------------ chorus_lib/src/transport/local.rs | 14 ++++++- 8 files changed, 99 insertions(+), 48 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 05e3906..dbd69d4 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -7,6 +7,7 @@ use chrono::NaiveDate; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; use chorus_lib::transport::local::LocalTransport; +use chorus_lib::transport_config; use chorus_lib::LocationSet; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { @@ -73,7 +74,9 @@ impl Choreography for BooksellerChoreography { } fn main() { - let transport = LocalTransport::::new(); + let config = transport_config!(Seller: (), Buyer: ()); + + let transport = LocalTransport::new(&config); let seller_projector = Projector::new(Seller, transport.clone()); let buyer_projector = Projector::new(Buyer, transport.clone()); diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 3355fda..8f99ec1 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -8,6 +8,7 @@ use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, transport::local::LocalTransport, }; +use chorus_lib::transport_config; use chrono::NaiveDate; #[derive(ChoreographyLocation)] @@ -142,7 +143,8 @@ fn main() { i }; - let transport = LocalTransport::::new(); + let config = transport_config!(Seller: (), Buyer1: (), Buyer2: ()); + let transport = LocalTransport::new(&config); let seller_projector = Arc::new(Projector::new(Seller, transport.clone())); let buyer1_projector = Arc::new(Projector::new(Buyer1, transport.clone())); let buyer2_projector = Arc::new(Projector::new(Buyer2, transport.clone())); diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index dd2a416..3f641be 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -4,6 +4,7 @@ use std::thread; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; use chorus_lib::transport::local::LocalTransport; +use chorus_lib::transport_config; use chorus_lib::LocationSet; // --- Define two locations (Alice and Bob) --- @@ -40,7 +41,9 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a local transport - let transport = LocalTransport::::new(); + let config = transport_config!(Alice: (), Bob: ()); + + let transport = LocalTransport::new(&config); // Run the choreography in two threads { diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 2ee491f..5bfa36e 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -6,6 +6,7 @@ use chorus_lib::core::{ ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, }; use chorus_lib::transport::local::LocalTransport; +use chorus_lib::transport_config; use chorus_lib::LocationSet; #[derive(ChoreographyLocation)] @@ -57,7 +58,8 @@ impl Choreography> for MainChoreography { } fn main() { - let transport = LocalTransport::::new(); + let config = transport_config!(Alice: (), Bob: (), Carol: ()); + let transport = LocalTransport::new(&config); let mut handles = vec![]; { let transport = transport.clone(); diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 581d86b..53e76bd 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -1,14 +1,16 @@ /// Choreographic tik-tak-toe game extern crate chorus_lib; +use chorus_lib::transport_config; use chorus_lib::{ core::{ ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, }, - http_config, + // http_config, transport::http::HttpTransport, LocationSet, }; + use clap::Parser; use std::io::Write; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; @@ -294,14 +296,8 @@ fn main() { match args.player { 'X' => { - // let mut config = HttpConfig::::new(); - // config.insert(PlayerX, (args.hostname.as_str(), args.port)); - // config.insert( - // PlayerO, - // (args.opponent_hostname.as_str(), args.opponent_port), - // ); - let config = http_config!(PlayerX: (args.hostname.as_str(), args.port), - PlayerO: (args.opponent_hostname.as_str(), args.opponent_port)); + let config = transport_config!(PlayerX: (args.hostname.as_str().to_string(), args.port), + PlayerO: (args.opponent_hostname.as_str().to_string(), args.opponent_port)); let transport = HttpTransport::new(PlayerX, &config); let projector = Projector::new(PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { @@ -310,14 +306,8 @@ fn main() { }); } 'O' => { - // let mut config = HttpConfig::::new(); - // config.insert(PlayerO, (args.hostname.as_str(), args.port)); - // config.insert( - // PlayerX, - // (args.opponent_hostname.as_str(), args.opponent_port), - // ); - let config = http_config!(PlayerO: (args.hostname.as_str(), args.port), - PlayerX: (args.opponent_hostname.as_str(), args.opponent_port)); + let config = transport_config!(PlayerO: (args.hostname.as_str().to_string(), args.port), + PlayerX: (args.opponent_hostname.as_str().to_string(), args.opponent_port)); let transport = HttpTransport::new(PlayerO, &config); let projector = Projector::new(PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index a7f854b..28705e5 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -2,3 +2,30 @@ pub mod http; pub mod local; + +/// A generic struct for configuration of `Transport`. +#[derive(Clone)] +pub struct TransportConfig { + /// The information about locations + pub info: std::collections::HashMap, + /// The struct is parametrized by the location set (`L`). + pub location_set: std::marker::PhantomData, +} + +/// This macro makes a `TransportConfig`. +#[macro_export] +macro_rules! transport_config { + ( $( $loc:ident : $val:expr ),* $(,)? ) => { + { + let mut config = std::collections::HashMap::new(); + $( + config.insert($loc::name().to_string(), $val); + )* + + $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _> { + info: config, + location_set: core::marker::PhantomData + } + } + }; +} diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 7602a41..7c9530c 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,6 +12,9 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; +use crate::transport::TransportConfig; +use crate::transport_config; + use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, utils::queue::BlockingQueue, @@ -29,23 +32,31 @@ pub struct HttpConfig { pub location_set: PhantomData, } -/// This macro makes a `HttpConfig`. -#[macro_export] -macro_rules! http_config { - ( $( $loc:ident : ( $host:expr, $port:expr ) ),* $(,)? ) => { - { - let mut config = std::collections::HashMap::new(); - $( - config.insert($loc::name().to_string(), ($host.to_string(), $port)); - )* - - $crate::transport::http::HttpConfig::<$crate::LocationSet!($( $loc ),*)> { - info: config, - location_set: core::marker::PhantomData - } - } - }; -} +// #[derive(Clone)] +// pub struct TransportConfig { +// /// The information about locations +// pub info: HashMap, +// /// The struct is parametrized by the location set (`L`). +// pub location_set: PhantomData, +// } + +// /// This macro makes a `HttpConfig`. +// #[macro_export] +// macro_rules! http_config { +// ( $( $loc:ident : ( $host:expr, $port:expr ) ),* $(,)? ) => { +// { +// let mut config = std::collections::HashMap::new(); +// $( +// config.insert($loc::name().to_string(), ($host.to_string(), $port)); +// )* + +// $crate::transport::http::HttpConfig::<$crate::LocationSet!($( $loc ),*)> { +// info: config, +// location_set: core::marker::PhantomData +// } +// } +// }; +// } /// The HTTP transport. pub struct HttpTransport { @@ -59,7 +70,10 @@ pub struct HttpTransport { impl HttpTransport { /// Creates a new `HttpTransport` instance from the projection target and a configuration. - pub fn new(_loc: C, http_config: &HttpConfig) -> Self + pub fn new( + _loc: C, + http_config: &TransportConfig, + ) -> Self where C: Member, { @@ -123,7 +137,7 @@ impl HttpTransport { impl Drop for HttpTransport { fn drop(&mut self) { self.server.unblock(); - self.join_handle.take().map(thread::JoinHandle::join); + // self.join_handle.take().map(thread::JoinHandle::join); } } @@ -169,9 +183,9 @@ mod tests { let v = 42; let (signal, wait) = mpsc::channel::<()>(); - let config = http_config!( - Alice: ("localhost", 9010), - Bob: ("localhost", 9011) + let config = transport_config!( + Alice: ("localhost".to_string(), 9010), + Bob: ("localhost".to_string(), 9011) ); let mut handles = Vec::new(); @@ -202,9 +216,9 @@ mod tests { let v = 42; let (signal, wait) = mpsc::channel::<()>(); - let config = http_config!( - Alice: ("localhost", 9020), - Bob: ("localhost", 9021) + let config = transport_config!( + Alice: ("localhost".to_string(), 9020), + Bob: ("localhost".to_string(), 9021) ); let mut handles = Vec::new(); diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index d7629ba..25646d3 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,6 +7,9 @@ use serde_json; use std::marker::PhantomData; +use crate::transport::TransportConfig; +use crate::transport_config; + use crate::core::{HList, Portable, Transport}; use crate::utils::queue::BlockingQueue; @@ -26,7 +29,7 @@ pub struct LocalTransport { impl LocalTransport { /// Creates a new `LocalTransport` instance from a list of locations. - pub fn new() -> Self { + pub fn new(local_config: &TransportConfig) -> Self { let mut queue_map: QueueMap = HashMap::new(); let locations_list = L::to_string_list(); @@ -85,7 +88,14 @@ mod tests { #[test] fn test_local_transport() { let v = 42; - let transport = LocalTransport::::new(); + + let config = transport_config!( + Alice: (), + Bob: () + ); + + let transport = LocalTransport::new(&config); + let mut handles = Vec::new(); { let transport = transport.clone(); From 642b971dde560f9023205965e77260ba83b946be Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 19:44:00 -0700 Subject: [PATCH 33/75] WIP: program doesn't terminate --- chorus_lib/src/transport.rs | 43 ++++++++++++++++++++++++---- chorus_lib/src/transport/http.rs | 47 ++++++++++++++++++++++--------- chorus_lib/src/transport/local.rs | 19 +++++++++++-- 3 files changed, 86 insertions(+), 23 deletions(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 28705e5..d38b24e 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -5,27 +5,58 @@ pub mod local; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig { +pub struct TransportConfig { /// The information about locations pub info: std::collections::HashMap, + pub target_info: (String, TargetInfoType), /// The struct is parametrized by the location set (`L`). pub location_set: std::marker::PhantomData, + } /// This macro makes a `TransportConfig`. +// #[macro_export] +// macro_rules! transport_config { +// ( $( $loc:ident : $val:expr ),* $(,)? ) => { +// { +// let mut config = std::collections::HashMap::new(); +// $( +// config.insert($loc::name().to_string(), $val); +// )* + +// $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _> { +// info: config, +// location_set: core::marker::PhantomData +// } +// } +// }; +// } + + +/// This macro makes a `TransportConfig`; V2. #[macro_export] macro_rules! transport_config { - ( $( $loc:ident : $val:expr ),* $(,)? ) => { + ( $choreography_loc:ident, $( $loc:ident : $val:expr ),* $(,)? ) => { { + let choreography_name = $choreography_loc::name().to_string(); let mut config = std::collections::HashMap::new(); + let mut target_info = None; $( - config.insert($loc::name().to_string(), $val); + if $loc::name().to_string() != choreography_name{ + config.insert($loc::name().to_string(), $val); + } else { + println!("heyyyy"); + target_info = Some($val); + } )* - $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _> { + // println!("{}, {}", target_info.unwrap().0, target.info.unwrap().1); + + $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _, _> { info: config, - location_set: core::marker::PhantomData + location_set: core::marker::PhantomData, + target_info: (choreography_name, target_info.unwrap()), } } }; -} +} \ No newline at end of file diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 7c9530c..b5d6d3e 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -72,7 +72,7 @@ impl HttpTransport { /// Creates a new `HttpTransport` instance from the projection target and a configuration. pub fn new( _loc: C, - http_config: &TransportConfig, + http_config: &TransportConfig, ) -> Self where C: Member, @@ -89,7 +89,7 @@ impl HttpTransport { Arc::new(m) }; - let (hostname, port) = info.get(at).unwrap(); + let (_, (hostname, port)) = &http_config.target_info; let server = Arc::new(Server::http(format!("{}:{}", hostname, port)).unwrap()); let join_handle = Some({ let server = server.clone(); @@ -183,14 +183,16 @@ mod tests { let v = 42; let (signal, wait) = mpsc::channel::<()>(); - let config = transport_config!( - Alice: ("localhost".to_string(), 9010), - Bob: ("localhost".to_string(), 9011) - ); let mut handles = Vec::new(); { - let config = config.clone(); + // let config = config.clone(); + let config = transport_config!( + Alice, + Alice: ("localhost".to_string(), 9010), + Bob: ("127.0.0.1".to_string(), 9011) + ); + handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start let transport = HttpTransport::new(Alice, &config); @@ -198,7 +200,13 @@ mod tests { })); } { - let config = config.clone(); + // let config = config.clone(); + let config = transport_config!( + Bob, + Alice: ("127.0.0.1".to_string(), 9010), + Bob: ("localhost".to_string(), 9011) + ); + handles.push(thread::spawn(move || { let transport = HttpTransport::new(Bob, &config); signal.send(()).unwrap(); @@ -216,14 +224,20 @@ mod tests { let v = 42; let (signal, wait) = mpsc::channel::<()>(); - let config = transport_config!( - Alice: ("localhost".to_string(), 9020), - Bob: ("localhost".to_string(), 9021) - ); + // let config = transport_config!( + // Alice, + // Alice: ("localhost".to_string(), 9020), + // Bob: ("localhost".to_string(), 9021) + // ); let mut handles = Vec::new(); { - let config = config.clone(); + let config = transport_config!( + Alice, + Alice: ("localhost".to_string(), 9020), + Bob: ("127.0.0.1".to_string(), 9021) + ); + // let config = config.clone(); handles.push(thread::spawn(move || { signal.send(()).unwrap(); let transport = HttpTransport::new(Alice, &config); @@ -231,7 +245,12 @@ mod tests { })); } { - let config = config.clone(); + // let config = config.clone(); + let config = transport_config!( + Alice, + Alice: ("127.0.0.1".to_string(), 9020), + Bob: ("localhost".to_string(), 9021) + ); handles.push(thread::spawn(move || { // wait for Alice to start, which forces Alice to retry wait.recv().unwrap(); diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 25646d3..bde1b59 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -29,7 +29,7 @@ pub struct LocalTransport { impl LocalTransport { /// Creates a new `LocalTransport` instance from a list of locations. - pub fn new(local_config: &TransportConfig) -> Self { + pub fn new(local_config: &TransportConfig) -> Self { let mut queue_map: QueueMap = HashMap::new(); let locations_list = L::to_string_list(); @@ -90,6 +90,7 @@ mod tests { let v = 42; let config = transport_config!( + Alice, Alice: (), Bob: () ); @@ -98,13 +99,25 @@ mod tests { let mut handles = Vec::new(); { - let transport = transport.clone(); + let config = transport_config!( + Alice, + Alice: (), + Bob: () + ); + // let transport = transport.clone(); + let transport = LocalTransport::new(&config); handles.push(thread::spawn(move || { transport.send::(Alice::name(), Bob::name(), &v); })); } { - let transport = transport.clone(); + let config = transport_config!( + Bob, + Alice: (), + Bob: () + ); + // let transport = transport.clone(); + let transport = LocalTransport::new(&config); handles.push(thread::spawn(move || { let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); From e486a7d18ca0bc5bbb49e1c8d5fcd5439d034258 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 23:18:55 -0700 Subject: [PATCH 34/75] WIP: the library tests work --- chorus_lib/src/transport.rs | 4 + chorus_lib/src/transport/http.rs | 122 ++++++++++++++++-------------- chorus_lib/src/transport/local.rs | 59 +++++++++------ 3 files changed, 103 insertions(+), 82 deletions(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index d38b24e..f331326 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,14 +3,18 @@ pub mod http; pub mod local; +use std::{collections::HashMap, sync::Arc}; + /// A generic struct for configuration of `Transport`. #[derive(Clone)] pub struct TransportConfig { /// The information about locations pub info: std::collections::HashMap, + /// The information about the target choreography pub target_info: (String, TargetInfoType), /// The struct is parametrized by the location set (`L`). pub location_set: std::marker::PhantomData, + // pub transport_channel: TransportChannel, } diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index b5d6d3e..d196b11 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,8 +12,8 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; -use crate::transport::TransportConfig; -use crate::transport_config; +use crate::transport::{TransportConfig}; +use crate::{transport_config, LocationSet}; use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, @@ -32,40 +32,25 @@ pub struct HttpConfig { pub location_set: PhantomData, } -// #[derive(Clone)] -// pub struct TransportConfig { -// /// The information about locations -// pub info: HashMap, -// /// The struct is parametrized by the location set (`L`). -// pub location_set: PhantomData, -// } - -// /// This macro makes a `HttpConfig`. -// #[macro_export] -// macro_rules! http_config { -// ( $( $loc:ident : ( $host:expr, $port:expr ) ),* $(,)? ) => { -// { -// let mut config = std::collections::HashMap::new(); -// $( -// config.insert($loc::name().to_string(), ($host.to_string(), $port)); -// )* - -// $crate::transport::http::HttpConfig::<$crate::LocationSet!($( $loc ),*)> { -// info: config, -// location_set: core::marker::PhantomData -// } -// } -// }; -// } + +/// A Transport channel used between multiple `Transport`s. +#[derive(Clone)] +pub struct TransportChannel{ + /// The location set where the channel is defined on. + pub location_set: std::marker::PhantomData, + queue_map: Arc>>, +} + /// The HTTP transport. pub struct HttpTransport { config: HashMap, agent: Agent, - queue_map: Arc>>, + // queue_map: Arc>>, server: Arc, join_handle: Option>, location_set: PhantomData, + transport_channel: TransportChannel, } impl HttpTransport { @@ -73,27 +58,19 @@ impl HttpTransport { pub fn new( _loc: C, http_config: &TransportConfig, + transport_channel: TransportChannel ) -> Self where C: Member, { let info = &http_config.info; - let at = C::name(); - let locs = Vec::from_iter(info.keys().map(|s| s.clone())); - - let queue_map = { - let mut m = HashMap::new(); - for loc in &locs { - m.insert(loc.to_string(), BlockingQueue::new()); - } - Arc::new(m) - }; let (_, (hostname, port)) = &http_config.target_info; let server = Arc::new(Server::http(format!("{}:{}", hostname, port)).unwrap()); let join_handle = Some({ let server = server.clone(); - let queue_map = queue_map.clone(); + let queue_map = transport_channel.queue_map.clone(); + thread::spawn(move || { for mut request in server.incoming_requests() { let mut body = String::new(); @@ -126,10 +103,10 @@ impl HttpTransport { Self { config: info.clone(), agent, - queue_map, join_handle, server, location_set: PhantomData, + transport_channel, } } } @@ -137,7 +114,7 @@ impl HttpTransport { impl Drop for HttpTransport { fn drop(&mut self) { self.server.unblock(); - // self.join_handle.take().map(thread::JoinHandle::join); + self.join_handle.take().map(thread::JoinHandle::join); } } @@ -158,7 +135,7 @@ impl Transport for HttpTransport { } fn receive(&self, from: &str, _at: &str) -> V { - let str = self.queue_map.get(from).unwrap().pop(); + let str = self.transport_channel.queue_map.get(from).unwrap().pop(); serde_json::from_str(&str).unwrap() } } @@ -184,18 +161,33 @@ mod tests { let (signal, wait) = mpsc::channel::<()>(); + let queue_map = { + let mut m = HashMap::new(); + for loc in &vec![Alice::name(), Bob::name()] { + m.insert(loc.to_string(), BlockingQueue::new()); + } + Arc::new(m) + }; + + let transport_channel = TransportChannel:: { + location_set: PhantomData, + queue_map: queue_map, + }; + let mut handles = Vec::new(); { // let config = config.clone(); let config = transport_config!( Alice, Alice: ("localhost".to_string(), 9010), - Bob: ("127.0.0.1".to_string(), 9011) + Bob: ("localhost".to_string(), 9011) ); + let transport_channel = transport_channel.clone(); + handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start - let transport = HttpTransport::new(Alice, &config); + let transport = HttpTransport::new(Alice, &config, transport_channel); transport.send::(Alice::name(), Bob::name(), &v); })); } @@ -203,12 +195,15 @@ mod tests { // let config = config.clone(); let config = transport_config!( Bob, - Alice: ("127.0.0.1".to_string(), 9010), + Alice: ("localhost".to_string(), 9010), Bob: ("localhost".to_string(), 9011) ); + + + let transport_channel = transport_channel.clone(); handles.push(thread::spawn(move || { - let transport = HttpTransport::new(Bob, &config); + let transport = HttpTransport::new(Bob, &config, transport_channel); signal.send(()).unwrap(); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); @@ -224,38 +219,49 @@ mod tests { let v = 42; let (signal, wait) = mpsc::channel::<()>(); - // let config = transport_config!( - // Alice, - // Alice: ("localhost".to_string(), 9020), - // Bob: ("localhost".to_string(), 9021) - // ); + let queue_map = { + let mut m = HashMap::new(); + for loc in &vec![Alice::name(), Bob::name()] { + m.insert(loc.to_string(), BlockingQueue::new()); + } + Arc::new(m) + }; + + let transport_channel = TransportChannel:: { + location_set: PhantomData, + queue_map, + }; let mut handles = Vec::new(); { let config = transport_config!( Alice, Alice: ("localhost".to_string(), 9020), - Bob: ("127.0.0.1".to_string(), 9021) + Bob: ("localhost".to_string(), 9021) ); - // let config = config.clone(); + + let transport_channel = transport_channel.clone(); + handles.push(thread::spawn(move || { signal.send(()).unwrap(); - let transport = HttpTransport::new(Alice, &config); + let transport = HttpTransport::new(Alice, &config, transport_channel); transport.send::(Alice::name(), Bob::name(), &v); })); } { - // let config = config.clone(); let config = transport_config!( - Alice, - Alice: ("127.0.0.1".to_string(), 9020), + Bob, + Alice: ("localhost".to_string(), 9020), Bob: ("localhost".to_string(), 9021) ); + + let transport_channel = transport_channel.clone(); + handles.push(thread::spawn(move || { // wait for Alice to start, which forces Alice to retry wait.recv().unwrap(); sleep(Duration::from_millis(100)); - let transport = HttpTransport::new(Bob, &config); + let transport = HttpTransport::new(Bob, &config, transport_channel); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); })); diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index bde1b59..5335850 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,8 +7,8 @@ use serde_json; use std::marker::PhantomData; -use crate::transport::TransportConfig; -use crate::transport_config; +use crate::transport::{TransportConfig}; +use crate::{transport_config, LocationSet}; use crate::core::{HList, Portable, Transport}; use crate::utils::queue::BlockingQueue; @@ -23,31 +23,33 @@ type QueueMap = HashMap>>; #[derive(Clone)] pub struct LocalTransport { internal_locations: Vec, - queue_map: Arc, + // queue_map: Arc, location_set: PhantomData, + transport_channel: TransportChannel, +} + +/// A Transport channel used between multiple `Transport`s. +#[derive(Clone)] +pub struct TransportChannel{ + /// The location set where the channel is defined on. + pub location_set: std::marker::PhantomData, + queue_map: Arc, } impl LocalTransport { /// Creates a new `LocalTransport` instance from a list of locations. - pub fn new(local_config: &TransportConfig) -> Self { - let mut queue_map: QueueMap = HashMap::new(); + pub fn new(_local_config: &TransportConfig, transport_channel: TransportChannel) -> Self { + // let mut queue_map: QueueMap = HashMap::new(); let locations_list = L::to_string_list(); - for sender in locations_list.clone() { - let mut n = HashMap::new(); - for receiver in locations_list.clone() { - n.insert(receiver.to_string(), BlockingQueue::new()); - } - queue_map.insert(sender.to_string(), n); - } let mut locations_vec = Vec::new(); for loc in locations_list.clone() { locations_vec.push(loc.to_string()); } LocalTransport { - queue_map: Arc::new(queue_map), internal_locations: locations_vec, location_set: PhantomData, + transport_channel, } } } @@ -59,7 +61,7 @@ impl Transport for LocalTransport { fn send(&self, from: &str, to: &str, data: &T) -> () { let data = serde_json::to_string(data).unwrap(); - self.queue_map + self.transport_channel.queue_map .get(from) .unwrap() .get(to) @@ -68,7 +70,7 @@ impl Transport for LocalTransport { } fn receive(&self, from: &str, at: &str) -> T { - let data = self.queue_map.get(from).unwrap().get(at).unwrap().pop(); + let data = self.transport_channel.queue_map.get(from).unwrap().get(at).unwrap().pop(); serde_json::from_str(&data).unwrap() } } @@ -88,14 +90,21 @@ mod tests { #[test] fn test_local_transport() { let v = 42; + + let mut queue_map: QueueMap = HashMap::new(); + for sender in &vec![Alice::name(), Bob::name()] { + let mut n = HashMap::new(); + for receiver in &vec![Alice::name(), Bob::name()] { + n.insert(receiver.to_string(), BlockingQueue::new()); + } + queue_map.insert(sender.to_string(), n); + } - let config = transport_config!( - Alice, - Alice: (), - Bob: () - ); - let transport = LocalTransport::new(&config); + let transport_channel = TransportChannel:: { + location_set: PhantomData, + queue_map: Arc::new(queue_map), + }; let mut handles = Vec::new(); { @@ -104,8 +113,9 @@ mod tests { Alice: (), Bob: () ); - // let transport = transport.clone(); - let transport = LocalTransport::new(&config); + let transport_channel = transport_channel.clone(); + + let transport = LocalTransport::new(&config, transport_channel); handles.push(thread::spawn(move || { transport.send::(Alice::name(), Bob::name(), &v); })); @@ -117,7 +127,8 @@ mod tests { Bob: () ); // let transport = transport.clone(); - let transport = LocalTransport::new(&config); + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); handles.push(thread::spawn(move || { let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); From 5df2e948fa8ad54e72b944c1791cdf739c04cd1e Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 00:48:55 -0700 Subject: [PATCH 35/75] Simplify things a bit --- chorus_lib/src/transport.rs | 23 ++++++++++- chorus_lib/src/transport/http.rs | 63 +++++++++++-------------------- chorus_lib/src/transport/local.rs | 46 +++++++++++----------- 3 files changed, 65 insertions(+), 67 deletions(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index f331326..eba7ad8 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,7 +3,7 @@ pub mod http; pub mod local; -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; /// A generic struct for configuration of `Transport`. #[derive(Clone)] @@ -18,6 +18,26 @@ pub struct TransportConfig { } +/// A Transport channel used between multiple `Transport`s. +pub struct TransportChannel{ + /// The location set where the channel is defined on. + pub location_set: std::marker::PhantomData, + queue_map: Arc, +} + +impl Clone for TransportChannel +where + L: crate::core::HList, + T: Send + Sync, +{ + fn clone(&self) -> Self { + Self { + location_set: self.location_set.clone(), + queue_map: self.queue_map.clone(), // This clones the Arc, not the underlying data + } + } +} + /// This macro makes a `TransportConfig`. // #[macro_export] // macro_rules! transport_config { @@ -49,7 +69,6 @@ macro_rules! transport_config { if $loc::name().to_string() != choreography_name{ config.insert($loc::name().to_string(), $val); } else { - println!("heyyyy"); target_info = Some($val); } )* diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index d196b11..60c50d6 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,7 +12,7 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; -use crate::transport::{TransportConfig}; +use crate::transport::{TransportConfig, TransportChannel}; use crate::{transport_config, LocationSet}; use crate::{ @@ -20,6 +20,9 @@ use crate::{ utils::queue::BlockingQueue, }; +type QueueMap = HashMap>; + + /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; @@ -32,33 +35,37 @@ pub struct HttpConfig { pub location_set: PhantomData, } - -/// A Transport channel used between multiple `Transport`s. -#[derive(Clone)] -pub struct TransportChannel{ - /// The location set where the channel is defined on. - pub location_set: std::marker::PhantomData, - queue_map: Arc>>, -} - - /// The HTTP transport. pub struct HttpTransport { config: HashMap, agent: Agent, - // queue_map: Arc>>, server: Arc, join_handle: Option>, location_set: PhantomData, - transport_channel: TransportChannel, + transport_channel: TransportChannel>, } impl HttpTransport { + pub fn transport_channel() -> TransportChannel> { + let queue_map = { + let mut m = HashMap::new(); + for loc in L::to_string_list() { + m.insert(loc.to_string(), BlockingQueue::new()); + } + Arc::new(m) + }; + + TransportChannel { + location_set: PhantomData, + queue_map: queue_map.into(), + } + } + /// Creates a new `HttpTransport` instance from the projection target and a configuration. pub fn new( _loc: C, http_config: &TransportConfig, - transport_channel: TransportChannel + transport_channel: TransportChannel> ) -> Self where C: Member, @@ -161,22 +168,10 @@ mod tests { let (signal, wait) = mpsc::channel::<()>(); - let queue_map = { - let mut m = HashMap::new(); - for loc in &vec![Alice::name(), Bob::name()] { - m.insert(loc.to_string(), BlockingQueue::new()); - } - Arc::new(m) - }; - - let transport_channel = TransportChannel:: { - location_set: PhantomData, - queue_map: queue_map, - }; + let transport_channel = HttpTransport::::transport_channel(); let mut handles = Vec::new(); { - // let config = config.clone(); let config = transport_config!( Alice, Alice: ("localhost".to_string(), 9010), @@ -192,7 +187,6 @@ mod tests { })); } { - // let config = config.clone(); let config = transport_config!( Bob, Alice: ("localhost".to_string(), 9010), @@ -219,18 +213,7 @@ mod tests { let v = 42; let (signal, wait) = mpsc::channel::<()>(); - let queue_map = { - let mut m = HashMap::new(); - for loc in &vec![Alice::name(), Bob::name()] { - m.insert(loc.to_string(), BlockingQueue::new()); - } - Arc::new(m) - }; - - let transport_channel = TransportChannel:: { - location_set: PhantomData, - queue_map, - }; + let transport_channel = HttpTransport::::transport_channel(); let mut handles = Vec::new(); { diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 5335850..eb87c13 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,7 +7,7 @@ use serde_json; use std::marker::PhantomData; -use crate::transport::{TransportConfig}; +use crate::transport::{TransportConfig, TransportChannel}; use crate::{transport_config, LocationSet}; use crate::core::{HList, Portable, Transport}; @@ -25,27 +25,36 @@ pub struct LocalTransport { internal_locations: Vec, // queue_map: Arc, location_set: PhantomData, - transport_channel: TransportChannel, + transport_channel: TransportChannel>, } -/// A Transport channel used between multiple `Transport`s. -#[derive(Clone)] -pub struct TransportChannel{ - /// The location set where the channel is defined on. - pub location_set: std::marker::PhantomData, - queue_map: Arc, -} impl LocalTransport { + pub fn transport_channel() -> TransportChannel> { + let mut queue_map: QueueMap = HashMap::new(); + for sender in L::to_string_list() { + let mut n = HashMap::new(); + for receiver in L::to_string_list() { + n.insert(receiver.to_string(), BlockingQueue::new()); + } + queue_map.insert(sender.to_string(), n); + } + + TransportChannel { + location_set: PhantomData, + queue_map: Arc::new(queue_map.into()), + } + } + /// Creates a new `LocalTransport` instance from a list of locations. - pub fn new(_local_config: &TransportConfig, transport_channel: TransportChannel) -> Self { - // let mut queue_map: QueueMap = HashMap::new(); + pub fn new(_local_config: &TransportConfig, transport_channel: TransportChannel>) -> Self { let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); for loc in locations_list.clone() { locations_vec.push(loc.to_string()); } + LocalTransport { internal_locations: locations_vec, location_set: PhantomData, @@ -91,20 +100,7 @@ mod tests { fn test_local_transport() { let v = 42; - let mut queue_map: QueueMap = HashMap::new(); - for sender in &vec![Alice::name(), Bob::name()] { - let mut n = HashMap::new(); - for receiver in &vec![Alice::name(), Bob::name()] { - n.insert(receiver.to_string(), BlockingQueue::new()); - } - queue_map.insert(sender.to_string(), n); - } - - - let transport_channel = TransportChannel:: { - location_set: PhantomData, - queue_map: Arc::new(queue_map), - }; + let transport_channel = LocalTransport::::transport_channel(); let mut handles = Vec::new(); { From d7e593c206f602e969c2af8cec57535465dd6a88 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 00:54:36 -0700 Subject: [PATCH 36/75] Remove redundant target-location argument --- chorus_lib/src/transport.rs | 9 +++++---- chorus_lib/src/transport/http.rs | 11 +++++------ chorus_lib/src/transport/local.rs | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index eba7ad8..2d113d4 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -4,14 +4,15 @@ pub mod http; pub mod local; use std::sync::Arc; +use crate::core::ChoreographyLocation; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig { +pub struct TransportConfig { /// The information about locations pub info: std::collections::HashMap, /// The information about the target choreography - pub target_info: (String, TargetInfoType), + pub target_info: (TargetLocation, TargetInfoType), /// The struct is parametrized by the location set (`L`). pub location_set: std::marker::PhantomData, // pub transport_channel: TransportChannel, @@ -75,10 +76,10 @@ macro_rules! transport_config { // println!("{}, {}", target_info.unwrap().0, target.info.unwrap().1); - $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _, _> { + $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _, _, _> { info: config, location_set: core::marker::PhantomData, - target_info: (choreography_name, target_info.unwrap()), + target_info: ($choreography_loc, target_info.unwrap()), } } }; diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 60c50d6..cf38cff 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -63,8 +63,7 @@ impl HttpTransport { /// Creates a new `HttpTransport` instance from the projection target and a configuration. pub fn new( - _loc: C, - http_config: &TransportConfig, + http_config: &TransportConfig, transport_channel: TransportChannel> ) -> Self where @@ -182,7 +181,7 @@ mod tests { handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start - let transport = HttpTransport::new(Alice, &config, transport_channel); + let transport = HttpTransport::new(&config, transport_channel); transport.send::(Alice::name(), Bob::name(), &v); })); } @@ -197,7 +196,7 @@ mod tests { let transport_channel = transport_channel.clone(); handles.push(thread::spawn(move || { - let transport = HttpTransport::new(Bob, &config, transport_channel); + let transport = HttpTransport::new(&config, transport_channel); signal.send(()).unwrap(); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); @@ -227,7 +226,7 @@ mod tests { handles.push(thread::spawn(move || { signal.send(()).unwrap(); - let transport = HttpTransport::new(Alice, &config, transport_channel); + let transport = HttpTransport::new(&config, transport_channel); transport.send::(Alice::name(), Bob::name(), &v); })); } @@ -244,7 +243,7 @@ mod tests { // wait for Alice to start, which forces Alice to retry wait.recv().unwrap(); sleep(Duration::from_millis(100)); - let transport = HttpTransport::new(Bob, &config, transport_channel); + let transport = HttpTransport::new(&config, transport_channel); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); })); diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index eb87c13..e98addf 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -10,7 +10,7 @@ use std::marker::PhantomData; use crate::transport::{TransportConfig, TransportChannel}; use crate::{transport_config, LocationSet}; -use crate::core::{HList, Portable, Transport}; +use crate::core::{HList, Portable, Transport, ChoreographyLocation}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; @@ -47,7 +47,7 @@ impl LocalTransport { } /// Creates a new `LocalTransport` instance from a list of locations. - pub fn new(_local_config: &TransportConfig, transport_channel: TransportChannel>) -> Self { + pub fn new(_local_config: &TransportConfig, transport_channel: TransportChannel>) -> Self { let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); From d835717d3c524d5c5270589fd6edcdb3bd800a44 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 01:11:54 -0700 Subject: [PATCH 37/75] Fix some warnings and remove an extra layer of Arc --- chorus_lib/examples/bookseller2.rs | 2 +- chorus_lib/src/transport.rs | 36 +++++++++--------------------- chorus_lib/src/transport/http.rs | 36 +++++++++++------------------- chorus_lib/src/transport/local.rs | 34 +++++++++++++++++++--------- 4 files changed, 47 insertions(+), 61 deletions(-) diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 8f99ec1..7e29b64 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -3,12 +3,12 @@ extern crate chorus_lib; use std::thread; use std::{collections::HashMap, sync::Arc}; +use chorus_lib::transport_config; use chorus_lib::LocationSet; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, transport::local::LocalTransport, }; -use chorus_lib::transport_config; use chrono::NaiveDate; #[derive(ChoreographyLocation)] diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 2d113d4..b0e6d6f 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,24 +3,27 @@ pub mod http; pub mod local; -use std::sync::Arc; use crate::core::ChoreographyLocation; +use std::sync::Arc; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig { +pub struct TransportConfig< + L: crate::core::HList, + InfoType, + TargetLocation: ChoreographyLocation, + TargetInfoType, +> { /// The information about locations pub info: std::collections::HashMap, /// The information about the target choreography pub target_info: (TargetLocation, TargetInfoType), /// The struct is parametrized by the location set (`L`). pub location_set: std::marker::PhantomData, - // pub transport_channel: TransportChannel, - } /// A Transport channel used between multiple `Transport`s. -pub struct TransportChannel{ +pub struct TransportChannel { /// The location set where the channel is defined on. pub location_set: std::marker::PhantomData, queue_map: Arc, @@ -34,31 +37,12 @@ where fn clone(&self) -> Self { Self { location_set: self.location_set.clone(), - queue_map: self.queue_map.clone(), // This clones the Arc, not the underlying data + queue_map: self.queue_map.clone(), // This clones the Arc, not the underlying data } } } /// This macro makes a `TransportConfig`. -// #[macro_export] -// macro_rules! transport_config { -// ( $( $loc:ident : $val:expr ),* $(,)? ) => { -// { -// let mut config = std::collections::HashMap::new(); -// $( -// config.insert($loc::name().to_string(), $val); -// )* - -// $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _> { -// info: config, -// location_set: core::marker::PhantomData -// } -// } -// }; -// } - - -/// This macro makes a `TransportConfig`; V2. #[macro_export] macro_rules! transport_config { ( $choreography_loc:ident, $( $loc:ident : $val:expr ),* $(,)? ) => { @@ -83,4 +67,4 @@ macro_rules! transport_config { } } }; -} \ No newline at end of file +} diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index cf38cff..8e825e3 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,7 +12,8 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; -use crate::transport::{TransportConfig, TransportChannel}; +use crate::transport::{TransportChannel, TransportConfig}; +#[cfg(test)] use crate::{transport_config, LocationSet}; use crate::{ @@ -22,19 +23,9 @@ use crate::{ type QueueMap = HashMap>; - /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; -/// A wrapper for HashMap -#[derive(Clone)] -pub struct HttpConfig { - /// The information about locations - pub info: HashMap, - /// The struct is parametrized by the location set (`L`). - pub location_set: PhantomData, -} - /// The HTTP transport. pub struct HttpTransport { config: HashMap, @@ -42,29 +33,30 @@ pub struct HttpTransport { server: Arc, join_handle: Option>, location_set: PhantomData, - transport_channel: TransportChannel>, + transport_channel: TransportChannel, } impl HttpTransport { - pub fn transport_channel() -> TransportChannel> { + /// Creates a `TransportChannel`. + pub fn transport_channel() -> TransportChannel { let queue_map = { let mut m = HashMap::new(); for loc in L::to_string_list() { m.insert(loc.to_string(), BlockingQueue::new()); } - Arc::new(m) + m }; TransportChannel { location_set: PhantomData, - queue_map: queue_map.into(), + queue_map: Arc::new(queue_map.into()), } } /// Creates a new `HttpTransport` instance from the projection target and a configuration. pub fn new( http_config: &TransportConfig, - transport_channel: TransportChannel> + transport_channel: TransportChannel, ) -> Self where C: Member, @@ -173,12 +165,12 @@ mod tests { { let config = transport_config!( Alice, - Alice: ("localhost".to_string(), 9010), + Alice: ("0.0.0.0".to_string(), 9010), Bob: ("localhost".to_string(), 9011) ); let transport_channel = transport_channel.clone(); - + handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start let transport = HttpTransport::new(&config, transport_channel); @@ -189,11 +181,9 @@ mod tests { let config = transport_config!( Bob, Alice: ("localhost".to_string(), 9010), - Bob: ("localhost".to_string(), 9011) + Bob: ("0.0.0.0".to_string(), 9011) ); - - let transport_channel = transport_channel.clone(); handles.push(thread::spawn(move || { let transport = HttpTransport::new(&config, transport_channel); @@ -218,7 +208,7 @@ mod tests { { let config = transport_config!( Alice, - Alice: ("localhost".to_string(), 9020), + Alice: ("0.0.0.0".to_string(), 9020), Bob: ("localhost".to_string(), 9021) ); @@ -234,7 +224,7 @@ mod tests { let config = transport_config!( Bob, Alice: ("localhost".to_string(), 9020), - Bob: ("localhost".to_string(), 9021) + Bob: ("0.0.0.0".to_string(), 9021) ); let transport_channel = transport_channel.clone(); diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index e98addf..2f425a7 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,10 +7,11 @@ use serde_json; use std::marker::PhantomData; -use crate::transport::{TransportConfig, TransportChannel}; +use crate::transport::{TransportChannel, TransportConfig}; +#[cfg(test)] use crate::{transport_config, LocationSet}; -use crate::core::{HList, Portable, Transport, ChoreographyLocation}; +use crate::core::{ChoreographyLocation, HList, Portable, Transport}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; @@ -25,12 +26,12 @@ pub struct LocalTransport { internal_locations: Vec, // queue_map: Arc, location_set: PhantomData, - transport_channel: TransportChannel>, + transport_channel: TransportChannel, } - impl LocalTransport { - pub fn transport_channel() -> TransportChannel> { + /// Creates a `TransportChannel`. + pub fn transport_channel() -> TransportChannel { let mut queue_map: QueueMap = HashMap::new(); for sender in L::to_string_list() { let mut n = HashMap::new(); @@ -46,15 +47,18 @@ impl LocalTransport { } } - /// Creates a new `LocalTransport` instance from a list of locations. - pub fn new(_local_config: &TransportConfig, transport_channel: TransportChannel>) -> Self { + /// Creates a new `LocalTransport` instance from a `TransportConfig` and a `TransportChannel`. + pub fn new( + _local_config: &TransportConfig, + transport_channel: TransportChannel, + ) -> Self { let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); for loc in locations_list.clone() { locations_vec.push(loc.to_string()); } - + LocalTransport { internal_locations: locations_vec, location_set: PhantomData, @@ -70,7 +74,8 @@ impl Transport for LocalTransport { fn send(&self, from: &str, to: &str, data: &T) -> () { let data = serde_json::to_string(data).unwrap(); - self.transport_channel.queue_map + self.transport_channel + .queue_map .get(from) .unwrap() .get(to) @@ -79,7 +84,14 @@ impl Transport for LocalTransport { } fn receive(&self, from: &str, at: &str) -> T { - let data = self.transport_channel.queue_map.get(from).unwrap().get(at).unwrap().pop(); + let data = self + .transport_channel + .queue_map + .get(from) + .unwrap() + .get(at) + .unwrap() + .pop(); serde_json::from_str(&data).unwrap() } } @@ -99,7 +111,7 @@ mod tests { #[test] fn test_local_transport() { let v = 42; - + let transport_channel = LocalTransport::::transport_channel(); let mut handles = Vec::new(); From 65096dd438e81645c7907f2a4a5d77943a96a31b Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 02:15:58 -0700 Subject: [PATCH 38/75] Use Equal trait instead of assuming equality of the location sets --- chorus_lib/src/core.rs | 16 ++++++++++++++++ chorus_lib/src/transport/http.rs | 7 ++++--- chorus_lib/src/transport/local.rs | 9 +++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index e7a985e..0eae329 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -171,6 +171,22 @@ where { } +/// Equal trait +pub trait Equal {} + +// Base case: HNil is equal to HNil +impl Equal for HNil {} + +// Recursive case: Head::Tail is equal to L if +// 1. Head is a member of L +// 2. Tail is equal to the remainder of L +impl Equal> for HCons +where + Head: Member, + Tail: Equal, +{ +} + /// Provides a method to work with located values at the current location pub struct Unwrapper { phantom: PhantomData, diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 8e825e3..83ac14e 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -17,7 +17,7 @@ use crate::transport::{TransportChannel, TransportConfig}; use crate::{transport_config, LocationSet}; use crate::{ - core::{ChoreographyLocation, HList, Member, Portable, Transport}, + core::{ChoreographyLocation, Equal, HList, Member, Portable, Transport}, utils::queue::BlockingQueue, }; @@ -54,12 +54,13 @@ impl HttpTransport { } /// Creates a new `HttpTransport` instance from the projection target and a configuration. - pub fn new( - http_config: &TransportConfig, + pub fn new( + http_config: &TransportConfig, transport_channel: TransportChannel, ) -> Self where C: Member, + L2: Equal, { let info = &http_config.info; diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 2f425a7..b41b626 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -11,7 +11,7 @@ use crate::transport::{TransportChannel, TransportConfig}; #[cfg(test)] use crate::{transport_config, LocationSet}; -use crate::core::{ChoreographyLocation, HList, Portable, Transport}; +use crate::core::{ChoreographyLocation, HList, Portable, Transport, Equal}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; @@ -48,10 +48,10 @@ impl LocalTransport { } /// Creates a new `LocalTransport` instance from a `TransportConfig` and a `TransportChannel`. - pub fn new( - _local_config: &TransportConfig, + pub fn new( + _local_config: &TransportConfig, transport_channel: TransportChannel, - ) -> Self { + ) -> Self where L2: Equal{ let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); @@ -121,6 +121,7 @@ mod tests { Alice: (), Bob: () ); + let transport_channel = transport_channel.clone(); let transport = LocalTransport::new(&config, transport_channel); From 9db45377de2cc591acc66f15da361c13b52a63b6 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 02:33:05 -0700 Subject: [PATCH 39/75] Update examples --- chorus_lib/examples/bookseller.rs | 24 +++++++-- chorus_lib/examples/bookseller2.rs | 87 +++++++++++++++++++++++++----- chorus_lib/examples/hello.rs | 25 ++++++--- chorus_lib/examples/loc-poly.rs | 23 ++++++-- chorus_lib/examples/tic-tac-toe.rs | 26 ++++++--- chorus_lib/src/transport/local.rs | 7 ++- 6 files changed, 158 insertions(+), 34 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index dbd69d4..3f5dd3f 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -74,11 +74,27 @@ impl Choreography for BooksellerChoreography { } fn main() { - let config = transport_config!(Seller: (), Buyer: ()); + // let config = transport_config!(Seller: (), Buyer: ()); - let transport = LocalTransport::new(&config); - let seller_projector = Projector::new(Seller, transport.clone()); - let buyer_projector = Projector::new(Buyer, transport.clone()); + let transport_channel = LocalTransport::::transport_channel(); + + let config_seller = transport_config!( + Seller, + Buyer: (), + Seller: (), + ); + + let config_buyer = transport_config!( + Buyer, + Buyer: (), + Seller: (), + ); + + let transport_seller = LocalTransport::new(&config_seller, transport_channel.clone()); + let transport_buyer = LocalTransport::new(&config_buyer, transport_channel.clone()); + + let seller_projector = Projector::new(Seller, transport_seller); + let buyer_projector = Projector::new(Buyer, transport_buyer); let mut handles: Vec> = Vec::new(); handles.push(thread::spawn(move || { diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 7e29b64..ff859c0 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -1,7 +1,7 @@ extern crate chorus_lib; use std::thread; -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap}; use chorus_lib::transport_config; use chorus_lib::LocationSet; @@ -143,17 +143,25 @@ fn main() { i }; - let config = transport_config!(Seller: (), Buyer1: (), Buyer2: ()); - let transport = LocalTransport::new(&config); - let seller_projector = Arc::new(Projector::new(Seller, transport.clone())); - let buyer1_projector = Arc::new(Projector::new(Buyer1, transport.clone())); - let buyer2_projector = Arc::new(Projector::new(Buyer2, transport.clone())); + let transport_channel = + LocalTransport::::transport_channel(); println!("Tries to buy HoTT with one buyer"); type OneBuyerBooksellerChoreography = BooksellerChoreography; let mut handles = Vec::new(); { - let seller_projector = seller_projector.clone(); + let config = transport_config!( + Seller, + Seller: (), + Buyer1: (), + Buyer2: (), + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); + + let seller_projector = Projector::new(Seller, transport); + let inventory = inventory.clone(); handles.push(thread::spawn(move || { seller_projector.epp_and_run(OneBuyerBooksellerChoreography { @@ -164,7 +172,18 @@ fn main() { })); } { - let buyer1_projector = buyer1_projector.clone(); + let config = transport_config!( + Buyer1, + Seller: (), + Buyer1: (), + Buyer2: (), + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); + + let buyer1_projector = Projector::new(Buyer1, transport); + handles.push(thread::spawn(move || { let result = buyer1_projector.epp_and_run(OneBuyerBooksellerChoreography { _marker: std::marker::PhantomData, @@ -178,7 +197,18 @@ fn main() { })); } { - let buyer2_projector = buyer2_projector.clone(); + let config = transport_config!( + Buyer2, + Seller: (), + Buyer1: (), + Buyer2: (), + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); + + let buyer2_projector = Projector::new(Buyer2, transport); + handles.push(thread::spawn(move || { buyer2_projector.epp_and_run(OneBuyerBooksellerChoreography { _marker: std::marker::PhantomData, @@ -195,7 +225,18 @@ fn main() { type TwoBuyerBooksellerChoreography = BooksellerChoreography; let mut handles = Vec::new(); { - let seller_projector = seller_projector.clone(); + let config = transport_config!( + Seller, + Seller: (), + Buyer1: (), + Buyer2: (), + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); + + let seller_projector = Projector::new(Seller, transport); + let inventory = inventory.clone(); handles.push(thread::spawn(move || { seller_projector.epp_and_run(TwoBuyerBooksellerChoreography { @@ -206,7 +247,18 @@ fn main() { })); } { - let buyer1_projector = buyer1_projector.clone(); + let config = transport_config!( + Buyer1, + Seller: (), + Buyer1: (), + Buyer2: (), + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); + + let buyer1_projector = Projector::new(Buyer1, transport); + handles.push(thread::spawn(move || { let result = buyer1_projector.epp_and_run(TwoBuyerBooksellerChoreography { _marker: std::marker::PhantomData, @@ -220,7 +272,18 @@ fn main() { })); } { - let buyer2_projector = buyer2_projector.clone(); + let config = transport_config!( + Buyer2, + Seller: (), + Buyer1: (), + Buyer2: (), + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); + + let buyer2_projector = Projector::new(Buyer2, transport); + handles.push(thread::spawn(move || { buyer2_projector.epp_and_run(TwoBuyerBooksellerChoreography { _marker: std::marker::PhantomData, diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index 3f641be..99e754f 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -40,21 +40,34 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); - // Create a local transport - let config = transport_config!(Alice: (), Bob: ()); - - let transport = LocalTransport::new(&config); + // Create a transport channel + let transport_channel = LocalTransport::::transport_channel(); // Run the choreography in two threads { - let transport = transport.clone(); + let config = transport_config!( + Alice, + Alice: (), + Bob: () + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); handles.push(thread::spawn(move || { let p = Projector::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } { - let transport = transport.clone(); + let config = transport_config!( + Alice, + Alice: (), + Bob: () + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); + handles.push(thread::spawn(move || { let p = Projector::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 5bfa36e..a770a2a 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -58,11 +58,18 @@ impl Choreography> for MainChoreography { } fn main() { - let config = transport_config!(Alice: (), Bob: (), Carol: ()); - let transport = LocalTransport::new(&config); + let transport_channel = LocalTransport::::transport_channel(); let mut handles = vec![]; { - let transport = transport.clone(); + let config = transport_config!( + Alice, + Alice: (), + Bob: (), + Carol: (), + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); handles.push(thread::spawn(|| { let p = Projector::new(Alice, transport); let v = p.epp_and_run(MainChoreography); @@ -70,7 +77,15 @@ fn main() { })); } { - let transport = transport.clone(); + let config = transport_config!( + Bob, + Alice: (), + Bob: (), + Carol: (), + ); + + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(&config, transport_channel); handles.push(thread::spawn(|| { let p = Projector::new(Bob, transport); p.epp_and_run(MainChoreography); diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 53e76bd..d7e3677 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -294,11 +294,20 @@ fn main() { Box::new(UserBrain::new(args.player)) }; + // Create a transport channel + let transport_channel = HttpTransport::::transport_channel(); + match args.player { 'X' => { - let config = transport_config!(PlayerX: (args.hostname.as_str().to_string(), args.port), - PlayerO: (args.opponent_hostname.as_str().to_string(), args.opponent_port)); - let transport = HttpTransport::new(PlayerX, &config); + let config = transport_config!( + PlayerX, + PlayerX: (args.hostname.as_str().to_string(), args.port), + PlayerO: (args.opponent_hostname.as_str().to_string(), args.opponent_port) + ); + + let transport_channel = transport_channel.clone(); + let transport = HttpTransport::new(&config, transport_channel); + let projector = Projector::new(PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.local(brain), @@ -306,9 +315,14 @@ fn main() { }); } 'O' => { - let config = transport_config!(PlayerO: (args.hostname.as_str().to_string(), args.port), - PlayerX: (args.opponent_hostname.as_str().to_string(), args.opponent_port)); - let transport = HttpTransport::new(PlayerO, &config); + let config = transport_config!( + PlayerO, + PlayerO: (args.hostname.as_str().to_string(), args.port), + PlayerX: (args.opponent_hostname.as_str().to_string(), args.opponent_port) + ); + + let transport_channel = transport_channel.clone(); + let transport = HttpTransport::new(&config, transport_channel); let projector = Projector::new(PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.remote(PlayerX), diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index b41b626..f404d25 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -11,7 +11,7 @@ use crate::transport::{TransportChannel, TransportConfig}; #[cfg(test)] use crate::{transport_config, LocationSet}; -use crate::core::{ChoreographyLocation, HList, Portable, Transport, Equal}; +use crate::core::{ChoreographyLocation, Equal, HList, Portable, Transport}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; @@ -51,7 +51,10 @@ impl LocalTransport { pub fn new( _local_config: &TransportConfig, transport_channel: TransportChannel, - ) -> Self where L2: Equal{ + ) -> Self + where + L2: Equal, + { let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); From 1f42d1773d2a2ebbc86721627570ea7f9b72a6bf Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 03:03:30 -0700 Subject: [PATCH 40/75] Remove the TransportChannel from HttpTransport --- chorus_lib/examples/bookseller2.rs | 2 +- chorus_lib/examples/tic-tac-toe.rs | 9 +-- chorus_lib/src/transport/http.rs | 89 ++++++++++++++++-------------- 3 files changed, 51 insertions(+), 49 deletions(-) diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index ff859c0..1e1d391 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -1,7 +1,7 @@ extern crate chorus_lib; +use std::collections::HashMap; use std::thread; -use std::{collections::HashMap}; use chorus_lib::transport_config; use chorus_lib::LocationSet; diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index d7e3677..1a24f9a 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -294,9 +294,6 @@ fn main() { Box::new(UserBrain::new(args.player)) }; - // Create a transport channel - let transport_channel = HttpTransport::::transport_channel(); - match args.player { 'X' => { let config = transport_config!( @@ -305,8 +302,7 @@ fn main() { PlayerO: (args.opponent_hostname.as_str().to_string(), args.opponent_port) ); - let transport_channel = transport_channel.clone(); - let transport = HttpTransport::new(&config, transport_channel); + let transport = HttpTransport::new(&config); let projector = Projector::new(PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { @@ -321,8 +317,7 @@ fn main() { PlayerX: (args.opponent_hostname.as_str().to_string(), args.opponent_port) ); - let transport_channel = transport_channel.clone(); - let transport = HttpTransport::new(&config, transport_channel); + let transport = HttpTransport::new(&config); let projector = Projector::new(PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.remote(PlayerX), diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 83ac14e..d5eef4e 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,12 +12,12 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; -use crate::transport::{TransportChannel, TransportConfig}; +use crate::transport::TransportConfig; #[cfg(test)] -use crate::{transport_config, LocationSet}; +use crate::transport_config; use crate::{ - core::{ChoreographyLocation, Equal, HList, Member, Portable, Transport}, + core::{ChoreographyLocation, HList, Member, Portable, Transport}, utils::queue::BlockingQueue, }; @@ -26,6 +26,33 @@ type QueueMap = HashMap>; /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; +/// A wrapper for HashMap +#[derive(Clone)] +pub struct HttpConfig { + /// The information about locations + pub info: HashMap, + /// The struct is parametrized by the location set (`L`). + pub location_set: PhantomData, +} + +/// This macro makes a `HttpConfig`. +#[macro_export] +macro_rules! http_config { + ( $( $loc:ident : ( $host:expr, $port:expr ) ),* $(,)? ) => { + { + let mut config = std::collections::HashMap::new(); + $( + config.insert($loc::name().to_string(), ($host.to_string(), $port)); + )* + + $crate::transport::http::HttpConfig::<$crate::LocationSet!($( $loc ),*)> { + info: config, + location_set: core::marker::PhantomData + } + } + }; +} + /// The HTTP transport. pub struct HttpTransport { config: HashMap, @@ -33,42 +60,33 @@ pub struct HttpTransport { server: Arc, join_handle: Option>, location_set: PhantomData, - transport_channel: TransportChannel, + // transport_channel: TransportChannel, + queue_map: Arc, } impl HttpTransport { - /// Creates a `TransportChannel`. - pub fn transport_channel() -> TransportChannel { - let queue_map = { + /// Creates a new `HttpTransport` instance from the projection target and a configuration. + pub fn new( + http_config: &TransportConfig, + ) -> Self + where + C: Member, + { + let queue_map: Arc = { let mut m = HashMap::new(); for loc in L::to_string_list() { m.insert(loc.to_string(), BlockingQueue::new()); } - m + Arc::new(m.into()) }; - TransportChannel { - location_set: PhantomData, - queue_map: Arc::new(queue_map.into()), - } - } - - /// Creates a new `HttpTransport` instance from the projection target and a configuration. - pub fn new( - http_config: &TransportConfig, - transport_channel: TransportChannel, - ) -> Self - where - C: Member, - L2: Equal, - { let info = &http_config.info; let (_, (hostname, port)) = &http_config.target_info; let server = Arc::new(Server::http(format!("{}:{}", hostname, port)).unwrap()); let join_handle = Some({ let server = server.clone(); - let queue_map = transport_channel.queue_map.clone(); + let queue_map = queue_map.clone(); thread::spawn(move || { for mut request in server.incoming_requests() { @@ -105,7 +123,7 @@ impl HttpTransport { join_handle, server, location_set: PhantomData, - transport_channel, + queue_map, } } } @@ -134,7 +152,7 @@ impl Transport for HttpTransport { } fn receive(&self, from: &str, _at: &str) -> V { - let str = self.transport_channel.queue_map.get(from).unwrap().pop(); + let str = self.queue_map.get(from).unwrap().pop(); serde_json::from_str(&str).unwrap() } } @@ -160,8 +178,6 @@ mod tests { let (signal, wait) = mpsc::channel::<()>(); - let transport_channel = HttpTransport::::transport_channel(); - let mut handles = Vec::new(); { let config = transport_config!( @@ -170,11 +186,9 @@ mod tests { Bob: ("localhost".to_string(), 9011) ); - let transport_channel = transport_channel.clone(); - handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start - let transport = HttpTransport::new(&config, transport_channel); + let transport = HttpTransport::new(&config); transport.send::(Alice::name(), Bob::name(), &v); })); } @@ -185,9 +199,8 @@ mod tests { Bob: ("0.0.0.0".to_string(), 9011) ); - let transport_channel = transport_channel.clone(); handles.push(thread::spawn(move || { - let transport = HttpTransport::new(&config, transport_channel); + let transport = HttpTransport::new(&config); signal.send(()).unwrap(); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); @@ -203,8 +216,6 @@ mod tests { let v = 42; let (signal, wait) = mpsc::channel::<()>(); - let transport_channel = HttpTransport::::transport_channel(); - let mut handles = Vec::new(); { let config = transport_config!( @@ -213,11 +224,9 @@ mod tests { Bob: ("localhost".to_string(), 9021) ); - let transport_channel = transport_channel.clone(); - handles.push(thread::spawn(move || { signal.send(()).unwrap(); - let transport = HttpTransport::new(&config, transport_channel); + let transport = HttpTransport::new(&config); transport.send::(Alice::name(), Bob::name(), &v); })); } @@ -228,13 +237,11 @@ mod tests { Bob: ("0.0.0.0".to_string(), 9021) ); - let transport_channel = transport_channel.clone(); - handles.push(thread::spawn(move || { // wait for Alice to start, which forces Alice to retry wait.recv().unwrap(); sleep(Duration::from_millis(100)); - let transport = HttpTransport::new(&config, transport_channel); + let transport = HttpTransport::new(&config); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); })); From 1739b1ba73db147acccb8e9f16b821b8768060e7 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 03:13:11 -0700 Subject: [PATCH 41/75] Move TransportChannel to LocalTransport and rename it to LocalTransportChannel --- chorus_lib/src/transport.rs | 23 ----------------------- chorus_lib/src/transport/local.rs | 29 ++++++++++++++++++----------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index b0e6d6f..9b4bf05 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -4,7 +4,6 @@ pub mod http; pub mod local; use crate::core::ChoreographyLocation; -use std::sync::Arc; /// A generic struct for configuration of `Transport`. #[derive(Clone)] @@ -22,26 +21,6 @@ pub struct TransportConfig< pub location_set: std::marker::PhantomData, } -/// A Transport channel used between multiple `Transport`s. -pub struct TransportChannel { - /// The location set where the channel is defined on. - pub location_set: std::marker::PhantomData, - queue_map: Arc, -} - -impl Clone for TransportChannel -where - L: crate::core::HList, - T: Send + Sync, -{ - fn clone(&self) -> Self { - Self { - location_set: self.location_set.clone(), - queue_map: self.queue_map.clone(), // This clones the Arc, not the underlying data - } - } -} - /// This macro makes a `TransportConfig`. #[macro_export] macro_rules! transport_config { @@ -58,8 +37,6 @@ macro_rules! transport_config { } )* - // println!("{}, {}", target_info.unwrap().0, target.info.unwrap().1); - $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _, _, _> { info: config, location_set: core::marker::PhantomData, diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index f404d25..d4b7691 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,7 +7,7 @@ use serde_json; use std::marker::PhantomData; -use crate::transport::{TransportChannel, TransportConfig}; +use crate::transport::TransportConfig; #[cfg(test)] use crate::{transport_config, LocationSet}; @@ -16,6 +16,14 @@ use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; +/// A Transport channel used between multiple `Transport`s. +#[derive(Clone)] +pub struct LocalTransportChannel { + /// The location set where the channel is defined on. + pub location_set: std::marker::PhantomData, + queue_map: Arc, +} + /// The local transport. /// /// This transport uses a blocking queue to allow for communication between threads. Each location must be executed in its thread. @@ -24,14 +32,13 @@ type QueueMap = HashMap>>; #[derive(Clone)] pub struct LocalTransport { internal_locations: Vec, - // queue_map: Arc, location_set: PhantomData, - transport_channel: TransportChannel, + local_channel: LocalTransportChannel, } impl LocalTransport { - /// Creates a `TransportChannel`. - pub fn transport_channel() -> TransportChannel { + /// Creates a `LocalTransportChannel`. + pub fn transport_channel() -> LocalTransportChannel { let mut queue_map: QueueMap = HashMap::new(); for sender in L::to_string_list() { let mut n = HashMap::new(); @@ -41,16 +48,16 @@ impl LocalTransport { queue_map.insert(sender.to_string(), n); } - TransportChannel { + LocalTransportChannel { location_set: PhantomData, queue_map: Arc::new(queue_map.into()), } } - /// Creates a new `LocalTransport` instance from a `TransportConfig` and a `TransportChannel`. + /// Creates a new `LocalTransport` instance from a `TransportConfig` and a `LocalTransportChannel`. pub fn new( _local_config: &TransportConfig, - transport_channel: TransportChannel, + local_channel: LocalTransportChannel, ) -> Self where L2: Equal, @@ -65,7 +72,7 @@ impl LocalTransport { LocalTransport { internal_locations: locations_vec, location_set: PhantomData, - transport_channel, + local_channel, } } } @@ -77,7 +84,7 @@ impl Transport for LocalTransport { fn send(&self, from: &str, to: &str, data: &T) -> () { let data = serde_json::to_string(data).unwrap(); - self.transport_channel + self.local_channel .queue_map .get(from) .unwrap() @@ -88,7 +95,7 @@ impl Transport for LocalTransport { fn receive(&self, from: &str, at: &str) -> T { let data = self - .transport_channel + .local_channel .queue_map .get(from) .unwrap() From 07f50216eb4d1dc4d041721ffc739e7f69106e51 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 16:57:01 -0700 Subject: [PATCH 42/75] Update examples and the book --- chorus_book/src/guide-input-and-output.md | 10 ++-- chorus_book/src/guide-projector.md | 16 +++-- chorus_book/src/guide-transport.md | 69 +++++++++++++++------- chorus_book/src/header.txt | 8 ++- chorus_lib/examples/bookseller.rs | 24 ++------ chorus_lib/examples/bookseller2.rs | 71 +++------------------- chorus_lib/examples/hello.rs | 25 ++------ chorus_lib/examples/loc-poly.rs | 27 ++------- chorus_lib/examples/tic-tac-toe.rs | 6 +- chorus_lib/src/core.rs | 18 +++--- chorus_lib/src/transport.rs | 14 ++--- chorus_lib/src/transport/http.rs | 51 +++++----------- chorus_lib/src/transport/local.rs | 72 +++++++++-------------- 13 files changed, 152 insertions(+), 259 deletions(-) diff --git a/chorus_book/src/guide-input-and-output.md b/chorus_book/src/guide-input-and-output.md index 483af04..82a1954 100644 --- a/chorus_book/src/guide-input-and-output.md +++ b/chorus_book/src/guide-input-and-output.md @@ -43,7 +43,7 @@ let choreo = DemoChoreography { input: "World".to_string(), }; -let projector = Projector::new(Alice, transport); +let projector = Projector::new(Alice, alice_transport); projector.epp_and_run(choreo); ``` @@ -93,7 +93,7 @@ To run the sample choreography above at Alice, we use the `local` method to cons # }); # } # } -let projector_for_alice = Projector::new(Alice, transport); +let projector_for_alice = Projector::new(Alice, alice_transport); // Because the target of the projector is Alice, the located value is available at Alice. let string_at_alice: Located = projector_for_alice.local("Hello, World!".to_string()); // Instantiate the choreography with the located value @@ -120,7 +120,7 @@ For Bob, we use the `remote` method to construct the located value. # }); # } # } -let projector_for_bob = Projector::new(Bob, transport); +let projector_for_bob = Projector::new(Bob, bob_transport); // Construct a remote located value at Alice. The actual value is not required. let string_at_alice = projector_for_bob.remote(Alice); // Instantiate the choreography with the located value @@ -161,7 +161,7 @@ impl Choreography for DemoChoreography { # } # } let choreo = DemoChoreography; -let projector = Projector::new(Alice, transport); +let projector = Projector::new(Alice, alice_transport); let output = projector.epp_and_run(choreo); assert_eq!(output, "Hello, World!".to_string()); ``` @@ -183,7 +183,7 @@ impl Choreography> for DemoChoreography { } } -let projector = Projector::new(Alice, transport); +let projector = Projector::new(Alice, alice_transport); let output = projector.epp_and_run(DemoChoreography); let string_at_alice = projector.unwrap(output); assert_eq!(string_at_alice, "Hello, World!".to_string()); diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index e887e52..0af3ccb 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -8,17 +8,19 @@ To create a `Projector`, you need to provide the target location and the transpo ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::LocalTransport; +# use std::sync::Arc; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::core::{ChoreographyLocation, Projector}; # use chorus_lib::{LocationSet}; -# let transport = LocalTransport::::new(); +# let transport_channel = Arc::new(LocalTransportChannel::::new()); +# let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # -let projector = Projector::new(Alice, transport); +let projector = Projector::new(Alice, alice_transport); ``` Notice that the `Projector` is parameterized by its target location type. You will need one projector for each location to execute choreography. @@ -29,10 +31,12 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::LocalTransport; +# use std::sync::Arc; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; # use chorus_lib::{LocationSet}; -# let transport = LocalTransport::::new(); +# let transport_channel = Arc::new(LocalTransportChannel::::new()); +# let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -45,7 +49,7 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # } # -# let projector = Projector::new(Alice, transport); +# let projector = Projector::new(Alice, alice_transport); projector.epp_and_run(HelloWorldChoreography); ``` diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index f3de634..a021db1 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -8,22 +8,47 @@ ChoRus provides two built-in transports: `local` and `http`. ### The Local Transport -The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. +The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. Each `local` transport is defined over `LocalTransportChannel`, which contains the set of `ChoreographyLocation` that the `local` transport operates on. You can build a `LocalTransportChannel` by importing the `LocalTransportChannel` stsruct from the `chorus_lib` crate. -To use the `local` transport, import the `LocalTransport` struct from the `chorus_lib` crate. +```rust +# extern crate chorus_lib; +# use std::sync::Arc; +# use chorus_lib::core::{ChoreographyLocation}; +# use chorus_lib::{LocationSet}; +# #[derive(ChoreographyLocation)] +# struct Alice; +# #[derive(ChoreographyLocation)] +# struct Bob; +use chorus_lib::transport::local::LocalTransportChannel; + +let transport_channel = Arc::new(LocalTransportChannel::::new()); +``` + +To use the `local` transport, first import the `LocalTransport` struct from the `chorus_lib` crate. ```rust # extern crate chorus_lib; -use chorus_lib::transport::local::LocalTransport; +# use std::sync::Arc; +# use chorus_lib::core::{ChoreographyLocation}; +# use chorus_lib::{LocationSet}; +# #[derive(ChoreographyLocation)] +# struct Alice; +# let transport_channel = Arc::new(LocalTransportChannel::::new()); +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; + +let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); ``` -You can construct a `LocalTransport` instance by passing a slice of locations to the `from` method. + Then build the transport by using the `LocalTransport::new` associated function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. + +You can construct a `LocalTransport` instance by passing a target location(lained in the [Projector section](./guide-projector.md)) and a `std::sync::Arc` of `LocalTransportChannel` to the `new` method. -Because of the nature of the `Local` transport, you must use the same `LocalTransport` instance for all locations. You can `clone` the `LocalTransport` instance and pass it to the threads. +Because of the nature of the `Local` transport, you must make a `std::sync::Arc` from the `LocalTransportChannel`, by using the `std::sync::Arc::clone(&local_transport)`. ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::LocalTransport; +# use std::sync::Arc; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use std::thread; # use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector}; # use chorus_lib::{LocationSet}; @@ -37,13 +62,11 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # fn run(self, op: &impl ChoreoOp) { # } # } - - +let transport_channel = Arc::new(LocalTransportChannel::::new()); let mut handles: Vec> = Vec::new(); -let transport = LocalTransport::::new(); { - // create a clone for Alice - let transport = transport.clone(); + // create a transport for Alice + let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); handles.push(thread::spawn(move || { let p = Projector::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); @@ -51,7 +74,7 @@ let transport = LocalTransport::::new(); } { // create another for Bob - let transport = transport.clone(); + let transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); handles.push(thread::spawn(move || { let p = Projector::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); @@ -63,24 +86,26 @@ let transport = LocalTransport::::new(); The `http` transport is used to execute choreographies on different machines. This is useful for executing choreographies in a distributed system. -To use the `http` transport, import the `HttpTransport` struct and the `http_config` macro from the `chorus_lib` crate. +To use the `http` transport, import the `HttpTransport` struct and the `transport_config` macro from the `chorus_lib` crate. ```rust # extern crate chorus_lib; use chorus_lib::transport::http::HttpTransport; -use chorus_lib::http_config; +use chorus_lib::transport_config; ``` -The `new` constructor takes the name of the projection target and "configuration" of type `HttpConfig`. To build the `HttpConfig`, you should use the macro `http_config` and give it a comma separatedlist of key: values where each key is a `ChoreographyLocation` and each value is a tuple of (host_name, port). You can think of configuration as a map from locations to the hostname and port of the location. +The new constructor takes a "configuration" of type TransportConfig. To build the TransportConfig, you should use the macro transport_config and give it a key => value, followed by a comma (if you have more than one key/value), and a comma-separated list of key: values where the key => value is the target ChoreographyLocation and the value is the required information for the target, and each following key: value is a ChoreographyLocation and the required information for it (`(host_name, port)` in this case). For HttpTransport You can think of TransportConfig as a map from locations to the hostname and port of the location. But for a generic Transport, it can contain any kind of information. ```rust {{#include ./header.txt}} # use chorus_lib::transport::http::{HttpTransport}; -# use chorus_lib::http_config; -# use std::collections::HashMap; +# use chorus_lib::transport_config; +let config = transport_config!( + Alice => ("0.0.0.0".to_string(), 9010), + Bob: ("localhost".to_string(), 9011) + ); -let config = http_config!(Alice: ("localhost", 8080), Bob: ("localhost", 8081)); -let transport = HttpTransport::new(Alice, &config); +let transport = HttpTransport::new(&config); ``` In the above example, the transport will start the HTTP server on port 8080 on localhost. If Alice needs to send a message to Bob, it will use `http://localhost:8081` as the destination. @@ -96,7 +121,8 @@ Note that when calling `epp_and_run` on a `Projector`, you will get a compile er ```rust, compile_fail # extern crate chorus_lib; -# use chorus_lib::transport::local::LocalTransport; +# use std::sync::Arc; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; # use chorus_lib::{LocationSet}; @@ -111,7 +137,8 @@ impl Choreography for HelloWorldChoreography { } } -let transport = LocalTransport::::new(); +let transport_channel = Arc::new(LocalTransportChannel::::new()); +let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); let projector = Projector::new(Alice, transport); projector.epp_and_run(HelloWorldChoreography); ``` diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 72d6873..ea4645e 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,6 +1,7 @@ # extern crate chorus_lib; +# use std::sync::Arc; # use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; -# use chorus_lib::transport::local::LocalTransport; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::{LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; @@ -8,4 +9,7 @@ # struct Bob; # #[derive(ChoreographyLocation)] # struct Carol; -# let transport = LocalTransport::::new(); +# let transport_channel = Arc::new(LocalTransportChannel::::new()); +# let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); +# let bob_transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); +# let carol_transport = LocalTransport::new(Carol, Arc::clone(&transport_channel)); \ No newline at end of file diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 3f5dd3f..abf01c5 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -1,13 +1,13 @@ extern crate chorus_lib; use std::io; +use std::sync::Arc; use std::thread; use chrono::NaiveDate; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; -use chorus_lib::transport::local::LocalTransport; -use chorus_lib::transport_config; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; use chorus_lib::LocationSet; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { @@ -74,24 +74,10 @@ impl Choreography for BooksellerChoreography { } fn main() { - // let config = transport_config!(Seller: (), Buyer: ()); + let transport_channel = Arc::new(LocalTransportChannel::::new()); - let transport_channel = LocalTransport::::transport_channel(); - - let config_seller = transport_config!( - Seller, - Buyer: (), - Seller: (), - ); - - let config_buyer = transport_config!( - Buyer, - Buyer: (), - Seller: (), - ); - - let transport_seller = LocalTransport::new(&config_seller, transport_channel.clone()); - let transport_buyer = LocalTransport::new(&config_buyer, transport_channel.clone()); + let transport_seller = LocalTransport::new(Seller, Arc::clone(&transport_channel)); + let transport_buyer = LocalTransport::new(Buyer, Arc::clone(&transport_channel)); let seller_projector = Projector::new(Seller, transport_seller); let buyer_projector = Projector::new(Buyer, transport_buyer); diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 1e1d391..7267b2b 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -1,13 +1,13 @@ extern crate chorus_lib; use std::collections::HashMap; +use std::sync::Arc; use std::thread; -use chorus_lib::transport_config; use chorus_lib::LocationSet; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, - transport::local::LocalTransport, + transport::local::{LocalTransport, LocalTransportChannel}, }; use chrono::NaiveDate; @@ -144,22 +144,13 @@ fn main() { }; let transport_channel = - LocalTransport::::transport_channel(); + Arc::new(LocalTransportChannel::::new()); println!("Tries to buy HoTT with one buyer"); type OneBuyerBooksellerChoreography = BooksellerChoreography; let mut handles = Vec::new(); { - let config = transport_config!( - Seller, - Seller: (), - Buyer1: (), - Buyer2: (), - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); - + let transport = LocalTransport::new(Seller, Arc::clone(&transport_channel)); let seller_projector = Projector::new(Seller, transport); let inventory = inventory.clone(); @@ -172,16 +163,7 @@ fn main() { })); } { - let config = transport_config!( - Buyer1, - Seller: (), - Buyer1: (), - Buyer2: (), - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); - + let transport = LocalTransport::new(Buyer1, Arc::clone(&transport_channel)); let buyer1_projector = Projector::new(Buyer1, transport); handles.push(thread::spawn(move || { @@ -197,16 +179,7 @@ fn main() { })); } { - let config = transport_config!( - Buyer2, - Seller: (), - Buyer1: (), - Buyer2: (), - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); - + let transport = LocalTransport::new(Buyer2, Arc::clone(&transport_channel)); let buyer2_projector = Projector::new(Buyer2, transport); handles.push(thread::spawn(move || { @@ -225,16 +198,7 @@ fn main() { type TwoBuyerBooksellerChoreography = BooksellerChoreography; let mut handles = Vec::new(); { - let config = transport_config!( - Seller, - Seller: (), - Buyer1: (), - Buyer2: (), - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); - + let transport = LocalTransport::new(Seller, Arc::clone(&transport_channel)); let seller_projector = Projector::new(Seller, transport); let inventory = inventory.clone(); @@ -247,16 +211,7 @@ fn main() { })); } { - let config = transport_config!( - Buyer1, - Seller: (), - Buyer1: (), - Buyer2: (), - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); - + let transport = LocalTransport::new(Buyer1, Arc::clone(&transport_channel)); let buyer1_projector = Projector::new(Buyer1, transport); handles.push(thread::spawn(move || { @@ -272,15 +227,7 @@ fn main() { })); } { - let config = transport_config!( - Buyer2, - Seller: (), - Buyer1: (), - Buyer2: (), - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); + let transport = LocalTransport::new(Buyer2, Arc::clone(&transport_channel)); let buyer2_projector = Projector::new(Buyer2, transport); diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index 99e754f..a69eadf 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -1,10 +1,10 @@ extern crate chorus_lib; +use std::sync::Arc; use std::thread; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; -use chorus_lib::transport::local::LocalTransport; -use chorus_lib::transport_config; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; use chorus_lib::LocationSet; // --- Define two locations (Alice and Bob) --- @@ -41,33 +41,18 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a transport channel - let transport_channel = LocalTransport::::transport_channel(); + let transport_channel = Arc::new(LocalTransportChannel::::new()); // Run the choreography in two threads { - let config = transport_config!( - Alice, - Alice: (), - Bob: () - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); + let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); handles.push(thread::spawn(move || { let p = Projector::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } { - let config = transport_config!( - Alice, - Alice: (), - Bob: () - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); - + let transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); handles.push(thread::spawn(move || { let p = Projector::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index a770a2a..57f23a7 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -1,12 +1,12 @@ extern crate chorus_lib; use std::fmt::Debug; +use std::sync::Arc; use std::thread; use chorus_lib::core::{ ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, }; -use chorus_lib::transport::local::LocalTransport; -use chorus_lib::transport_config; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; use chorus_lib::LocationSet; #[derive(ChoreographyLocation)] @@ -58,18 +58,11 @@ impl Choreography> for MainChoreography { } fn main() { - let transport_channel = LocalTransport::::transport_channel(); + let transport_channel = + Arc::new(LocalTransportChannel::::new()); let mut handles = vec![]; { - let config = transport_config!( - Alice, - Alice: (), - Bob: (), - Carol: (), - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); + let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); handles.push(thread::spawn(|| { let p = Projector::new(Alice, transport); let v = p.epp_and_run(MainChoreography); @@ -77,15 +70,7 @@ fn main() { })); } { - let config = transport_config!( - Bob, - Alice: (), - Bob: (), - Carol: (), - ); - - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); + let transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); handles.push(thread::spawn(|| { let p = Projector::new(Bob, transport); p.epp_and_run(MainChoreography); diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 1a24f9a..dd93cad 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -297,8 +297,7 @@ fn main() { match args.player { 'X' => { let config = transport_config!( - PlayerX, - PlayerX: (args.hostname.as_str().to_string(), args.port), + PlayerX => (args.hostname.as_str().to_string(), args.port), PlayerO: (args.opponent_hostname.as_str().to_string(), args.opponent_port) ); @@ -312,8 +311,7 @@ fn main() { } 'O' => { let config = transport_config!( - PlayerO, - PlayerO: (args.hostname.as_str().to_string(), args.port), + PlayerO => (args.hostname.as_str().to_string(), args.port), PlayerX: (args.opponent_hostname.as_str().to_string(), args.opponent_port) ); diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 0eae329..4fe152e 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -280,7 +280,7 @@ pub trait Choreography { /// Provides methods to send and receive messages. /// /// The trait provides methods to send and receive messages between locations. Implement this trait to define a custom transport. -pub trait Transport { +pub trait Transport { /// Returns a list of locations. fn locations(&self) -> Vec; /// Sends a message from `from` to `to`. @@ -290,7 +290,7 @@ pub trait Transport { } /// Provides a method to perform end-point projection. -pub struct Projector, Index> +pub struct Projector, Index> where L1: Member, { @@ -300,7 +300,7 @@ where index: PhantomData, } -impl, Index> Projector +impl, Index> Projector where L1: Member, { @@ -349,14 +349,14 @@ where where L: Subset, { - struct EppOp<'a, L: HList, L1: ChoreographyLocation, LS: HList, B: Transport> { + struct EppOp<'a, L: HList, L1: ChoreographyLocation, LS: HList, B: Transport> { target: PhantomData, transport: &'a B, locations: Vec, marker: PhantomData, - location_set: PhantomData, + projector_location_set: PhantomData, } - impl<'a, L: HList, T: ChoreographyLocation, LS: HList, B: Transport> ChoreoOp + impl<'a, L: HList, T: ChoreographyLocation, LS: HList, B: Transport> ChoreoOp for EppOp<'a, L, T, LS, B> { fn locally( @@ -425,7 +425,7 @@ where transport: &self.transport, locations: self.transport.locations(), marker: PhantomData::, - location_set: PhantomData::, + projector_location_set: PhantomData::, }; choreo.run(&op) } @@ -444,7 +444,7 @@ where transport: self.transport, locations: locs_vec.clone(), marker: PhantomData::, - location_set: PhantomData::, + projector_location_set: PhantomData::, }; return choreo.run(&op); } @@ -457,7 +457,7 @@ where transport: &self.transport, locations: self.transport.locations(), marker: PhantomData::, - location_set: PhantomData::, + projector_location_set: PhantomData::, }; choreo.run(&op) } diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 9b4bf05..d529a74 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -24,23 +24,17 @@ pub struct TransportConfig< /// This macro makes a `TransportConfig`. #[macro_export] macro_rules! transport_config { - ( $choreography_loc:ident, $( $loc:ident : $val:expr ),* $(,)? ) => { + ( $choreography_loc:ident => $choreography_val:expr, $( $loc:ident : $val:expr ),* $(,)? ) => { { - let choreography_name = $choreography_loc::name().to_string(); let mut config = std::collections::HashMap::new(); - let mut target_info = None; $( - if $loc::name().to_string() != choreography_name{ - config.insert($loc::name().to_string(), $val); - } else { - target_info = Some($val); - } + config.insert($loc::name().to_string(), $val); )* - $crate::transport::TransportConfig::<$crate::LocationSet!($( $loc ),*), _, _, _> { + $crate::transport::TransportConfig::<$crate::LocationSet!($choreography_loc, $( $loc ),*), _, _, _> { info: config, location_set: core::marker::PhantomData, - target_info: ($choreography_loc, target_info.unwrap()), + target_info: ($choreography_loc, $choreography_val), } } }; diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index d5eef4e..f94340a 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -35,42 +35,24 @@ pub struct HttpConfig { pub location_set: PhantomData, } -/// This macro makes a `HttpConfig`. -#[macro_export] -macro_rules! http_config { - ( $( $loc:ident : ( $host:expr, $port:expr ) ),* $(,)? ) => { - { - let mut config = std::collections::HashMap::new(); - $( - config.insert($loc::name().to_string(), ($host.to_string(), $port)); - )* - - $crate::transport::http::HttpConfig::<$crate::LocationSet!($( $loc ),*)> { - info: config, - location_set: core::marker::PhantomData - } - } - }; -} - /// The HTTP transport. -pub struct HttpTransport { +pub struct HttpTransport { config: HashMap, agent: Agent, server: Arc, join_handle: Option>, location_set: PhantomData, - // transport_channel: TransportChannel, queue_map: Arc, + target_location: PhantomData, } -impl HttpTransport { - /// Creates a new `HttpTransport` instance from the projection target and a configuration. - pub fn new( - http_config: &TransportConfig, +impl HttpTransport { + /// Creates a new `HttpTransport` instance from the configuration. + pub fn new( + http_config: &TransportConfig, ) -> Self where - C: Member, + TLocation: Member, { let queue_map: Arc = { let mut m = HashMap::new(); @@ -124,18 +106,19 @@ impl HttpTransport { server, location_set: PhantomData, queue_map, + target_location: PhantomData, } } } -impl Drop for HttpTransport { +impl Drop for HttpTransport { fn drop(&mut self) { self.server.unblock(); self.join_handle.take().map(thread::JoinHandle::join); } } -impl Transport for HttpTransport { +impl Transport for HttpTransport { fn locations(&self) -> Vec { Vec::from_iter(self.config.keys().map(|s| s.clone())) } @@ -181,8 +164,7 @@ mod tests { let mut handles = Vec::new(); { let config = transport_config!( - Alice, - Alice: ("0.0.0.0".to_string(), 9010), + Alice => ("0.0.0.0".to_string(), 9010), Bob: ("localhost".to_string(), 9011) ); @@ -194,9 +176,8 @@ mod tests { } { let config = transport_config!( - Bob, + Bob => ("0.0.0.0".to_string(), 9011), Alice: ("localhost".to_string(), 9010), - Bob: ("0.0.0.0".to_string(), 9011) ); handles.push(thread::spawn(move || { @@ -219,8 +200,7 @@ mod tests { let mut handles = Vec::new(); { let config = transport_config!( - Alice, - Alice: ("0.0.0.0".to_string(), 9020), + Alice => ("0.0.0.0".to_string(), 9020), Bob: ("localhost".to_string(), 9021) ); @@ -232,9 +212,8 @@ mod tests { } { let config = transport_config!( - Bob, - Alice: ("localhost".to_string(), 9020), - Bob: ("0.0.0.0".to_string(), 9021) + Bob=> ("0.0.0.0".to_string(), 9021), + Alice: ("localhost".to_string(), 9020) ); handles.push(thread::spawn(move || { diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index d4b7691..e594308 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,11 +7,10 @@ use serde_json; use std::marker::PhantomData; -use crate::transport::TransportConfig; #[cfg(test)] -use crate::{transport_config, LocationSet}; +use crate::LocationSet; -use crate::core::{ChoreographyLocation, Equal, HList, Portable, Transport}; +use crate::core::{ChoreographyLocation, HList, Portable, Transport}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; @@ -24,21 +23,9 @@ pub struct LocalTransportChannel { queue_map: Arc, } -/// The local transport. -/// -/// This transport uses a blocking queue to allow for communication between threads. Each location must be executed in its thread. -/// -/// Unlike network-based transports, all locations must share the same `LocalTransport` instance. The struct implements `Clone` so that it can be shared across threads. -#[derive(Clone)] -pub struct LocalTransport { - internal_locations: Vec, - location_set: PhantomData, - local_channel: LocalTransportChannel, -} - -impl LocalTransport { +impl LocalTransportChannel { /// Creates a `LocalTransportChannel`. - pub fn transport_channel() -> LocalTransportChannel { + pub fn new() -> LocalTransportChannel { let mut queue_map: QueueMap = HashMap::new(); for sender in L::to_string_list() { let mut n = HashMap::new(); @@ -53,15 +40,24 @@ impl LocalTransport { queue_map: Arc::new(queue_map.into()), } } +} - /// Creates a new `LocalTransport` instance from a `TransportConfig` and a `LocalTransportChannel`. - pub fn new( - _local_config: &TransportConfig, - local_channel: LocalTransportChannel, - ) -> Self - where - L2: Equal, - { +/// The local transport. +/// +/// This transport uses a blocking queue to allow for communication between threads. Each location must be executed in its thread. +/// +/// Unlike network-based transports, all locations must share the same `LocalTransport` instance. The struct implements `Clone` so that it can be shared across threads. +#[derive(Clone)] +pub struct LocalTransport { + internal_locations: Vec, + location_set: PhantomData, + local_channel: Arc>, + target_location: PhantomData, +} + +impl LocalTransport { + /// Creates a new `LocalTransport` instance from a Target `ChoreographyLocation` and a `LocalTransportChannel`. + pub fn new(_target: TargetLocation, local_channel: Arc>) -> Self { let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); @@ -73,11 +69,14 @@ impl LocalTransport { internal_locations: locations_vec, location_set: PhantomData, local_channel, + target_location: PhantomData, } } } -impl Transport for LocalTransport { +impl Transport + for LocalTransport +{ fn locations(&self) -> Vec { return self.internal_locations.clone(); } @@ -122,32 +121,17 @@ mod tests { fn test_local_transport() { let v = 42; - let transport_channel = LocalTransport::::transport_channel(); + let transport_channel = Arc::new(LocalTransportChannel::::new()); let mut handles = Vec::new(); { - let config = transport_config!( - Alice, - Alice: (), - Bob: () - ); - - let transport_channel = transport_channel.clone(); - - let transport = LocalTransport::new(&config, transport_channel); + let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); handles.push(thread::spawn(move || { transport.send::(Alice::name(), Bob::name(), &v); })); } { - let config = transport_config!( - Bob, - Alice: (), - Bob: () - ); - // let transport = transport.clone(); - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(&config, transport_channel); + let transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); handles.push(thread::spawn(move || { let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); From ab005b28beaf3726cdb419117fa5dc831f9e3c57 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Tue, 19 Sep 2023 17:17:36 -0700 Subject: [PATCH 43/75] Use std::marker::PhantomData instead of core::marker::PhantomData --- chorus_lib/src/transport.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index d529a74..a4e8996 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -33,7 +33,7 @@ macro_rules! transport_config { $crate::transport::TransportConfig::<$crate::LocationSet!($choreography_loc, $( $loc ),*), _, _, _> { info: config, - location_set: core::marker::PhantomData, + location_set: std::marker::PhantomData, target_info: ($choreography_loc, $choreography_val), } } From 9c99e5cb058acd88a1b9eb568f68d1d1cf7ec431 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Wed, 20 Sep 2023 03:07:07 -0700 Subject: [PATCH 44/75] Change some imports --- chorus_book/src/guide-transport.md | 3 ++- chorus_lib/src/transport.rs | 16 +++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index a021db1..93a5b6e 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -33,8 +33,9 @@ To use the `local` transport, first import the `LocalTransport` struct from the # use chorus_lib::{LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; +# use chorus_lib::transport::local::LocalTransportChannel; # let transport_channel = Arc::new(LocalTransportChannel::::new()); -use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +use chorus_lib::transport::local::{LocalTransport}; let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); ``` diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index a4e8996..b49d255 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,22 +3,20 @@ pub mod http; pub mod local; -use crate::core::ChoreographyLocation; +use crate::core::{ChoreographyLocation, HList}; +use std::collections::HashMap; +use std::marker::PhantomData; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig< - L: crate::core::HList, - InfoType, - TargetLocation: ChoreographyLocation, - TargetInfoType, -> { +pub struct TransportConfig +{ /// The information about locations - pub info: std::collections::HashMap, + pub info: HashMap, /// The information about the target choreography pub target_info: (TargetLocation, TargetInfoType), /// The struct is parametrized by the location set (`L`). - pub location_set: std::marker::PhantomData, + pub location_set: PhantomData, } /// This macro makes a `TransportConfig`. From 0b91d606cc977d553c17dcdf9a34439c321d1d1e Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Wed, 20 Sep 2023 23:07:41 -0700 Subject: [PATCH 45/75] Change the API for building a TransportConfig; now we use builder pattern --- chorus_lib/src/core.rs | 22 +++++++++++ chorus_lib/src/transport.rs | 64 +++++++++++++++++++++++++++++++- chorus_lib/src/transport/http.rs | 36 +++++++++--------- 3 files changed, 103 insertions(+), 19 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 4fe152e..264072f 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -121,6 +121,28 @@ where } } +/// A trait to handle appending a type to our HList +pub trait Append: HList { + /// Return the result after appending + type Result: HList; +} + +impl Append for HNil +where + Head: ChoreographyLocation, +{ + type Result = HCons; +} + +impl Append for HCons +where + Head: ChoreographyLocation, + NewHead: ChoreographyLocation, + Tail: HList, +{ + type Result = HCons>; +} + // TODO(shumbo): Export the macro under the `core` module /// Macro to generate hlist diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index b49d255..8b9a336 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,7 +3,8 @@ pub mod http; pub mod local; -use crate::core::{ChoreographyLocation, HList}; +use crate::core::{Append, ChoreographyLocation, HList}; +use crate::LocationSet; use std::collections::HashMap; use std::marker::PhantomData; @@ -19,6 +20,67 @@ pub struct TransportConfig, } +/// Initiate a transport for a given target. +/// Note that information about other locations have to provided using `.with` +pub fn transport_for_target( + location: TargetLocation, + info: TargetInfoType, +) -> TransportConfig +where + TargetLocation: ChoreographyLocation, +{ + TransportConfig::for_target(location, info) +} + +impl + TransportConfig +{ + /// A transport for a given target + fn for_target(location: TargetLocation, info: TargetInfoType) -> Self { + Self { + info: HashMap::new(), + target_info: (location, info), + location_set: PhantomData, + } + } + + /// Assuming you have a way to append to HLists, and that `L2` is the type-level list + /// that results from appending `NewLocation` to `L`. + pub fn with( + self, + _location: NewLocation, + info: InfoType, + ) -> TransportConfig + where + // L2: RemoveHead, + L: Append, + NewLocation: ChoreographyLocation, + L2: HList, + { + let mut new_info = HashMap::new(); + for (k, v) in self.info.into_iter() { + new_info.insert(k, v); + } + // Assuming NewLocation has a `name` associated function + new_info.insert(NewLocation::name().to_string(), info); + + TransportConfig { + info: new_info, + target_info: self.target_info, + location_set: PhantomData, + } + } + + /// Finalize the `TransportConfig`. + pub fn build(self) -> TransportConfig { + TransportConfig { + info: self.info, + location_set: PhantomData, + target_info: self.target_info, + } + } +} + /// This macro makes a `TransportConfig`. #[macro_export] macro_rules! transport_config { diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index f94340a..753eb29 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,9 +12,9 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; -use crate::transport::TransportConfig; +use crate::transport::{TransportConfig}; #[cfg(test)] -use crate::transport_config; +use crate::transport::transport_for_target; use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, @@ -155,6 +155,9 @@ mod tests { #[derive(ChoreographyLocation)] struct Bob; + #[derive(ChoreographyLocation)] + struct Carol; + #[test] fn test_http_transport() { let v = 42; @@ -163,10 +166,9 @@ mod tests { let mut handles = Vec::new(); { - let config = transport_config!( - Alice => ("0.0.0.0".to_string(), 9010), - Bob: ("localhost".to_string(), 9011) - ); + let config = transport_for_target(Alice, ("0.0.0.0".to_string(), 9010)) + .with(Bob, ("localhost".to_string(), 9011)) + .build(); handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start @@ -175,10 +177,10 @@ mod tests { })); } { - let config = transport_config!( - Bob => ("0.0.0.0".to_string(), 9011), - Alice: ("localhost".to_string(), 9010), - ); + + let config = transport_for_target(Bob, ("0.0.0.0".to_string(), 9011)) + .with(Alice, ("localhost".to_string(), 9010)) + .build(); handles.push(thread::spawn(move || { let transport = HttpTransport::new(&config); @@ -199,10 +201,9 @@ mod tests { let mut handles = Vec::new(); { - let config = transport_config!( - Alice => ("0.0.0.0".to_string(), 9020), - Bob: ("localhost".to_string(), 9021) - ); + let config = transport_for_target(Alice, ("0.0.0.0".to_string(), 9020)) + .with(Bob, ("localhost".to_string(), 9021)) + .build(); handles.push(thread::spawn(move || { signal.send(()).unwrap(); @@ -211,10 +212,9 @@ mod tests { })); } { - let config = transport_config!( - Bob=> ("0.0.0.0".to_string(), 9021), - Alice: ("localhost".to_string(), 9020) - ); + let config = transport_for_target(Bob, ("0.0.0.0".to_string(), 9021)) + .with(Alice, ("localhost".to_string(), 9020)) + .build(); handles.push(thread::spawn(move || { // wait for Alice to start, which forces Alice to retry From 62f4a5995f856c4255e87172f0f84370dcf42e11 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 21 Sep 2023 05:49:28 -0700 Subject: [PATCH 46/75] Change the examples and the book to reflect the new API --- chorus_book/src/guide-transport.md | 17 ++++----- chorus_book/src/header.txt | 8 ++-- chorus_lib/examples/bookseller.rs | 8 ++-- chorus_lib/examples/bookseller2.rs | 40 ++++++++++---------- chorus_lib/examples/hello.rs | 10 ++--- chorus_lib/examples/loc-poly.rs | 8 ++-- chorus_lib/examples/tic-tac-toe.rs | 38 ++++++++++++------- chorus_lib/src/core.rs | 22 ----------- chorus_lib/src/transport.rs | 59 +++++++----------------------- chorus_lib/src/transport/http.rs | 21 +++++------ chorus_lib/src/transport/local.rs | 17 ++++----- 11 files changed, 97 insertions(+), 151 deletions(-) diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 93a5b6e..d037054 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -87,24 +87,21 @@ let mut handles: Vec> = Vec::new(); The `http` transport is used to execute choreographies on different machines. This is useful for executing choreographies in a distributed system. -To use the `http` transport, import the `HttpTransport` struct and the `transport_config` macro from the `chorus_lib` crate. +To use the `http` transport, import the `HttpTransport` struct and the `HttpTransportConfig` type alias from the `chorus_lib` crate. ```rust # extern crate chorus_lib; -use chorus_lib::transport::http::HttpTransport; -use chorus_lib::transport_config; +use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; ``` -The new constructor takes a "configuration" of type TransportConfig. To build the TransportConfig, you should use the macro transport_config and give it a key => value, followed by a comma (if you have more than one key/value), and a comma-separated list of key: values where the key => value is the target ChoreographyLocation and the value is the required information for the target, and each following key: value is a ChoreographyLocation and the required information for it (`(host_name, port)` in this case). For HttpTransport You can think of TransportConfig as a map from locations to the hostname and port of the location. But for a generic Transport, it can contain any kind of information. +The new constructor takes a "configuration" of type `HttpTransportConfig`. To build the config, you should use the pattern `HttpTransportConfig::for_target(target_location, target_information).with(other_location, other_location_information)...build()` where the target_location is the target ChoreographyLocation and target_information is the required information for the target, and each following `.with(other_location, other_location_information)` is a ChoreographyLocation and the required information for it (`(host_name, port)` in this case). For HttpTransport You can think of TransportConfig as a map from locations to the hostname and port of the location. But for a generic Transport, it can contain any kind of information. ```rust {{#include ./header.txt}} -# use chorus_lib::transport::http::{HttpTransport}; -# use chorus_lib::transport_config; -let config = transport_config!( - Alice => ("0.0.0.0".to_string(), 9010), - Bob: ("localhost".to_string(), 9011) - ); +# use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; +let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9010)) + .with(Bob, ("localhost".to_string(), 9011)) + .build(); let transport = HttpTransport::new(&config); ``` diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index ea4645e..2f3a421 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -9,7 +9,7 @@ # struct Bob; # #[derive(ChoreographyLocation)] # struct Carol; -# let transport_channel = Arc::new(LocalTransportChannel::::new()); -# let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); -# let bob_transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); -# let carol_transport = LocalTransport::new(Carol, Arc::clone(&transport_channel)); \ No newline at end of file +# let transport_channel = LocalTransportChannel::::new(); +# let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); +# let bob_transport = LocalTransport::new(Bob, transport_channel.clone()); +# let carol_transport = LocalTransport::new(Carol, transport_channel.clone()); \ No newline at end of file diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index abf01c5..4115bc9 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -1,7 +1,6 @@ extern crate chorus_lib; use std::io; -use std::sync::Arc; use std::thread; use chrono::NaiveDate; @@ -74,10 +73,9 @@ impl Choreography for BooksellerChoreography { } fn main() { - let transport_channel = Arc::new(LocalTransportChannel::::new()); - - let transport_seller = LocalTransport::new(Seller, Arc::clone(&transport_channel)); - let transport_buyer = LocalTransport::new(Buyer, Arc::clone(&transport_channel)); + let transport_channel = LocalTransportChannel::::new(); + let transport_seller = LocalTransport::new(Seller, transport_channel.clone()); + let transport_buyer = LocalTransport::new(Buyer, transport_channel.clone()); let seller_projector = Projector::new(Seller, transport_seller); let buyer_projector = Projector::new(Buyer, transport_buyer); diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 7267b2b..4ea5be3 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -143,16 +143,25 @@ fn main() { i }; - let transport_channel = - Arc::new(LocalTransportChannel::::new()); + let transport_channel = LocalTransportChannel::::new(); + let seller_projector = Arc::new(Projector::new( + Seller, + LocalTransport::new(Seller, transport_channel.clone()), + )); + let buyer1_projector = Arc::new(Projector::new( + Buyer1, + LocalTransport::new(Buyer1, transport_channel.clone()), + )); + let buyer2_projector = Arc::new(Projector::new( + Buyer2, + LocalTransport::new(Buyer2, transport_channel.clone()), + )); println!("Tries to buy HoTT with one buyer"); type OneBuyerBooksellerChoreography = BooksellerChoreography; let mut handles = Vec::new(); { - let transport = LocalTransport::new(Seller, Arc::clone(&transport_channel)); - let seller_projector = Projector::new(Seller, transport); - + let seller_projector = seller_projector.clone(); let inventory = inventory.clone(); handles.push(thread::spawn(move || { seller_projector.epp_and_run(OneBuyerBooksellerChoreography { @@ -163,9 +172,7 @@ fn main() { })); } { - let transport = LocalTransport::new(Buyer1, Arc::clone(&transport_channel)); - let buyer1_projector = Projector::new(Buyer1, transport); - + let buyer1_projector = buyer1_projector.clone(); handles.push(thread::spawn(move || { let result = buyer1_projector.epp_and_run(OneBuyerBooksellerChoreography { _marker: std::marker::PhantomData, @@ -179,9 +186,7 @@ fn main() { })); } { - let transport = LocalTransport::new(Buyer2, Arc::clone(&transport_channel)); - let buyer2_projector = Projector::new(Buyer2, transport); - + let buyer2_projector = buyer2_projector.clone(); handles.push(thread::spawn(move || { buyer2_projector.epp_and_run(OneBuyerBooksellerChoreography { _marker: std::marker::PhantomData, @@ -198,9 +203,7 @@ fn main() { type TwoBuyerBooksellerChoreography = BooksellerChoreography; let mut handles = Vec::new(); { - let transport = LocalTransport::new(Seller, Arc::clone(&transport_channel)); - let seller_projector = Projector::new(Seller, transport); - + let seller_projector = seller_projector.clone(); let inventory = inventory.clone(); handles.push(thread::spawn(move || { seller_projector.epp_and_run(TwoBuyerBooksellerChoreography { @@ -211,9 +214,7 @@ fn main() { })); } { - let transport = LocalTransport::new(Buyer1, Arc::clone(&transport_channel)); - let buyer1_projector = Projector::new(Buyer1, transport); - + let buyer1_projector = buyer1_projector.clone(); handles.push(thread::spawn(move || { let result = buyer1_projector.epp_and_run(TwoBuyerBooksellerChoreography { _marker: std::marker::PhantomData, @@ -227,10 +228,7 @@ fn main() { })); } { - let transport = LocalTransport::new(Buyer2, Arc::clone(&transport_channel)); - - let buyer2_projector = Projector::new(Buyer2, transport); - + let buyer2_projector = buyer2_projector.clone(); handles.push(thread::spawn(move || { buyer2_projector.epp_and_run(TwoBuyerBooksellerChoreography { _marker: std::marker::PhantomData, diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index a69eadf..beeb9d5 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -1,6 +1,5 @@ extern crate chorus_lib; -use std::sync::Arc; use std::thread; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; @@ -41,18 +40,19 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a transport channel - let transport_channel = Arc::new(LocalTransportChannel::::new()); - + let transport_channel = LocalTransportChannel::::new(); // Run the choreography in two threads { - let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(Alice, transport_channel); handles.push(thread::spawn(move || { let p = Projector::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } { - let transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); + let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(Bob, transport_channel); handles.push(thread::spawn(move || { let p = Projector::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index 57f23a7..ad6f09a 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -1,6 +1,5 @@ extern crate chorus_lib; use std::fmt::Debug; -use std::sync::Arc; use std::thread; use chorus_lib::core::{ @@ -58,11 +57,10 @@ impl Choreography> for MainChoreography { } fn main() { - let transport_channel = - Arc::new(LocalTransportChannel::::new()); + let transport_channel = LocalTransportChannel::::new(); let mut handles = vec![]; { - let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); + let transport = LocalTransport::new(Alice, transport_channel.clone()); handles.push(thread::spawn(|| { let p = Projector::new(Alice, transport); let v = p.epp_and_run(MainChoreography); @@ -70,7 +68,7 @@ fn main() { })); } { - let transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); + let transport = LocalTransport::new(Bob, transport_channel.clone()); handles.push(thread::spawn(|| { let p = Projector::new(Bob, transport); p.epp_and_run(MainChoreography); diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index dd93cad..200b797 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -1,12 +1,11 @@ /// Choreographic tik-tak-toe game extern crate chorus_lib; -use chorus_lib::transport_config; +use chorus_lib::transport::http::HttpTransportConfig; use chorus_lib::{ core::{ ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, }, - // http_config, transport::http::HttpTransport, LocationSet, }; @@ -296,13 +295,19 @@ fn main() { match args.player { 'X' => { - let config = transport_config!( - PlayerX => (args.hostname.as_str().to_string(), args.port), - PlayerO: (args.opponent_hostname.as_str().to_string(), args.opponent_port) - ); - + let config = HttpTransportConfig::for_target( + PlayerX, + (args.hostname.as_str().to_string(), args.port), + ) + .with( + PlayerO, + ( + args.opponent_hostname.as_str().to_string(), + args.opponent_port, + ), + ) + .build(); let transport = HttpTransport::new(&config); - let projector = Projector::new(PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.local(brain), @@ -310,11 +315,18 @@ fn main() { }); } 'O' => { - let config = transport_config!( - PlayerO => (args.hostname.as_str().to_string(), args.port), - PlayerX: (args.opponent_hostname.as_str().to_string(), args.opponent_port) - ); - + let config = HttpTransportConfig::for_target( + PlayerO, + (args.hostname.as_str().to_string(), args.port), + ) + .with( + PlayerX, + ( + args.opponent_hostname.as_str().to_string(), + args.opponent_port, + ), + ) + .build(); let transport = HttpTransport::new(&config); let projector = Projector::new(PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 264072f..4fe152e 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -121,28 +121,6 @@ where } } -/// A trait to handle appending a type to our HList -pub trait Append: HList { - /// Return the result after appending - type Result: HList; -} - -impl Append for HNil -where - Head: ChoreographyLocation, -{ - type Result = HCons; -} - -impl Append for HCons -where - Head: ChoreographyLocation, - NewHead: ChoreographyLocation, - Tail: HList, -{ - type Result = HCons>; -} - // TODO(shumbo): Export the macro under the `core` module /// Macro to generate hlist diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 8b9a336..43c7f49 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,7 +3,7 @@ pub mod http; pub mod local; -use crate::core::{Append, ChoreographyLocation, HList}; +use crate::core::{ChoreographyLocation, HCons, HList}; use crate::LocationSet; use std::collections::HashMap; use std::marker::PhantomData; @@ -20,43 +20,29 @@ pub struct TransportConfig, } -/// Initiate a transport for a given target. -/// Note that information about other locations have to provided using `.with` -pub fn transport_for_target( - location: TargetLocation, - info: TargetInfoType, -) -> TransportConfig -where - TargetLocation: ChoreographyLocation, +impl + TransportConfig { - TransportConfig::for_target(location, info) -} - -impl - TransportConfig -{ - /// A transport for a given target - fn for_target(location: TargetLocation, info: TargetInfoType) -> Self { + /// A transport for a given target. + pub fn for_target(location: TargetLocation, info: TargetInfoType) -> Self { Self { info: HashMap::new(), target_info: (location, info), location_set: PhantomData, } } +} - /// Assuming you have a way to append to HLists, and that `L2` is the type-level list - /// that results from appending `NewLocation` to `L`. - pub fn with( +impl + TransportConfig +{ + /// Adds information about a new `ChoreographyLocation`. + pub fn with( self, _location: NewLocation, info: InfoType, - ) -> TransportConfig - where - // L2: RemoveHead, - L: Append, - NewLocation: ChoreographyLocation, - L2: HList, - { + ) -> TransportConfig, InfoType, TargetLocation, TargetInfoType> +where { let mut new_info = HashMap::new(); for (k, v) in self.info.into_iter() { new_info.insert(k, v); @@ -80,22 +66,3 @@ impl } } } - -/// This macro makes a `TransportConfig`. -#[macro_export] -macro_rules! transport_config { - ( $choreography_loc:ident => $choreography_val:expr, $( $loc:ident : $val:expr ),* $(,)? ) => { - { - let mut config = std::collections::HashMap::new(); - $( - config.insert($loc::name().to_string(), $val); - )* - - $crate::transport::TransportConfig::<$crate::LocationSet!($choreography_loc, $( $loc ),*), _, _, _> { - info: config, - location_set: std::marker::PhantomData, - target_info: ($choreography_loc, $choreography_val), - } - } - }; -} diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 753eb29..ae9875c 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,9 +12,9 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; -use crate::transport::{TransportConfig}; -#[cfg(test)] -use crate::transport::transport_for_target; +use crate::transport::TransportConfig; +// #[cfg(test)] +// use crate::transport::transport_for_target; use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, @@ -22,6 +22,8 @@ use crate::{ }; type QueueMap = HashMap>; +/// A type alias for `TransportConfig`s used for building `HttpTransport` +pub type HttpTransportConfig = TransportConfig; /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; @@ -48,9 +50,7 @@ pub struct HttpTransport { impl HttpTransport { /// Creates a new `HttpTransport` instance from the configuration. - pub fn new( - http_config: &TransportConfig, - ) -> Self + pub fn new(http_config: &HttpTransportConfig) -> Self where TLocation: Member, { @@ -166,7 +166,7 @@ mod tests { let mut handles = Vec::new(); { - let config = transport_for_target(Alice, ("0.0.0.0".to_string(), 9010)) + let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9010)) .with(Bob, ("localhost".to_string(), 9011)) .build(); @@ -177,8 +177,7 @@ mod tests { })); } { - - let config = transport_for_target(Bob, ("0.0.0.0".to_string(), 9011)) + let config = HttpTransportConfig::for_target(Bob, ("0.0.0.0".to_string(), 9011)) .with(Alice, ("localhost".to_string(), 9010)) .build(); @@ -201,7 +200,7 @@ mod tests { let mut handles = Vec::new(); { - let config = transport_for_target(Alice, ("0.0.0.0".to_string(), 9020)) + let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9020)) .with(Bob, ("localhost".to_string(), 9021)) .build(); @@ -212,7 +211,7 @@ mod tests { })); } { - let config = transport_for_target(Bob, ("0.0.0.0".to_string(), 9021)) + let config = HttpTransportConfig::for_target(Bob, ("0.0.0.0".to_string(), 9021)) .with(Alice, ("localhost".to_string(), 9020)) .build(); diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index e594308..511b3bc 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -16,16 +16,15 @@ use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; /// A Transport channel used between multiple `Transport`s. -#[derive(Clone)] pub struct LocalTransportChannel { /// The location set where the channel is defined on. pub location_set: std::marker::PhantomData, - queue_map: Arc, + queue_map: QueueMap, } impl LocalTransportChannel { /// Creates a `LocalTransportChannel`. - pub fn new() -> LocalTransportChannel { + pub fn new() -> Arc> { let mut queue_map: QueueMap = HashMap::new(); for sender in L::to_string_list() { let mut n = HashMap::new(); @@ -35,10 +34,10 @@ impl LocalTransportChannel { queue_map.insert(sender.to_string(), n); } - LocalTransportChannel { + Arc::new(LocalTransportChannel { location_set: PhantomData, - queue_map: Arc::new(queue_map.into()), - } + queue_map, + }) } } @@ -121,17 +120,17 @@ mod tests { fn test_local_transport() { let v = 42; - let transport_channel = Arc::new(LocalTransportChannel::::new()); + let transport_channel = LocalTransportChannel::::new(); let mut handles = Vec::new(); { - let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); + let transport = LocalTransport::new(Alice, transport_channel.clone()); handles.push(thread::spawn(move || { transport.send::(Alice::name(), Bob::name(), &v); })); } { - let transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); + let transport = LocalTransport::new(Bob, transport_channel.clone()); handles.push(thread::spawn(move || { let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); From b5dfba0612c0714a5c398aac357797dec529bbb6 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 21 Sep 2023 05:55:54 -0700 Subject: [PATCH 47/75] Remove a bunch of unnecessary Arcs --- chorus_book/src/guide-projector.md | 10 ++++------ chorus_book/src/guide-transport.md | 24 ++++++++++-------------- chorus_book/src/header.txt | 1 - 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 0af3ccb..1e82a6b 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -8,12 +8,11 @@ To create a `Projector`, you need to provide the target location and the transpo ```rust # extern crate chorus_lib; -# use std::sync::Arc; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::core::{ChoreographyLocation, Projector}; # use chorus_lib::{LocationSet}; -# let transport_channel = Arc::new(LocalTransportChannel::::new()); -# let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); +# let transport_channel = LocalTransportChannel::::new(); +# let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -31,12 +30,11 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro ```rust # extern crate chorus_lib; -# use std::sync::Arc; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; # use chorus_lib::{LocationSet}; -# let transport_channel = Arc::new(LocalTransportChannel::::new()); -# let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); +# let transport_channel = LocalTransportChannel::::new(); +# let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index d037054..67315e2 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -12,7 +12,6 @@ The `local` transport is used to execute choreographies on the same machine on d ```rust # extern crate chorus_lib; -# use std::sync::Arc; # use chorus_lib::core::{ChoreographyLocation}; # use chorus_lib::{LocationSet}; # #[derive(ChoreographyLocation)] @@ -21,34 +20,32 @@ The `local` transport is used to execute choreographies on the same machine on d # struct Bob; use chorus_lib::transport::local::LocalTransportChannel; -let transport_channel = Arc::new(LocalTransportChannel::::new()); +let transport_channel = LocalTransportChannel::::new(); ``` To use the `local` transport, first import the `LocalTransport` struct from the `chorus_lib` crate. ```rust # extern crate chorus_lib; -# use std::sync::Arc; # use chorus_lib::core::{ChoreographyLocation}; # use chorus_lib::{LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # use chorus_lib::transport::local::LocalTransportChannel; -# let transport_channel = Arc::new(LocalTransportChannel::::new()); +# let transport_channel = LocalTransportChannel::::new(); use chorus_lib::transport::local::{LocalTransport}; -let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); +let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); ``` Then build the transport by using the `LocalTransport::new` associated function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. -You can construct a `LocalTransport` instance by passing a target location(lained in the [Projector section](./guide-projector.md)) and a `std::sync::Arc` of `LocalTransportChannel` to the `new` method. +You can construct a `LocalTransport` instance by passing a target location(lained in the [Projector section](./guide-projector.md)) and a `LocalTransportChannel`. -Because of the nature of the `Local` transport, you must make a `std::sync::Arc` from the `LocalTransportChannel`, by using the `std::sync::Arc::clone(&local_transport)`. +Because of the nature of the `Local` transport, you must use the same `LocalTransportChannel` instance for all locations. You can `clone` the `LocalTransprotChannel` instance and pass it to each `Projector::new` constructor. ```rust # extern crate chorus_lib; -# use std::sync::Arc; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use std::thread; # use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector}; @@ -63,11 +60,11 @@ Because of the nature of the `Local` transport, you must make a `std::sync::Arc` # fn run(self, op: &impl ChoreoOp) { # } # } -let transport_channel = Arc::new(LocalTransportChannel::::new()); +let transport_channel = LocalTransportChannel::::new(); let mut handles: Vec> = Vec::new(); { // create a transport for Alice - let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); + let transport = LocalTransport::new(Alice, transport_channel.clone()); handles.push(thread::spawn(move || { let p = Projector::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); @@ -75,7 +72,7 @@ let mut handles: Vec> = Vec::new(); } { // create another for Bob - let transport = LocalTransport::new(Bob, Arc::clone(&transport_channel)); + let transport = LocalTransport::new(Bob, transport_channel.clone()); handles.push(thread::spawn(move || { let p = Projector::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); @@ -119,7 +116,6 @@ Note that when calling `epp_and_run` on a `Projector`, you will get a compile er ```rust, compile_fail # extern crate chorus_lib; -# use std::sync::Arc; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; # use chorus_lib::{LocationSet}; @@ -135,8 +131,8 @@ impl Choreography for HelloWorldChoreography { } } -let transport_channel = Arc::new(LocalTransportChannel::::new()); -let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel)); +let transport_channel = LocalTransportChannel::::new(); +let transport = LocalTransport::new(Alice, transport_channel.clone()); let projector = Projector::new(Alice, transport); projector.epp_and_run(HelloWorldChoreography); ``` diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 2f3a421..60d16f4 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,5 +1,4 @@ # extern crate chorus_lib; -# use std::sync::Arc; # use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::{LocationSet}; From 2b379cd9a948dad5c0886876faba527df88f5c75 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 21 Sep 2023 16:30:29 -0700 Subject: [PATCH 48/75] Update the guide for Transport --- chorus_book/src/guide-transport.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 67315e2..311f8fa 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -24,6 +24,8 @@ let transport_channel = LocalTransportChannel::::new() ``` To use the `local` transport, first import the `LocalTransport` struct from the `chorus_lib` crate. + +Then build the transport by using the `LocalTransport::new` associated function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. ```rust # extern crate chorus_lib; @@ -38,10 +40,6 @@ use chorus_lib::transport::local::{LocalTransport}; let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); ``` - Then build the transport by using the `LocalTransport::new` associated function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. - -You can construct a `LocalTransport` instance by passing a target location(lained in the [Projector section](./guide-projector.md)) and a `LocalTransportChannel`. - Because of the nature of the `Local` transport, you must use the same `LocalTransportChannel` instance for all locations. You can `clone` the `LocalTransprotChannel` instance and pass it to each `Projector::new` constructor. ```rust @@ -91,13 +89,13 @@ To use the `http` transport, import the `HttpTransport` struct and the `HttpTran use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; ``` -The new constructor takes a "configuration" of type `HttpTransportConfig`. To build the config, you should use the pattern `HttpTransportConfig::for_target(target_location, target_information).with(other_location, other_location_information)...build()` where the target_location is the target ChoreographyLocation and target_information is the required information for the target, and each following `.with(other_location, other_location_information)` is a ChoreographyLocation and the required information for it (`(host_name, port)` in this case). For HttpTransport You can think of TransportConfig as a map from locations to the hostname and port of the location. But for a generic Transport, it can contain any kind of information. +The primary constructor requires an argument of type `HttpTransportConfig`. To create an instance of this configuration, utilize the builder pattern. Start with `HttpTransportConfig::for_target(target_location, target_information)` and then chain additional locations using the `.with(other_location, other_location_information)` method. Conclude with `.build()`. In this context, `target_location` refers to the target `ChoreographyLocation`, and `target_information` is specifically a tuple of `(host_name: String, port: u16)`. Subsequent calls to `.with()` allow you to add more locations and their respective information. For the `HttpTransport`, think of `HttpTransportConfig` as a mapping from locations to their hostnames and ports. However, for other generic transports, the corresponding information might vary, potentially diverging from the `(host_name, port)` format presented here. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. ```rust {{#include ./header.txt}} # use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; -let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9010)) - .with(Bob, ("localhost".to_string(), 9011)) +let config = HttpTransportConfig::for_target(Alice, ("localhost".to_string(), 8080)) + .with(Bob, ("localhost".to_string(), 8081)) .build(); let transport = HttpTransport::new(&config); From 2597d37aab669678499b3ada31063e85c5602a00 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 21 Sep 2023 16:45:07 -0700 Subject: [PATCH 49/75] Remove some empty spaces in the book --- chorus_book/src/guide-projector.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 1e82a6b..7c01015 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -17,8 +17,6 @@ To create a `Projector`, you need to provide the target location and the transpo # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; -# - let projector = Projector::new(Alice, alice_transport); ``` @@ -45,8 +43,6 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # fn run(self, op: &impl ChoreoOp) { # } # } -# - # let projector = Projector::new(Alice, alice_transport); projector.epp_and_run(HelloWorldChoreography); ``` From f0b8461ed24a0e429c6157c82ede2a42faa4819d Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 21 Sep 2023 16:46:55 -0700 Subject: [PATCH 50/75] Remove redundant comments --- chorus_lib/src/transport.rs | 1 - chorus_lib/src/transport/http.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 43c7f49..3329bd1 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -47,7 +47,6 @@ where { for (k, v) in self.info.into_iter() { new_info.insert(k, v); } - // Assuming NewLocation has a `name` associated function new_info.insert(NewLocation::name().to_string(), info); TransportConfig { diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index ae9875c..53a574d 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -13,8 +13,6 @@ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; use crate::transport::TransportConfig; -// #[cfg(test)] -// use crate::transport::transport_for_target; use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, From 83a5212927d99f5e027b7b640e71df9fbecae769 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Wed, 27 Sep 2023 19:20:51 -0700 Subject: [PATCH 51/75] Change the API so that we have a builder pattern for LocalTransportChannel --- chorus_book/src/guide-projector.md | 4 +-- chorus_book/src/guide-transport.md | 27 ++++++++++----- chorus_book/src/header.txt | 2 +- chorus_lib/examples/bookseller.rs | 2 +- chorus_lib/examples/bookseller2.rs | 6 +++- chorus_lib/examples/hello.rs | 11 +++--- chorus_lib/examples/loc-poly.rs | 6 +++- chorus_lib/examples/tic-tac-toe.rs | 12 +++---- chorus_lib/src/core.rs | 27 ++++----------- chorus_lib/src/transport.rs | 25 ++++---------- chorus_lib/src/transport/http.rs | 39 +++++++-------------- chorus_lib/src/transport/local.rs | 54 +++++++++++++++++++++--------- 12 files changed, 108 insertions(+), 107 deletions(-) diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 7c01015..0a84c40 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -11,7 +11,7 @@ To create a `Projector`, you need to provide the target location and the transpo # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::core::{ChoreographyLocation, Projector}; # use chorus_lib::{LocationSet}; -# let transport_channel = LocalTransportChannel::::new(); +# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] # struct Alice; @@ -31,7 +31,7 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; # use chorus_lib::{LocationSet}; -# let transport_channel = LocalTransportChannel::::new(); +# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] # struct Alice; diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 311f8fa..504030e 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -8,7 +8,7 @@ ChoRus provides two built-in transports: `local` and `http`. ### The Local Transport -The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. Each `local` transport is defined over `LocalTransportChannel`, which contains the set of `ChoreographyLocation` that the `local` transport operates on. You can build a `LocalTransportChannel` by importing the `LocalTransportChannel` stsruct from the `chorus_lib` crate. +The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. Each `local` transport is defined over `LocalTransportChannel`, which contains the set of `ChoreographyLocation` that the `local` transport operates on. You can build a `LocalTransportChannel` by importing the `LocalTransportChannel` struct from the `chorus_lib` crate. ```rust # extern crate chorus_lib; @@ -20,7 +20,7 @@ The `local` transport is used to execute choreographies on the same machine on d # struct Bob; use chorus_lib::transport::local::LocalTransportChannel; -let transport_channel = LocalTransportChannel::::new(); +let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); ``` To use the `local` transport, first import the `LocalTransport` struct from the `chorus_lib` crate. @@ -34,7 +34,7 @@ Then build the transport by using the `LocalTransport::new` associated function, # #[derive(ChoreographyLocation)] # struct Alice; # use chorus_lib::transport::local::LocalTransportChannel; -# let transport_channel = LocalTransportChannel::::new(); +# let transport_channel = LocalTransportChannel::new().with(Alice); use chorus_lib::transport::local::{LocalTransport}; let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); @@ -58,7 +58,7 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # fn run(self, op: &impl ChoreoOp) { # } # } -let transport_channel = LocalTransportChannel::::new(); +let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); let mut handles: Vec> = Vec::new(); { // create a transport for Alice @@ -89,23 +89,32 @@ To use the `http` transport, import the `HttpTransport` struct and the `HttpTran use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; ``` -The primary constructor requires an argument of type `HttpTransportConfig`. To create an instance of this configuration, utilize the builder pattern. Start with `HttpTransportConfig::for_target(target_location, target_information)` and then chain additional locations using the `.with(other_location, other_location_information)` method. Conclude with `.build()`. In this context, `target_location` refers to the target `ChoreographyLocation`, and `target_information` is specifically a tuple of `(host_name: String, port: u16)`. Subsequent calls to `.with()` allow you to add more locations and their respective information. For the `HttpTransport`, think of `HttpTransportConfig` as a mapping from locations to their hostnames and ports. However, for other generic transports, the corresponding information might vary, potentially diverging from the `(host_name, port)` format presented here. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. +The primary constructor requires an argument of type `HttpTransportConfig`. To create an instance of this configuration, start with `HttpTransportConfig::for_target(target_location, (hostname, port))`. It will create set a projection target and the hostname and port to listen on. Then, provide information to connect to other locations by method-chaining the `.with(other_location, (hostname, port))` method. You can think of `HttpTransportConfig` as a mapping from locations to their hostnames and ports. ```rust {{#include ./header.txt}} # use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; let config = HttpTransportConfig::for_target(Alice, ("localhost".to_string(), 8080)) - .with(Bob, ("localhost".to_string(), 8081)) - .build(); + .with(Bob, ("localhost".to_string(), 8081)); -let transport = HttpTransport::new(&config); +let transport = HttpTransport::new(config); ``` In the above example, the transport will start the HTTP server on port 8080 on localhost. If Alice needs to send a message to Bob, it will use `http://localhost:8081` as the destination. ## Creating a Custom Transport -You can also create your own transport by implementing the `Transport` trait. See the API documentation for more details. +You can also create your own transport by implementing the `Transport` trait. It might be helpful first build a `TransportConfig` to have the the information that you need for each `ChoreographyLocation`, and then have a constructor that takes the `TransportConfig` and builds the `Transport` based on it. While the syntax is similar to `HttpTransportConfig`, the type of information for each `ChoreographyLocation` might diverge from the `(host_name, port)` format presented here. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. + +```rust +{{#include ./header.txt}} +# use chorus_lib::transport::TransportConfig; +let config = TransportConfig::for_target(Alice, ()) + .with(Bob, ("localhost".to_string(), 8081)) + .with(Carol, ("localhost".to_string(), 8082)); +``` + +See the API documentation for more details. ### Note on the location set of the Choreography diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 60d16f4..7231c30 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -8,7 +8,7 @@ # struct Bob; # #[derive(ChoreographyLocation)] # struct Carol; -# let transport_channel = LocalTransportChannel::::new(); +# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob).with(Carol); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # let bob_transport = LocalTransport::new(Bob, transport_channel.clone()); # let carol_transport = LocalTransport::new(Carol, transport_channel.clone()); \ No newline at end of file diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 4115bc9..7889aa6 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -73,7 +73,7 @@ impl Choreography for BooksellerChoreography { } fn main() { - let transport_channel = LocalTransportChannel::::new(); + let transport_channel = LocalTransportChannel::new().with(Seller).with(Buyer); let transport_seller = LocalTransport::new(Seller, transport_channel.clone()); let transport_buyer = LocalTransport::new(Buyer, transport_channel.clone()); diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 4ea5be3..729d5af 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -143,7 +143,11 @@ fn main() { i }; - let transport_channel = LocalTransportChannel::::new(); + let transport_channel = LocalTransportChannel::new() + .with(Seller) + .with(Buyer1) + .with(Buyer2); + let seller_projector = Arc::new(Projector::new( Seller, LocalTransport::new(Seller, transport_channel.clone()), diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index beeb9d5..ba8bcfd 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -40,19 +40,20 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a transport channel - let transport_channel = LocalTransportChannel::::new(); + // let transport_channel = LocalTransportChannel::::new(); + let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); // Run the choreography in two threads { - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(Alice, transport_channel); + // let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(Alice, transport_channel.clone()); handles.push(thread::spawn(move || { let p = Projector::new(Alice, transport); p.epp_and_run(HelloWorldChoreography); })); } { - let transport_channel = transport_channel.clone(); - let transport = LocalTransport::new(Bob, transport_channel); + // let transport_channel = transport_channel.clone(); + let transport = LocalTransport::new(Bob, transport_channel.clone()); handles.push(thread::spawn(move || { let p = Projector::new(Bob, transport); p.epp_and_run(HelloWorldChoreography); diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index ad6f09a..e6b8e0b 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -57,7 +57,11 @@ impl Choreography> for MainChoreography { } fn main() { - let transport_channel = LocalTransportChannel::::new(); + let transport_channel = LocalTransportChannel::new() + .with(Alice) + .with(Bob) + .with(Carol); + let mut handles = vec![]; { let transport = LocalTransport::new(Alice, transport_channel.clone()); diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 200b797..79abb03 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -305,9 +305,9 @@ fn main() { args.opponent_hostname.as_str().to_string(), args.opponent_port, ), - ) - .build(); - let transport = HttpTransport::new(&config); + ); + + let transport = HttpTransport::new(config); let projector = Projector::new(PlayerX, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.local(brain), @@ -325,9 +325,9 @@ fn main() { args.opponent_hostname.as_str().to_string(), args.opponent_port, ), - ) - .build(); - let transport = HttpTransport::new(&config); + ); + + let transport = HttpTransport::new(config); let projector = Projector::new(PlayerO, transport); projector.epp_and_run(TicTacToeChoreography { brain_for_x: projector.remote(PlayerX), diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 4fe152e..f2ba43e 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -96,12 +96,11 @@ pub trait HList { /// returns fn to_string_list() -> Vec<&'static str>; } + /// end of HList -#[derive(Clone)] pub struct HNil; -/// An element of HList -#[derive(Clone)] +/// An element of HList pub struct HCons(Head, Tail); impl HList for HNil { @@ -171,22 +170,6 @@ where { } -/// Equal trait -pub trait Equal {} - -// Base case: HNil is equal to HNil -impl Equal for HNil {} - -// Recursive case: Head::Tail is equal to L if -// 1. Head is a member of L -// 2. Tail is equal to the remainder of L -impl Equal> for HCons -where - Head: Member, - Tail: Equal, -{ -} - /// Provides a method to work with located values at the current location pub struct Unwrapper { phantom: PhantomData, @@ -280,7 +263,11 @@ pub trait Choreography { /// Provides methods to send and receive messages. /// /// The trait provides methods to send and receive messages between locations. Implement this trait to define a custom transport. -pub trait Transport { +/// +/// The type parameter `L` is the location set that the transport is operating on. +/// +/// The type paramter `TargetLocation` is the target `ChoreographyLocation`. +pub trait Transport { /// Returns a list of locations. fn locations(&self) -> Vec; /// Sends a message from `from` to `to`. diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 3329bd1..9f8209a 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -13,11 +13,11 @@ use std::marker::PhantomData; pub struct TransportConfig { /// The information about locations - pub info: HashMap, + info: HashMap, /// The information about the target choreography - pub target_info: (TargetLocation, TargetInfoType), + target_info: (TargetLocation, TargetInfoType), /// The struct is parametrized by the location set (`L`). - pub location_set: PhantomData, + location_set: PhantomData, } impl @@ -38,30 +38,17 @@ impl { /// Adds information about a new `ChoreographyLocation`. pub fn with( - self, + mut self, _location: NewLocation, info: InfoType, ) -> TransportConfig, InfoType, TargetLocation, TargetInfoType> where { - let mut new_info = HashMap::new(); - for (k, v) in self.info.into_iter() { - new_info.insert(k, v); - } - new_info.insert(NewLocation::name().to_string(), info); + self.info.insert(NewLocation::name().to_string(), info); - TransportConfig { - info: new_info, - target_info: self.target_info, - location_set: PhantomData, - } - } - - /// Finalize the `TransportConfig`. - pub fn build(self) -> TransportConfig { TransportConfig { info: self.info, - location_set: PhantomData, target_info: self.target_info, + location_set: PhantomData, } } } diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 53a574d..0c20a58 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -26,15 +26,6 @@ pub type HttpTransportConfig = TransportConfig -#[derive(Clone)] -pub struct HttpConfig { - /// The information about locations - pub info: HashMap, - /// The struct is parametrized by the location set (`L`). - pub location_set: PhantomData, -} - /// The HTTP transport. pub struct HttpTransport { config: HashMap, @@ -48,7 +39,7 @@ pub struct HttpTransport { impl HttpTransport { /// Creates a new `HttpTransport` instance from the configuration. - pub fn new(http_config: &HttpTransportConfig) -> Self + pub fn new(http_config: HttpTransportConfig) -> Self where TLocation: Member, { @@ -60,8 +51,6 @@ impl HttpTransport { Arc::new(m.into()) }; - let info = &http_config.info; - let (_, (hostname, port)) = &http_config.target_info; let server = Arc::new(Server::http(format!("{}:{}", hostname, port)).unwrap()); let join_handle = Some({ @@ -98,7 +87,7 @@ impl HttpTransport { let agent = AgentBuilder::new().build(); Self { - config: info.clone(), + config: http_config.info, agent, join_handle, server, @@ -116,7 +105,9 @@ impl Drop for HttpTransport { } } -impl Transport for HttpTransport { +impl Transport + for HttpTransport +{ fn locations(&self) -> Vec { Vec::from_iter(self.config.keys().map(|s| s.clone())) } @@ -165,22 +156,20 @@ mod tests { let mut handles = Vec::new(); { let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9010)) - .with(Bob, ("localhost".to_string(), 9011)) - .build(); + .with(Bob, ("localhost".to_string(), 9011)); handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start - let transport = HttpTransport::new(&config); + let transport = HttpTransport::new(config); transport.send::(Alice::name(), Bob::name(), &v); })); } { let config = HttpTransportConfig::for_target(Bob, ("0.0.0.0".to_string(), 9011)) - .with(Alice, ("localhost".to_string(), 9010)) - .build(); + .with(Alice, ("localhost".to_string(), 9010)); handles.push(thread::spawn(move || { - let transport = HttpTransport::new(&config); + let transport = HttpTransport::new(config); signal.send(()).unwrap(); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); @@ -199,25 +188,23 @@ mod tests { let mut handles = Vec::new(); { let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9020)) - .with(Bob, ("localhost".to_string(), 9021)) - .build(); + .with(Bob, ("localhost".to_string(), 9021)); handles.push(thread::spawn(move || { signal.send(()).unwrap(); - let transport = HttpTransport::new(&config); + let transport = HttpTransport::new(config); transport.send::(Alice::name(), Bob::name(), &v); })); } { let config = HttpTransportConfig::for_target(Bob, ("0.0.0.0".to_string(), 9021)) - .with(Alice, ("localhost".to_string(), 9020)) - .build(); + .with(Alice, ("localhost".to_string(), 9020)); handles.push(thread::spawn(move || { // wait for Alice to start, which forces Alice to retry wait.recv().unwrap(); sleep(Duration::from_millis(100)); - let transport = HttpTransport::new(&config); + let transport = HttpTransport::new(config); let v2 = transport.receive::(Alice::name(), Bob::name()); assert_eq!(v, v2); })); diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 511b3bc..3ba58b3 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,37 +7,60 @@ use serde_json; use std::marker::PhantomData; -#[cfg(test)] use crate::LocationSet; -use crate::core::{ChoreographyLocation, HList, Portable, Transport}; +use crate::core::{ChoreographyLocation, HCons, HList, Portable, Transport}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; /// A Transport channel used between multiple `Transport`s. -pub struct LocalTransportChannel { +pub struct LocalTransportChannel { /// The location set where the channel is defined on. - pub location_set: std::marker::PhantomData, - queue_map: QueueMap, + location_set: std::marker::PhantomData, + queue_map: Arc, +} + +impl Clone for LocalTransportChannel { + fn clone(&self) -> Self { + LocalTransportChannel { + location_set: PhantomData, + queue_map: self.queue_map.clone(), + } + } +} + +impl LocalTransportChannel { + /// Creates a new `LocalTransportChannel` instance + pub fn new() -> Self { + Self { + location_set: PhantomData, + queue_map: HashMap::new().into(), + } + } } impl LocalTransportChannel { - /// Creates a `LocalTransportChannel`. - pub fn new() -> Arc> { + /// Adds a new location to the set of locations in the `LocalTransportChannel`. + pub fn with( + self, + _location: NewLocation, + ) -> LocalTransportChannel> { let mut queue_map: QueueMap = HashMap::new(); - for sender in L::to_string_list() { + let mut str_list = L::to_string_list(); + str_list.push(NewLocation::name()); + for sender in &str_list { let mut n = HashMap::new(); - for receiver in L::to_string_list() { + for receiver in &str_list { n.insert(receiver.to_string(), BlockingQueue::new()); } queue_map.insert(sender.to_string(), n); } - Arc::new(LocalTransportChannel { + LocalTransportChannel { location_set: PhantomData, - queue_map, - }) + queue_map: queue_map.into(), + } } } @@ -46,17 +69,16 @@ impl LocalTransportChannel { /// This transport uses a blocking queue to allow for communication between threads. Each location must be executed in its thread. /// /// Unlike network-based transports, all locations must share the same `LocalTransport` instance. The struct implements `Clone` so that it can be shared across threads. -#[derive(Clone)] pub struct LocalTransport { internal_locations: Vec, location_set: PhantomData, - local_channel: Arc>, + local_channel: LocalTransportChannel, target_location: PhantomData, } impl LocalTransport { /// Creates a new `LocalTransport` instance from a Target `ChoreographyLocation` and a `LocalTransportChannel`. - pub fn new(_target: TargetLocation, local_channel: Arc>) -> Self { + pub fn new(_target: TargetLocation, local_channel: LocalTransportChannel) -> Self { let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); @@ -120,7 +142,7 @@ mod tests { fn test_local_transport() { let v = 42; - let transport_channel = LocalTransportChannel::::new(); + let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); let mut handles = Vec::new(); { From 3542ebb2bc792428ccbdfbe0e90ed67e48b63785 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sat, 30 Sep 2023 09:46:45 -0700 Subject: [PATCH 52/75] export `LocationSet` under the `core` module --- chorus_book/src/guide-projector.md | 6 ++---- chorus_book/src/guide-transport.md | 14 +++++--------- chorus_book/src/header.txt | 3 +-- chorus_lib/examples/bookseller.rs | 3 +-- chorus_lib/examples/bookseller2.rs | 3 +-- chorus_lib/examples/hello.rs | 3 +-- chorus_lib/examples/input-output.rs | 5 +---- chorus_lib/examples/loc-poly.rs | 3 +-- chorus_lib/examples/runner.rs | 5 ++--- chorus_lib/examples/tic-tac-toe.rs | 4 ++-- chorus_lib/src/core.rs | 27 +++++++++++++++++++++++---- chorus_lib/src/transport.rs | 3 +-- chorus_lib/src/transport/local.rs | 4 +--- 13 files changed, 42 insertions(+), 41 deletions(-) diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 0a84c40..5378a8b 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -9,8 +9,7 @@ To create a `Projector`, you need to provide the target location and the transpo ```rust # extern crate chorus_lib; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -# use chorus_lib::core::{ChoreographyLocation, Projector}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, Projector, LocationSet}; # let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] @@ -29,8 +28,7 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro ```rust # extern crate chorus_lib; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp, LocationSet}; # let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 504030e..56f99db 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -12,8 +12,7 @@ The `local` transport is used to execute choreographies on the same machine on d ```rust # extern crate chorus_lib; -# use chorus_lib::core::{ChoreographyLocation}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -24,13 +23,12 @@ let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); ``` To use the `local` transport, first import the `LocalTransport` struct from the `chorus_lib` crate. - + Then build the transport by using the `LocalTransport::new` associated function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. ```rust # extern crate chorus_lib; -# use chorus_lib::core::{ChoreographyLocation}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # use chorus_lib::transport::local::LocalTransportChannel; @@ -46,8 +44,7 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # extern crate chorus_lib; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; # use std::thread; -# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] @@ -116,10 +113,9 @@ let config = TransportConfig::for_target(Alice, ()) See the API documentation for more details. - ### Note on the location set of the Choreography -Note that when calling `epp_and_run` on a `Projector`, you will get a compile error if the location set of the `Choreography` is not a subset of the location set of the `Transport`. In other words, the `Transport` should have information about every `ChoreographyLocation` that `Choreography` can talk about. So this will fail: +Note that when calling `epp_and_run` on a `Projector`, you will get a compile error if the location set of the `Choreography` is not a subset of the location set of the `Transport`. In other words, the `Transport` should have information about every `ChoreographyLocation` that `Choreography` can talk about. So this will fail: ```rust, compile_fail # extern crate chorus_lib; diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index 7231c30..d22111b 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,7 +1,6 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner, LocationSet}; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -# use chorus_lib::{LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 7889aa6..61152ea 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -5,9 +5,8 @@ use std::thread; use chrono::NaiveDate; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -use chorus_lib::LocationSet; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { match title.trim() { diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 729d5af..e493af6 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -4,9 +4,8 @@ use std::collections::HashMap; use std::sync::Arc; use std::thread; -use chorus_lib::LocationSet; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector}, + core::{ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Projector}, transport::local::{LocalTransport, LocalTransportChannel}, }; use chrono::NaiveDate; diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index ba8bcfd..f11f242 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -2,9 +2,8 @@ extern crate chorus_lib; use std::thread; -use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -use chorus_lib::LocationSet; // --- Define two locations (Alice and Bob) --- diff --git a/chorus_lib/examples/input-output.rs b/chorus_lib/examples/input-output.rs index a02844b..7664a65 100644 --- a/chorus_lib/examples/input-output.rs +++ b/chorus_lib/examples/input-output.rs @@ -1,8 +1,5 @@ extern crate chorus_lib; -use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located}, - LocationSet, -}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet}; #[derive(ChoreographyLocation)] struct Alice; #[derive(ChoreographyLocation)] diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index e6b8e0b..f2a50d5 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -3,10 +3,9 @@ use std::fmt::Debug; use std::thread; use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, Located, Portable, Projector, + ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Portable, Projector, }; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; -use chorus_lib::LocationSet; #[derive(ChoreographyLocation)] struct Alice; diff --git a/chorus_lib/examples/runner.rs b/chorus_lib/examples/runner.rs index 83dd4a7..e6d39ec 100644 --- a/chorus_lib/examples/runner.rs +++ b/chorus_lib/examples/runner.rs @@ -1,7 +1,6 @@ extern crate chorus_lib; -use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Runner, Superposition}, - LocationSet, +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Runner, Superposition, }; #[derive(ChoreographyLocation)] diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 79abb03..6a09302 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -4,10 +4,10 @@ extern crate chorus_lib; use chorus_lib::transport::http::HttpTransportConfig; use chorus_lib::{ core::{ - ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, Projector, Serialize, + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, LocationSet, Projector, + Serialize, }, transport::http::HttpTransport, - LocationSet, }; use clap::Parser; diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index f2ba43e..00809d6 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -120,16 +120,35 @@ where } } -// TODO(shumbo): Export the macro under the `core` module +// To export `LocationSet` under the `core` module, we define an internal macro and export it. +// This is because Rust does not allow us to export a macro from a module without re-exporting it. +// `__ChoRus_Internal_LocationSet` is the internal macro and it is configured not to be visible in the documentation. -/// Macro to generate hlist +/// Macro to define a set of locations that a choreography is defined on. +/// +/// ``` +/// # use chorus_lib::core::{ChoreographyLocation, LocationSet}; +/// # +/// # #[derive(ChoreographyLocation)] +/// # struct Alice; +/// # #[derive(ChoreographyLocation)] +/// # struct Bob; +/// # #[derive(ChoreographyLocation)] +/// # struct Carol; +/// # +/// type L = LocationSet!(Alice, Bob, Carol); +/// ``` +#[doc(hidden)] #[macro_export] -macro_rules! LocationSet { +macro_rules! __ChoRus_Internal_LocationSet { () => { $crate::core::HNil }; ($head:ty $(,)*) => { $crate::core::HCons<$head, $crate::core::HNil> }; - ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, $crate::LocationSet!($($tail)*)> }; + ($head:ty, $($tail:tt)*) => { $crate::core::HCons<$head, $crate::core::LocationSet!($($tail)*)> }; } +#[doc(inline)] +pub use __ChoRus_Internal_LocationSet as LocationSet; + /// Marker pub struct Here; /// Marker diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 9f8209a..6d98d8c 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,8 +3,7 @@ pub mod http; pub mod local; -use crate::core::{ChoreographyLocation, HCons, HList}; -use crate::LocationSet; +use crate::core::{ChoreographyLocation, HCons, HList, LocationSet}; use std::collections::HashMap; use std::marker::PhantomData; diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 3ba58b3..6533467 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,9 +7,7 @@ use serde_json; use std::marker::PhantomData; -use crate::LocationSet; - -use crate::core::{ChoreographyLocation, HCons, HList, Portable, Transport}; +use crate::core::{ChoreographyLocation, HCons, HList, LocationSet, Portable, Transport}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; From be86cb3b1d9262f44ce04c8348b1dd03f9f5a3d9 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sat, 30 Sep 2023 09:56:55 -0700 Subject: [PATCH 53/75] hide internal structures from the documentation --- chorus_lib/src/core.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 00809d6..2c43e92 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -6,6 +6,7 @@ use std::marker::PhantomData; use serde::de::DeserializeOwned; // re-export so that users can use derive macros without importing serde +#[doc(no_inline)] pub use serde::{Deserialize, Serialize}; /// Represents a location. It can be derived using `#[derive(ChoreographyLocation)]`. @@ -92,15 +93,18 @@ where // --- HList and Helpers --- /// heterogeneous list +#[doc(hidden)] pub trait HList { /// returns fn to_string_list() -> Vec<&'static str>; } /// end of HList +#[doc(hidden)] pub struct HNil; /// An element of HList +#[doc(hidden)] pub struct HCons(Head, Tail); impl HList for HNil { @@ -150,13 +154,20 @@ macro_rules! __ChoRus_Internal_LocationSet { pub use __ChoRus_Internal_LocationSet as LocationSet; /// Marker +#[doc(hidden)] pub struct Here; /// Marker +#[doc(hidden)] pub struct There(Index); -/// Check membership +/// Check if a location is a member of a location set +/// +/// The trait is used to check if a location is a member of a location set. +/// +/// It takes two type parameters `L` and `Index`. `L` is a location set and `Index` is some type that is inferred by the compiler. +/// If a location `L1` is in `L`, then there exists a type `Index` such that `L1` implements `Member`. pub trait Member { - /// Return HList of non-member + /// A location set that is the remainder of `L` after removing the member. type Remainder: HList; } @@ -175,7 +186,12 @@ where type Remainder = HCons; } -/// Check subset +/// Check if a location set is a subset of another location set +/// +/// The trait is used to check if a location set is a subset of another location set. +/// +/// It takes two type parameters `L` and `Index`. `L` is a location set and `Index` is some type that is inferred by the compiler. +/// If a location set `M` is a subset of `L`, then there exists a type `Index` such that `M` implements `Subset`. pub trait Subset {} // Base case: HNil is a subset of any set From feb84a27b9e7b9a39704026318c8c5de42fabd86 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sat, 30 Sep 2023 10:01:14 -0700 Subject: [PATCH 54/75] fix docs --- chorus_lib/src/core.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 2c43e92..5fe334e 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -9,13 +9,25 @@ use serde::de::DeserializeOwned; #[doc(no_inline)] pub use serde::{Deserialize, Serialize}; -/// Represents a location. It can be derived using `#[derive(ChoreographyLocation)]`. +/// Represents a location. +/// +/// It can be derived using `#[derive(ChoreographyLocation)]`. +/// +/// ``` +/// # use chorus_lib::core::ChoreographyLocation; +/// # +/// #[derive(ChoreographyLocation)] +/// struct Alice; +/// ``` pub trait ChoreographyLocation: Copy { /// Returns the name of the location as a string. fn name() -> &'static str; } -/// Represents a value that can be used in a choreography. ChoRus uses [serde](https://serde.rs/) to serialize and deserialize values. +/// Represents a value that can be used in a choreography. +/// +/// ChoRus uses [serde](https://serde.rs/) to serialize and deserialize values. +/// /// It can be derived using `#[derive(Serialize, Deserialize)]` as long as all the fields satisfy the `Portable` trait. pub trait Portable: Serialize + DeserializeOwned {} impl Portable for T {} @@ -301,7 +313,7 @@ pub trait Choreography { /// /// The type parameter `L` is the location set that the transport is operating on. /// -/// The type paramter `TargetLocation` is the target `ChoreographyLocation`. +/// The type parameter `TargetLocation` is the target `ChoreographyLocation`. pub trait Transport { /// Returns a list of locations. fn locations(&self) -> Vec; From 8c02c2497496c5c07b67d8e0ef541cba0ec1446a Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sun, 1 Oct 2023 03:48:55 -0700 Subject: [PATCH 55/75] update comments in `hello.rs` --- chorus_lib/examples/hello.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index f11f242..32176d9 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -18,6 +18,9 @@ struct HelloWorldChoreography; // Implement the `Choreography` trait for `HelloWorldChoreography` impl Choreography for HelloWorldChoreography { + // Define the set of locations involved in the choreography. + // In this case, the set consists of `Alice` and `Bob` and + // the choreography can use theses locations. type L = LocationSet!(Alice, Bob); fn run(self, op: &impl ChoreoOp) { // Create a located value at Alice From 34e44dd0545002c0b039ff3d2fa9b3ef4f054190 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sun, 1 Oct 2023 04:26:48 -0700 Subject: [PATCH 56/75] LocalTransportChannelBuilder --- chorus_book/src/guide-projector.md | 10 ++-- chorus_book/src/guide-transport.md | 28 +++++---- chorus_book/src/header.txt | 4 +- chorus_lib/examples/bookseller.rs | 8 ++- chorus_lib/examples/bookseller2.rs | 7 ++- chorus_lib/examples/hello.rs | 7 ++- chorus_lib/examples/loc-poly.rs | 7 ++- chorus_lib/src/transport/local.rs | 95 ++++++++++++++++++++++++------ 8 files changed, 121 insertions(+), 45 deletions(-) diff --git a/chorus_book/src/guide-projector.md b/chorus_book/src/guide-projector.md index 5378a8b..23bd0c2 100644 --- a/chorus_book/src/guide-projector.md +++ b/chorus_book/src/guide-projector.md @@ -8,14 +8,14 @@ To create a `Projector`, you need to provide the target location and the transpo ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # use chorus_lib::core::{ChoreographyLocation, Projector, LocationSet}; -# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); -# let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; +# let transport_channel = LocalTransportChannelBuilder::new().with(Alice).with(Bob).build(); +# let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); let projector = Projector::new(Alice, alice_transport); ``` @@ -27,9 +27,9 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp, LocationSet}; -# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); +# let transport_channel = LocalTransportChannelBuilder::new().with(Alice).with(Bob).build(); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # #[derive(ChoreographyLocation)] # struct Alice; diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 56f99db..085db99 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -8,7 +8,9 @@ ChoRus provides two built-in transports: `local` and `http`. ### The Local Transport -The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. Each `local` transport is defined over `LocalTransportChannel`, which contains the set of `ChoreographyLocation` that the `local` transport operates on. You can build a `LocalTransportChannel` by importing the `LocalTransportChannel` struct from the `chorus_lib` crate. +The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. + +To use the local transport, we first need to create a `LocalTransportChannel`, which works as a channel between threads and allows them to send messages to each other. To do so, we use the `LocalTransportChannelBuilder` struct from the `chorus_lib` crate. ```rust # extern crate chorus_lib; @@ -17,32 +19,35 @@ The `local` transport is used to execute choreographies on the same machine on d # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; -use chorus_lib::transport::local::LocalTransportChannel; +use chorus_lib::transport::local::LocalTransportChannelBuilder; -let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); +let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .build(); ``` -To use the `local` transport, first import the `LocalTransport` struct from the `chorus_lib` crate. +Using the `with` method, we add locations to the channel. When we call `build`, it will create an instance of `LocalTransportChannel`. -Then build the transport by using the `LocalTransport::new` associated function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. +Then, create a transport by using the `LocalTransport::new` function, which takes a target location (explained in the [Projector section](./guide-projector.md)) and the `LocalTransportChannel`. ```rust # extern crate chorus_lib; # use chorus_lib::core::{ChoreographyLocation, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; -# use chorus_lib::transport::local::LocalTransportChannel; -# let transport_channel = LocalTransportChannel::new().with(Alice); +# use chorus_lib::transport::local::LocalTransportChannelBuilder; +# let transport_channel = LocalTransportChannelBuilder::new().with(Alice).build(); use chorus_lib::transport::local::{LocalTransport}; let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); ``` -Because of the nature of the `Local` transport, you must use the same `LocalTransportChannel` instance for all locations. You can `clone` the `LocalTransprotChannel` instance and pass it to each `Projector::new` constructor. +Because of the nature of the `Local` transport, you must use the same `LocalTransportChannel` instance for all locations. You can `clone` the `LocalTransportChannel` instance and pass it to each `Projector::new` constructor. ```rust # extern crate chorus_lib; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # use std::thread; # use chorus_lib::core::{ChoreographyLocation, ChoreoOp, Choreography, Projector, LocationSet}; # #[derive(ChoreographyLocation)] @@ -55,7 +60,10 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran # fn run(self, op: &impl ChoreoOp) { # } # } -let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); +let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .build(); let mut handles: Vec> = Vec::new(); { // create a transport for Alice diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index d22111b..f24bfe6 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,13 +1,13 @@ # extern crate chorus_lib; # use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner, LocationSet}; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # #[derive(ChoreographyLocation)] # struct Alice; # #[derive(ChoreographyLocation)] # struct Bob; # #[derive(ChoreographyLocation)] # struct Carol; -# let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob).with(Carol); +# let transport_channel = LocalTransportChannelBuilder::new().with(Alice).with(Bob).with(Carol).build(); # let alice_transport = LocalTransport::new(Alice, transport_channel.clone()); # let bob_transport = LocalTransport::new(Bob, transport_channel.clone()); # let carol_transport = LocalTransport::new(Carol, transport_channel.clone()); \ No newline at end of file diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 61152ea..2237be3 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -3,10 +3,11 @@ extern crate chorus_lib; use std::io; use std::thread; +use chorus_lib::transport::local::LocalTransportChannelBuilder; use chrono::NaiveDate; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; -use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +use chorus_lib::transport::local::LocalTransport; fn get_book(title: &str) -> Option<(i32, NaiveDate)> { match title.trim() { @@ -72,7 +73,10 @@ impl Choreography for BooksellerChoreography { } fn main() { - let transport_channel = LocalTransportChannel::new().with(Seller).with(Buyer); + let transport_channel = LocalTransportChannelBuilder::new() + .with(Seller) + .with(Buyer) + .build(); let transport_seller = LocalTransport::new(Seller, transport_channel.clone()); let transport_buyer = LocalTransport::new(Buyer, transport_channel.clone()); diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index e493af6..b62c0da 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -6,7 +6,7 @@ use std::thread; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Projector}, - transport::local::{LocalTransport, LocalTransportChannel}, + transport::local::{LocalTransport, LocalTransportChannelBuilder}, }; use chrono::NaiveDate; @@ -142,10 +142,11 @@ fn main() { i }; - let transport_channel = LocalTransportChannel::new() + let transport_channel = LocalTransportChannelBuilder::new() .with(Seller) .with(Buyer1) - .with(Buyer2); + .with(Buyer2) + .build(); let seller_projector = Arc::new(Projector::new( Seller, diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index 32176d9..ab10faa 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -3,7 +3,7 @@ extern crate chorus_lib; use std::thread; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; -use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; // --- Define two locations (Alice and Bob) --- @@ -43,7 +43,10 @@ fn main() { let mut handles: Vec> = Vec::new(); // Create a transport channel // let transport_channel = LocalTransportChannel::::new(); - let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .build(); // Run the choreography in two threads { // let transport_channel = transport_channel.clone(); diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index f2a50d5..b12b3af 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -5,7 +5,7 @@ use std::thread; use chorus_lib::core::{ ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Portable, Projector, }; -use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; #[derive(ChoreographyLocation)] struct Alice; @@ -56,10 +56,11 @@ impl Choreography> for MainChoreography { } fn main() { - let transport_channel = LocalTransportChannel::new() + let transport_channel = LocalTransportChannelBuilder::new() .with(Alice) .with(Bob) - .with(Carol); + .with(Carol) + .build(); let mut handles = vec![]; { diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 6533467..c8e8b31 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -28,25 +28,29 @@ impl Clone for LocalTransportChannel { } } -impl LocalTransportChannel { - /// Creates a new `LocalTransportChannel` instance - pub fn new() -> Self { - Self { - location_set: PhantomData, - queue_map: HashMap::new().into(), - } - } -} - impl LocalTransportChannel { - /// Adds a new location to the set of locations in the `LocalTransportChannel`. - pub fn with( - self, - _location: NewLocation, - ) -> LocalTransportChannel> { + /// Creates a new `LocalTransportChannel` instance. + /// + /// You must specify the location set of the channel. The channel can only be used by locations in the set. + /// + /// In most cases, you should use `LocalTransportChannelBuilder` instead of calling this method directly. + /// + /// # Examples + /// ``` + /// use chorus_lib::transport::local::{LocalTransportChannel}; + /// use chorus_lib::core::{LocationSet, ChoreographyLocation}; + /// + /// #[derive(ChoreographyLocation)] + /// struct Alice; + /// + /// #[derive(ChoreographyLocation)] + /// struct Bob; + /// + /// let transport_channel = LocalTransportChannel::::new(); + /// ``` + pub fn new() -> LocalTransportChannel { let mut queue_map: QueueMap = HashMap::new(); - let mut str_list = L::to_string_list(); - str_list.push(NewLocation::name()); + let str_list = L::to_string_list(); for sender in &str_list { let mut n = HashMap::new(); for receiver in &str_list { @@ -62,6 +66,58 @@ impl LocalTransportChannel { } } +/// A builder for `LocalTransportChannel`. +/// +/// Use this builder to create a `LocalTransportChannel` instance. +/// +/// # Examples +/// +/// ``` +/// use chorus_lib::core::{LocationSet, ChoreographyLocation}; +/// use chorus_lib::transport::local::{LocalTransportChannelBuilder}; +/// +/// #[derive(ChoreographyLocation)] +/// struct Alice; +/// +/// #[derive(ChoreographyLocation)] +/// struct Bob; +/// +/// let transport_channel = LocalTransportChannelBuilder::new() +/// .with(Alice) +/// .with(Bob) +/// .build(); +/// ``` +pub struct LocalTransportChannelBuilder { + location_set: PhantomData, +} + +impl LocalTransportChannelBuilder { + /// Creates a new `LocalTransportChannelBuilder` instance + pub fn new() -> Self { + Self { + location_set: PhantomData, + } + } +} + +impl LocalTransportChannelBuilder { + /// Adds a new location to the set of locations in the `LocalTransportChannel`. + pub fn with( + &self, + location: NewLocation, + ) -> LocalTransportChannelBuilder> { + _ = location; + LocalTransportChannelBuilder { + location_set: PhantomData, + } + } + + /// Builds a `LocalTransportChannel` instance. + pub fn build(&self) -> LocalTransportChannel { + LocalTransportChannel::new() + } +} + /// The local transport. /// /// This transport uses a blocking queue to allow for communication between threads. Each location must be executed in its thread. @@ -140,7 +196,10 @@ mod tests { fn test_local_transport() { let v = 42; - let transport_channel = LocalTransportChannel::new().with(Alice).with(Bob); + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .build(); let mut handles = Vec::new(); { From e75b8f176cb5ea56a80683062e995169596d6a7c Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sun, 1 Oct 2023 05:02:41 -0700 Subject: [PATCH 57/75] refactor `TransportConfig` with a separate builder struct --- chorus_book/src/guide-transport.md | 23 +++++---- chorus_lib/examples/tic-tac-toe.rs | 13 ++--- chorus_lib/src/transport.rs | 76 ++++++++++++++++++++++-------- chorus_lib/src/transport/http.rs | 51 +++++++++++++++----- 4 files changed, 115 insertions(+), 48 deletions(-) diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 085db99..82ed2ff 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -87,21 +87,23 @@ let mut handles: Vec> = Vec::new(); The `http` transport is used to execute choreographies on different machines. This is useful for executing choreographies in a distributed system. -To use the `http` transport, import the `HttpTransport` struct and the `HttpTransportConfig` type alias from the `chorus_lib` crate. +To use the `http` transport, import `HttpTransport` and `HttpTransportConfigBuilder` from the `chorus_lib` crate. ```rust # extern crate chorus_lib; -use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; +use chorus_lib::transport::http::{HttpTransport, HttpTransportConfigBuilder}; ``` -The primary constructor requires an argument of type `HttpTransportConfig`. To create an instance of this configuration, start with `HttpTransportConfig::for_target(target_location, (hostname, port))`. It will create set a projection target and the hostname and port to listen on. Then, provide information to connect to other locations by method-chaining the `.with(other_location, (hostname, port))` method. You can think of `HttpTransportConfig` as a mapping from locations to their hostnames and ports. +We need to construct a `HttpTransportConfig` using the `HttpTransportConfigBuilder`. First, we specify the target location and the hostname and port to listen on using the `for_target` method. Then, we specify the other locations and their `(hostname, port)` pairs using the `with` method. ```rust {{#include ./header.txt}} -# use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig}; -let config = HttpTransportConfig::for_target(Alice, ("localhost".to_string(), 8080)) - .with(Bob, ("localhost".to_string(), 8081)); - +# use chorus_lib::transport::http::{HttpTransport, HttpTransportConfigBuilder}; +// `Alice` listens on port 8080 on localhost +let config = HttpTransportConfigBuilder::for_target(Alice, ("localhost".to_string(), 8080)) + // Connect to `Bob` on port 8081 on localhost + .with(Bob, ("localhost".to_string(), 8081)) + .build(); let transport = HttpTransport::new(config); ``` @@ -113,10 +115,11 @@ You can also create your own transport by implementing the `Transport` trait. It ```rust {{#include ./header.txt}} -# use chorus_lib::transport::TransportConfig; -let config = TransportConfig::for_target(Alice, ()) +# use chorus_lib::transport::TransportConfigBuilder; +let config = TransportConfigBuilder::for_target(Alice, ()) .with(Bob, ("localhost".to_string(), 8081)) - .with(Carol, ("localhost".to_string(), 8082)); + .with(Carol, ("localhost".to_string(), 8082)) + .build(); ``` See the API documentation for more details. diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 6a09302..3cc4518 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -1,13 +1,12 @@ /// Choreographic tik-tak-toe game extern crate chorus_lib; -use chorus_lib::transport::http::HttpTransportConfig; use chorus_lib::{ core::{ ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Located, LocationSet, Projector, Serialize, }, - transport::http::HttpTransport, + transport::http::{HttpTransport, HttpTransportConfigBuilder}, }; use clap::Parser; @@ -295,7 +294,7 @@ fn main() { match args.player { 'X' => { - let config = HttpTransportConfig::for_target( + let config = HttpTransportConfigBuilder::for_target( PlayerX, (args.hostname.as_str().to_string(), args.port), ) @@ -305,7 +304,8 @@ fn main() { args.opponent_hostname.as_str().to_string(), args.opponent_port, ), - ); + ) + .build(); let transport = HttpTransport::new(config); let projector = Projector::new(PlayerX, transport); @@ -315,7 +315,7 @@ fn main() { }); } 'O' => { - let config = HttpTransportConfig::for_target( + let config = HttpTransportConfigBuilder::for_target( PlayerO, (args.hostname.as_str().to_string(), args.port), ) @@ -325,7 +325,8 @@ fn main() { args.opponent_hostname.as_str().to_string(), args.opponent_port, ), - ); + ) + .build(); let transport = HttpTransport::new(config); let projector = Projector::new(PlayerO, transport); diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 6d98d8c..ee7c666 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -9,44 +9,80 @@ use std::marker::PhantomData; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig -{ +pub struct TransportConfig { /// The information about locations - info: HashMap, + info: HashMap, /// The information about the target choreography - target_info: (TargetLocation, TargetInfoType), + target_info: (Target, TargetInfo), /// The struct is parametrized by the location set (`L`). location_set: PhantomData, } -impl - TransportConfig +/// A builder for `TransportConfig`. +/// +/// Use this builder to create a `TransportConfig` instance. +/// +/// # Examples +/// +/// ``` +/// use chorus_lib::core::{LocationSet, ChoreographyLocation}; +/// use chorus_lib::transport::TransportConfigBuilder; +/// +/// #[derive(ChoreographyLocation)] +/// struct Alice; +/// +/// #[derive(ChoreographyLocation)] +/// struct Bob; +/// +/// let transport_config = TransportConfigBuilder::for_target(Alice, "value_for_target".to_string()) +/// .with(Bob, "value_for_bob".to_string()) +/// .build(); +/// ``` +pub struct TransportConfigBuilder { + target: (Target, TargetInfo), + location_set: PhantomData, + info: HashMap<&'static str, Info>, +} + +impl + TransportConfigBuilder { - /// A transport for a given target. - pub fn for_target(location: TargetLocation, info: TargetInfoType) -> Self { + /// Creates a new `TransportConfigBuilder` instance for a given target. + pub fn for_target(target: Target, info: TargetInfo) -> Self { Self { - info: HashMap::new(), - target_info: (location, info), + target: (target, info), location_set: PhantomData, + info: HashMap::new(), } } } -impl - TransportConfig +impl + TransportConfigBuilder { /// Adds information about a new `ChoreographyLocation`. + /// + /// This method tells the builder that the choreography involves a new location and how to communicate with it. pub fn with( - mut self, - _location: NewLocation, - info: InfoType, - ) -> TransportConfig, InfoType, TargetLocation, TargetInfoType> -where { - self.info.insert(NewLocation::name().to_string(), info); + self, + location: NewLocation, + info: Info, + ) -> TransportConfigBuilder, Info> { + _ = location; + let mut new_info = self.info; + new_info.insert(NewLocation::name(), info); + TransportConfigBuilder { + target: self.target, + location_set: PhantomData, + info: new_info, + } + } + /// Builds a `TransportConfig` instance. + pub fn build(self) -> TransportConfig { TransportConfig { - info: self.info, - target_info: self.target_info, + info: HashMap::new(), + target_info: self.target, location_set: PhantomData, } } diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 0c20a58..bb019ea 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -12,16 +12,37 @@ use retry::{ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; -use crate::transport::TransportConfig; - use crate::{ core::{ChoreographyLocation, HList, Member, Portable, Transport}, + transport::{TransportConfig, TransportConfigBuilder}, utils::queue::BlockingQueue, }; type QueueMap = HashMap>; -/// A type alias for `TransportConfig`s used for building `HttpTransport` -pub type HttpTransportConfig = TransportConfig; + +/// Config for `HttpTransport`. +pub type HttpTransportConfig = TransportConfig; + +/// A builder for `HttpTransportConfig`. +/// +/// # Examples +/// +/// ``` +/// # use chorus_lib::core::{LocationSet, ChoreographyLocation}; +/// # use chorus_lib::transport::http::HttpTransportConfigBuilder; +/// # +/// # #[derive(ChoreographyLocation)] +/// # struct Alice; +/// # +/// # #[derive(ChoreographyLocation)] +/// # struct Bob; +/// # +/// let transport_config = HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9010)) +/// .with(Bob, ("example.com".to_string(), 80)) +/// .build(); +/// ``` +pub type HttpTransportConfigBuilder = + TransportConfigBuilder; /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; @@ -155,8 +176,10 @@ mod tests { let mut handles = Vec::new(); { - let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9010)) - .with(Bob, ("localhost".to_string(), 9011)); + let config = + HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9010)) + .with(Bob, ("localhost".to_string(), 9011)) + .build(); handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start @@ -165,8 +188,9 @@ mod tests { })); } { - let config = HttpTransportConfig::for_target(Bob, ("0.0.0.0".to_string(), 9011)) - .with(Alice, ("localhost".to_string(), 9010)); + let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0".to_string(), 9011)) + .with(Alice, ("localhost".to_string(), 9010)) + .build(); handles.push(thread::spawn(move || { let transport = HttpTransport::new(config); @@ -187,8 +211,10 @@ mod tests { let mut handles = Vec::new(); { - let config = HttpTransportConfig::for_target(Alice, ("0.0.0.0".to_string(), 9020)) - .with(Bob, ("localhost".to_string(), 9021)); + let config = + HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9020)) + .with(Bob, ("localhost".to_string(), 9021)) + .build(); handles.push(thread::spawn(move || { signal.send(()).unwrap(); @@ -197,8 +223,9 @@ mod tests { })); } { - let config = HttpTransportConfig::for_target(Bob, ("0.0.0.0".to_string(), 9021)) - .with(Alice, ("localhost".to_string(), 9020)); + let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0".to_string(), 9021)) + .with(Alice, ("localhost".to_string(), 9020)) + .build(); handles.push(thread::spawn(move || { // wait for Alice to start, which forces Alice to retry From cc6eed1b8c9cdd440bba2989784afa14cb918994 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sun, 1 Oct 2023 05:26:55 -0700 Subject: [PATCH 58/75] fix test --- chorus_lib/src/transport.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index ee7c666..f040d9c 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -41,7 +41,7 @@ pub struct TransportConfig { target: (Target, TargetInfo), location_set: PhantomData, - info: HashMap<&'static str, Info>, + info: HashMap, } impl @@ -70,7 +70,7 @@ impl ) -> TransportConfigBuilder, Info> { _ = location; let mut new_info = self.info; - new_info.insert(NewLocation::name(), info); + new_info.insert(NewLocation::name().to_string(), info); TransportConfigBuilder { target: self.target, location_set: PhantomData, @@ -81,7 +81,7 @@ impl /// Builds a `TransportConfig` instance. pub fn build(self) -> TransportConfig { TransportConfig { - info: HashMap::new(), + info: self.info, target_info: self.target, location_set: PhantomData, } From 0c419a582da7716573ca96042f1397fc09369464 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sun, 1 Oct 2023 05:39:33 -0700 Subject: [PATCH 59/75] public fields on `TransportConfig` --- chorus_lib/src/transport.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index f040d9c..1ddfc70 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -11,9 +11,9 @@ use std::marker::PhantomData; #[derive(Clone)] pub struct TransportConfig { /// The information about locations - info: HashMap, + pub info: HashMap, /// The information about the target choreography - target_info: (Target, TargetInfo), + pub target_info: (Target, TargetInfo), /// The struct is parametrized by the location set (`L`). location_set: PhantomData, } From 59bbab4790a5c2ff002b9dffb1926703c5c6414a Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Oct 2023 12:14:42 -0700 Subject: [PATCH 60/75] fix failing example --- chorus_book/src/guide-transport.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 82ed2ff..28ceee3 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -130,7 +130,7 @@ Note that when calling `epp_and_run` on a `Projector`, you will get a compile er ```rust, compile_fail # extern crate chorus_lib; -# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel}; +# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; # use chorus_lib::{LocationSet}; @@ -145,7 +145,7 @@ impl Choreography for HelloWorldChoreography { } } -let transport_channel = LocalTransportChannel::::new(); +let transport_channel = LocalTransportChannelBuilder::new().with(Alice).build(); let transport = LocalTransport::new(Alice, transport_channel.clone()); let projector = Projector::new(Alice, transport); projector.epp_and_run(HelloWorldChoreography); From de87e462e4fcd9a040c037e4e4fadb5d3e9d454c Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Oct 2023 12:24:38 -0700 Subject: [PATCH 61/75] remove unnecessary comment --- chorus_lib/examples/hello.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index ab10faa..34fe020 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -42,7 +42,6 @@ impl Choreography for HelloWorldChoreography { fn main() { let mut handles: Vec> = Vec::new(); // Create a transport channel - // let transport_channel = LocalTransportChannel::::new(); let transport_channel = LocalTransportChannelBuilder::new() .with(Alice) .with(Bob) From 1a0f0e1da56163f169f03775398da0dba254b2a7 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Oct 2023 12:25:19 -0700 Subject: [PATCH 62/75] remove more unnecessary comments --- chorus_lib/examples/hello.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/chorus_lib/examples/hello.rs b/chorus_lib/examples/hello.rs index 34fe020..f861ed2 100644 --- a/chorus_lib/examples/hello.rs +++ b/chorus_lib/examples/hello.rs @@ -48,7 +48,6 @@ fn main() { .build(); // Run the choreography in two threads { - // let transport_channel = transport_channel.clone(); let transport = LocalTransport::new(Alice, transport_channel.clone()); handles.push(thread::spawn(move || { let p = Projector::new(Alice, transport); @@ -56,7 +55,6 @@ fn main() { })); } { - // let transport_channel = transport_channel.clone(); let transport = LocalTransport::new(Bob, transport_channel.clone()); handles.push(thread::spawn(move || { let p = Projector::new(Bob, transport); From 5c907758ee60daf7f1e69278eaeb869a08c94ff8 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Oct 2023 12:27:02 -0700 Subject: [PATCH 63/75] adjust comment --- chorus_lib/src/transport/local.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index c8e8b31..7026717 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -122,7 +122,7 @@ impl LocalTransportChannelBuilder { /// /// This transport uses a blocking queue to allow for communication between threads. Each location must be executed in its thread. /// -/// Unlike network-based transports, all locations must share the same `LocalTransport` instance. The struct implements `Clone` so that it can be shared across threads. +/// All locations must share the same `LocalTransportChannel` instance. `LocalTransportChannel` implements `Clone` so that it can be shared across threads. pub struct LocalTransport { internal_locations: Vec, location_set: PhantomData, From d4f96a4ab1c168e904790462a5679c67da22385d Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Oct 2023 12:29:13 -0700 Subject: [PATCH 64/75] remove underscore for better hints --- chorus_lib/src/core.rs | 3 ++- chorus_lib/src/transport/local.rs | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 5fe334e..426ad39 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -342,7 +342,8 @@ where /// /// - `target` is the projection target of the choreography. /// - `transport` is an implementation of `Transport`. - pub fn new(_target: L1, transport: B) -> Self { + pub fn new(target: L1, transport: B) -> Self { + _ = target; Projector { target: PhantomData, transport, diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 7026717..9e7f447 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -132,7 +132,9 @@ pub struct LocalTransport { impl LocalTransport { /// Creates a new `LocalTransport` instance from a Target `ChoreographyLocation` and a `LocalTransportChannel`. - pub fn new(_target: TargetLocation, local_channel: LocalTransportChannel) -> Self { + pub fn new(target: TargetLocation, local_channel: LocalTransportChannel) -> Self { + _ = target; + let locations_list = L::to_string_list(); let mut locations_vec = Vec::new(); From 4663697e98a1887c28e685dc82378f5a565c5b31 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Oct 2023 12:40:00 -0700 Subject: [PATCH 65/75] fix import path --- chorus_book/src/guide-transport.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 28ceee3..aceab44 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -131,8 +131,7 @@ Note that when calling `epp_and_run` on a `Projector`, you will get a compile er ```rust, compile_fail # extern crate chorus_lib; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; -# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp}; -# use chorus_lib::{LocationSet}; +# use chorus_lib::core::{ChoreographyLocation, Projector, Choreography, ChoreoOp, LocationSet}; # #[derive(ChoreographyLocation)] # struct Alice; From 0de1a302fb204886f43d5107bb65f00c87119344 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Tue, 3 Oct 2023 00:11:11 -0700 Subject: [PATCH 66/75] rename HList into `LocationSet` --- chorus_lib/src/core.rs | 62 +++++++++++++++++-------------- chorus_lib/src/transport.rs | 8 ++-- chorus_lib/src/transport/http.rs | 10 ++--- chorus_lib/src/transport/local.rs | 18 ++++----- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 426ad39..5fe92ab 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -106,7 +106,7 @@ where /// heterogeneous list #[doc(hidden)] -pub trait HList { +pub trait LocationSet { /// returns fn to_string_list() -> Vec<&'static str>; } @@ -119,15 +119,15 @@ pub struct HNil; #[doc(hidden)] pub struct HCons(Head, Tail); -impl HList for HNil { +impl LocationSet for HNil { fn to_string_list() -> Vec<&'static str> { Vec::new() } } -impl HList for HCons +impl LocationSet for HCons where Head: ChoreographyLocation, - Tail: HList, + Tail: LocationSet, { fn to_string_list() -> Vec<&'static str> { let mut v = Tail::to_string_list(); @@ -180,12 +180,12 @@ pub struct There(Index); /// If a location `L1` is in `L`, then there exists a type `Index` such that `L1` implements `Member`. pub trait Member { /// A location set that is the remainder of `L` after removing the member. - type Remainder: HList; + type Remainder: LocationSet; } impl Member, Here> for Head where - Tail: HList, + Tail: LocationSet, { type Remainder = Tail; } @@ -204,13 +204,14 @@ where /// /// It takes two type parameters `L` and `Index`. `L` is a location set and `Index` is some type that is inferred by the compiler. /// If a location set `M` is a subset of `L`, then there exists a type `Index` such that `M` implements `Subset`. -pub trait Subset {} +pub trait Subset {} // Base case: HNil is a subset of any set -impl Subset for HNil {} +impl Subset for HNil {} // Recursive case -impl Subset> for HCons +impl Subset> + for HCons where Head: Member, Tail: Subset, @@ -233,7 +234,7 @@ impl Unwrapper { /// /// The trait provides methods to work with located values. An implementation of the trait is "injected" into /// a choreography at runtime and provides the actual implementation of the operators. -pub trait ChoreoOp { +pub trait ChoreoOp { /// Performs a computation at the specified location. /// /// `locally` performs a computation at a location, which are specified by `location` and `computation`, respectively. @@ -278,10 +279,10 @@ pub trait ChoreoOp { /// Calls a choreography. fn call>(&self, choreo: C) -> R where - M: HList + Subset; + M: LocationSet + Subset; /// Calls a choreography on a subset of locations. - fn colocally, Index>( + fn colocally, Index>( &self, choreo: C, ) -> R @@ -298,7 +299,7 @@ pub trait ChoreoOp { /// The trait provides a method `run` that takes an implementation of `ChoreoOp` and returns a value of type `R`. pub trait Choreography { /// Locations - type L: HList; + type L: LocationSet; /// A method that executes a choreography. /// /// The method takes an implementation of `ChoreoOp`. Inside the method, you can use the operators provided by `ChoreoOp` to define a choreography. @@ -314,7 +315,7 @@ pub trait Choreography { /// The type parameter `L` is the location set that the transport is operating on. /// /// The type parameter `TargetLocation` is the target `ChoreographyLocation`. -pub trait Transport { +pub trait Transport { /// Returns a list of locations. fn locations(&self) -> Vec; /// Sends a message from `from` to `to`. @@ -324,7 +325,7 @@ pub trait Transport { } /// Provides a method to perform end-point projection. -pub struct Projector, Index> +pub struct Projector, Index> where L1: Member, { @@ -334,7 +335,8 @@ where index: PhantomData, } -impl, Index> Projector +impl, Index> + Projector where L1: Member, { @@ -377,22 +379,28 @@ where } /// Performs end-point projection and runs a choreography. - pub fn epp_and_run<'a, V, L: HList, C: Choreography, IndexSet>( + pub fn epp_and_run<'a, V, L: LocationSet, C: Choreography, IndexSet>( &'a self, choreo: C, ) -> V where L: Subset, { - struct EppOp<'a, L: HList, L1: ChoreographyLocation, LS: HList, B: Transport> { + struct EppOp< + 'a, + L: LocationSet, + L1: ChoreographyLocation, + LS: LocationSet, + B: Transport, + > { target: PhantomData, transport: &'a B, locations: Vec, marker: PhantomData, projector_location_set: PhantomData, } - impl<'a, L: HList, T: ChoreographyLocation, LS: HList, B: Transport> ChoreoOp - for EppOp<'a, L, T, LS, B> + impl<'a, L: LocationSet, T: ChoreographyLocation, LS: LocationSet, B: Transport> + ChoreoOp for EppOp<'a, L, T, LS, B> { fn locally( &self, @@ -453,7 +461,7 @@ where fn call>(&self, choreo: C) -> R where - M: HList + Subset, + M: LocationSet + Subset, { let op: EppOp<'a, M, T, LS, B> = EppOp { target: PhantomData::, @@ -465,7 +473,7 @@ where choreo.run(&op) } - fn colocally, Index>( + fn colocally, Index>( &self, choreo: C, ) -> R { @@ -499,11 +507,11 @@ where } /// Provides a method to run a choreography without end-point projection. -pub struct Runner { +pub struct Runner { marker: PhantomData, } -impl Runner { +impl Runner { /// Constructs a runner. pub fn new() -> Self { Runner { @@ -528,7 +536,7 @@ impl Runner { /// Runs a choreography directly pub fn run<'a, V, C: Choreography>(&'a self, choreo: C) -> V { struct RunOp(PhantomData); - impl ChoreoOp for RunOp { + impl ChoreoOp for RunOp { fn locally( &self, _location: L1, @@ -569,13 +577,13 @@ impl Runner { fn call>(&self, choreo: C) -> R where - M: HList + Subset, + M: LocationSet + Subset, { let op: RunOp = RunOp(PhantomData); choreo.run(&op) } - fn colocally, Index>( + fn colocally, Index>( &self, choreo: C, ) -> R { diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 1ddfc70..418e45f 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -3,13 +3,13 @@ pub mod http; pub mod local; -use crate::core::{ChoreographyLocation, HCons, HList, LocationSet}; +use crate::core::{ChoreographyLocation, HCons, LocationSet}; use std::collections::HashMap; use std::marker::PhantomData; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig { +pub struct TransportConfig { /// The information about locations pub info: HashMap, /// The information about the target choreography @@ -38,7 +38,7 @@ pub struct TransportConfig { +pub struct TransportConfigBuilder { target: (Target, TargetInfo), location_set: PhantomData, info: HashMap, @@ -57,7 +57,7 @@ impl } } -impl +impl TransportConfigBuilder { /// Adds information about a new `ChoreographyLocation`. diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index bb019ea..4e6024d 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -13,7 +13,7 @@ use tiny_http::Server; use ureq::{Agent, AgentBuilder}; use crate::{ - core::{ChoreographyLocation, HList, Member, Portable, Transport}, + core::{ChoreographyLocation, LocationSet, Member, Portable, Transport}, transport::{TransportConfig, TransportConfigBuilder}, utils::queue::BlockingQueue, }; @@ -48,7 +48,7 @@ pub type HttpTransportConfigBuilder = const HEADER_SRC: &str = "X-CHORUS-SOURCE"; /// The HTTP transport. -pub struct HttpTransport { +pub struct HttpTransport { config: HashMap, agent: Agent, server: Arc, @@ -58,7 +58,7 @@ pub struct HttpTransport { target_location: PhantomData, } -impl HttpTransport { +impl HttpTransport { /// Creates a new `HttpTransport` instance from the configuration. pub fn new(http_config: HttpTransportConfig) -> Self where @@ -119,14 +119,14 @@ impl HttpTransport { } } -impl Drop for HttpTransport { +impl Drop for HttpTransport { fn drop(&mut self) { self.server.unblock(); self.join_handle.take().map(thread::JoinHandle::join); } } -impl Transport +impl Transport for HttpTransport { fn locations(&self) -> Vec { diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 9e7f447..9419f53 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -7,19 +7,19 @@ use serde_json; use std::marker::PhantomData; -use crate::core::{ChoreographyLocation, HCons, HList, LocationSet, Portable, Transport}; +use crate::core::{ChoreographyLocation, HCons, LocationSet, Portable, Transport}; use crate::utils::queue::BlockingQueue; type QueueMap = HashMap>>; /// A Transport channel used between multiple `Transport`s. -pub struct LocalTransportChannel { +pub struct LocalTransportChannel { /// The location set where the channel is defined on. location_set: std::marker::PhantomData, queue_map: Arc, } -impl Clone for LocalTransportChannel { +impl Clone for LocalTransportChannel { fn clone(&self) -> Self { LocalTransportChannel { location_set: PhantomData, @@ -28,7 +28,7 @@ impl Clone for LocalTransportChannel { } } -impl LocalTransportChannel { +impl LocalTransportChannel { /// Creates a new `LocalTransportChannel` instance. /// /// You must specify the location set of the channel. The channel can only be used by locations in the set. @@ -87,7 +87,7 @@ impl LocalTransportChannel { /// .with(Bob) /// .build(); /// ``` -pub struct LocalTransportChannelBuilder { +pub struct LocalTransportChannelBuilder { location_set: PhantomData, } @@ -100,7 +100,7 @@ impl LocalTransportChannelBuilder { } } -impl LocalTransportChannelBuilder { +impl LocalTransportChannelBuilder { /// Adds a new location to the set of locations in the `LocalTransportChannel`. pub fn with( &self, @@ -123,14 +123,14 @@ impl LocalTransportChannelBuilder { /// This transport uses a blocking queue to allow for communication between threads. Each location must be executed in its thread. /// /// All locations must share the same `LocalTransportChannel` instance. `LocalTransportChannel` implements `Clone` so that it can be shared across threads. -pub struct LocalTransport { +pub struct LocalTransport { internal_locations: Vec, location_set: PhantomData, local_channel: LocalTransportChannel, target_location: PhantomData, } -impl LocalTransport { +impl LocalTransport { /// Creates a new `LocalTransport` instance from a Target `ChoreographyLocation` and a `LocalTransportChannel`. pub fn new(target: TargetLocation, local_channel: LocalTransportChannel) -> Self { _ = target; @@ -151,7 +151,7 @@ impl LocalTransport { } } -impl Transport +impl Transport for LocalTransport { fn locations(&self) -> Vec { From 68ca49991818aacc4880d6e95a05755b6a9e6345 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 4 Oct 2023 00:06:03 -0700 Subject: [PATCH 67/75] improve inlay hint --- chorus_lib/src/core.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 5fe92ab..8ab9a2c 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -364,10 +364,11 @@ where /// Constructs a `Located` struct *NOT* located at the projection target. /// /// Use this method to run a choreography that takes a located value as an input. - pub fn remote(&self, _l2: L2) -> Located + pub fn remote(&self, at: L2) -> Located where L2: Member<>::Remainder, Index2>, { + _ = at; Located::remote() } From 8ff02690cebc97f70f17d86e0a10a230929c0928 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 5 Oct 2023 12:52:40 -0700 Subject: [PATCH 68/75] Add a section for LocationSet in the book --- chorus_book/src/guide-choreography.md | 2 +- chorus_book/src/guide-locations.md | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/chorus_book/src/guide-choreography.md b/chorus_book/src/guide-choreography.md index 32003c3..1480948 100644 --- a/chorus_book/src/guide-choreography.md +++ b/chorus_book/src/guide-choreography.md @@ -21,7 +21,7 @@ impl Choreography for HelloWorldChoreography { `Choreography` must implement the `run` method which defines the behavior of the system. The `run` method takes a reference to an object that implements the `ChoreoOp` trait. The `ChoreoOp` trait provides choreographic operators such as `locally` and `comm`. -Also, each `Choreography` has an associated type `L`, called it's location set; this is the set of `ChoreographyLocation`s that the `Choreography` can operate on. To build a location set, you can use the macro `LocationSet!`. +Also, each `Choreography` has an associated `LocationSet` type, `L`; this is the `LocationSet` that the `Choreography` can operate on. ## Choreographic Operators diff --git a/chorus_book/src/guide-locations.md b/chorus_book/src/guide-locations.md index 3e5c2b8..76aec76 100644 --- a/chorus_book/src/guide-locations.md +++ b/chorus_book/src/guide-locations.md @@ -29,3 +29,22 @@ The `ChoreographyLocation` trait provides the `name` method, which returns the n let name = Alice::name(); assert_eq!(name, "Alice"); ``` + +## Location Set + +Each Choreography is allowed to operate on a set of `ChoreographyLocation`, called its `LocationSet`. You can use the macro `LocationSet!` and give it a comma separated list of `ChoreographyLocation` to build a `LocationSet`. + +```rust +# extern crate chorus_lib; +# use chorus_lib::core::ChoreographyLocation; +# #[derive(ChoreographyLocation)] +# struct Alice; +# +# #[derive(ChoreographyLocation)] +# struct Bob; +use chorus_lib::core::LocationSet; + +type L = LocationSet!(Alice, Bob); +``` + +Internally, `LocationSet` is also used at other places like [Projector](./guide-projector.md) and [Transport](./guide-transport.md) to ensure that they have comprehensive information regarding the `ChoreographyLocation` values they're working with. This is crucial as it allows the system to catch potential errors during compile time instead of runtime, leading to safer code. You can check the API documentation for more details. From 8868303d6cb8fe3ab5cd2fac5b35b038f2470806 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Thu, 5 Oct 2023 13:02:42 -0700 Subject: [PATCH 69/75] Change the documentation for custom transports --- chorus_book/src/guide-transport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index aceab44..40879dd 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -111,7 +111,7 @@ In the above example, the transport will start the HTTP server on port 8080 on l ## Creating a Custom Transport -You can also create your own transport by implementing the `Transport` trait. It might be helpful first build a `TransportConfig` to have the the information that you need for each `ChoreographyLocation`, and then have a constructor that takes the `TransportConfig` and builds the `Transport` based on it. While the syntax is similar to `HttpTransportConfig`, the type of information for each `ChoreographyLocation` might diverge from the `(host_name, port)` format presented here. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. +You can also create your own transport by implementing the `Transport` trait. It might be helpful to first build a `TransportConfig` to have the the information that you need for each `ChoreographyLocation`, and then have a constructor that takes the `TransportConfig` and builds the `Transport` based on it. While the syntax is similar to `HttpTransportConfig`, which is `HttpTransportConfigBuilder::for_target(target_location, target_information)`, chained with information about other locations using the `.with(other_location, other_location_information)`, the type of information for each `ChoreographyLocation` might diverge from the `(host_name, port)` format presented in `HttpTransport`. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. ```rust {{#include ./header.txt}} From fc1b4cd9fe537305869cfc46e76f036cdb5dd828 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 6 Oct 2023 06:22:33 -0700 Subject: [PATCH 70/75] Remove unnecessary informations about LocationSet --- chorus_book/src/guide-locations.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chorus_book/src/guide-locations.md b/chorus_book/src/guide-locations.md index 76aec76..0df45cc 100644 --- a/chorus_book/src/guide-locations.md +++ b/chorus_book/src/guide-locations.md @@ -32,7 +32,8 @@ assert_eq!(name, "Alice"); ## Location Set -Each Choreography is allowed to operate on a set of `ChoreographyLocation`, called its `LocationSet`. You can use the macro `LocationSet!` and give it a comma separated list of `ChoreographyLocation` to build a `LocationSet`. +A `LocationSet` is a set of `ChoreographyLocation` values. It's primarily used to ensure type safety within the system, and you'll see its application in future sections. + ```rust # extern crate chorus_lib; @@ -46,5 +47,3 @@ use chorus_lib::core::LocationSet; type L = LocationSet!(Alice, Bob); ``` - -Internally, `LocationSet` is also used at other places like [Projector](./guide-projector.md) and [Transport](./guide-transport.md) to ensure that they have comprehensive information regarding the `ChoreographyLocation` values they're working with. This is crucial as it allows the system to catch potential errors during compile time instead of runtime, leading to safer code. You can check the API documentation for more details. From 23b6079910a7b3b4533ac8c27be3a281b159cfe7 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 6 Oct 2023 14:23:15 -0700 Subject: [PATCH 71/75] Update chorus_book/src/guide-locations.md Co-authored-by: Shun Kashiwa --- chorus_book/src/guide-locations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chorus_book/src/guide-locations.md b/chorus_book/src/guide-locations.md index 0df45cc..ff838da 100644 --- a/chorus_book/src/guide-locations.md +++ b/chorus_book/src/guide-locations.md @@ -32,7 +32,7 @@ assert_eq!(name, "Alice"); ## Location Set -A `LocationSet` is a set of `ChoreographyLocation` values. It's primarily used to ensure type safety within the system, and you'll see its application in future sections. +A `LocationSet` is a special type representing a set of `ChoreographyLocation` types. It's used to ensure type safety within the system, and you'll see its application in future sections. ```rust From 2ba79a39d9f4756e7499ab3c8603706ebbb2c1ed Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Fri, 6 Oct 2023 14:30:34 -0700 Subject: [PATCH 72/75] Add a note on how to build a LocationSet using the LocationSet macro --- chorus_book/src/guide-locations.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chorus_book/src/guide-locations.md b/chorus_book/src/guide-locations.md index ff838da..f26ffe9 100644 --- a/chorus_book/src/guide-locations.md +++ b/chorus_book/src/guide-locations.md @@ -32,8 +32,7 @@ assert_eq!(name, "Alice"); ## Location Set -A `LocationSet` is a special type representing a set of `ChoreographyLocation` types. It's used to ensure type safety within the system, and you'll see its application in future sections. - +A `LocationSet` is a special type representing a set of `ChoreographyLocation` types. It's used to ensure type safety within the system, and you'll see its application in future sections. To build a `LocationSet` type, you can use the `LocationSet` macro from the `chorus_lib` crate. ```rust # extern crate chorus_lib; From 9b57b3cdaeef96eefc7cfccd42226ef39ecc41b5 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Fri, 6 Oct 2023 15:59:05 -0700 Subject: [PATCH 73/75] bump version to 0.2.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fc39199..deedead 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["chorus_lib", "chorus_derive"] [workspace.package] -version = "0.1.3" +version = "0.2.0" edition = "2021" authors = ["Shun Kashiwa "] homepage = "https://lsd-ucsc.github.io/ChoRus/" From dbfe6f2582f3bd455866412e676beeadc1ad3b6a Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Fri, 6 Oct 2023 17:48:21 -0700 Subject: [PATCH 74/75] update chorus_derive version --- chorus_lib/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chorus_lib/Cargo.toml b/chorus_lib/Cargo.toml index 4634724..560c75c 100644 --- a/chorus_lib/Cargo.toml +++ b/chorus_lib/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["choreography"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chorus_derive = { version = "0.1.0", path = "../chorus_derive" } +chorus_derive = { version = "0.2.0", path = "../chorus_derive" } retry = "2.0.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.104" From 897104cf0db9cc3c9c744157625cd6e2a01196cd Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Fri, 6 Oct 2023 18:25:31 -0700 Subject: [PATCH 75/75] review the use of `String` and switch to `&str` when possible (#22) * review the use of `String` and switch to `&str` when possible * get rid of more String --- chorus_book/src/guide-transport.md | 10 +++--- chorus_lib/examples/tic-tac-toe.rs | 14 +++----- chorus_lib/src/core.rs | 9 +++-- chorus_lib/src/transport.rs | 35 ++++++++++++------- chorus_lib/src/transport/http.rs | 55 +++++++++++++++--------------- chorus_lib/src/transport/local.rs | 13 ++----- 6 files changed, 66 insertions(+), 70 deletions(-) diff --git a/chorus_book/src/guide-transport.md b/chorus_book/src/guide-transport.md index 40879dd..363ede8 100644 --- a/chorus_book/src/guide-transport.md +++ b/chorus_book/src/guide-transport.md @@ -100,9 +100,9 @@ We need to construct a `HttpTransportConfig` using the `HttpTransportConfigBuild {{#include ./header.txt}} # use chorus_lib::transport::http::{HttpTransport, HttpTransportConfigBuilder}; // `Alice` listens on port 8080 on localhost -let config = HttpTransportConfigBuilder::for_target(Alice, ("localhost".to_string(), 8080)) +let config = HttpTransportConfigBuilder::for_target(Alice, ("localhost", 8080)) // Connect to `Bob` on port 8081 on localhost - .with(Bob, ("localhost".to_string(), 8081)) + .with(Bob, ("localhost", 8081)) .build(); let transport = HttpTransport::new(config); ``` @@ -111,14 +111,14 @@ In the above example, the transport will start the HTTP server on port 8080 on l ## Creating a Custom Transport -You can also create your own transport by implementing the `Transport` trait. It might be helpful to first build a `TransportConfig` to have the the information that you need for each `ChoreographyLocation`, and then have a constructor that takes the `TransportConfig` and builds the `Transport` based on it. While the syntax is similar to `HttpTransportConfig`, which is `HttpTransportConfigBuilder::for_target(target_location, target_information)`, chained with information about other locations using the `.with(other_location, other_location_information)`, the type of information for each `ChoreographyLocation` might diverge from the `(host_name, port)` format presented in `HttpTransport`. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. +You can also create your own transport by implementing the `Transport` trait. It might be helpful to first build a `TransportConfig` to have the the information that you need for each `ChoreographyLocation`, and then have a constructor that takes the `TransportConfig` and builds the `Transport` based on it. While the syntax is similar to `HttpTransportConfig`, which is `HttpTransportConfigBuilder::for_target(target_location, target_information)`, chained with information about other locations using the `.with(other_location, other_location_information)`, the type of information for each `ChoreographyLocation` might diverge from the `(host_name, port)` format presented in `HttpTransport`. In some cases, the `target_information` could even have a different type than the following `other_location_information` types. But all the `other_location_information`s should have the same type. ```rust {{#include ./header.txt}} # use chorus_lib::transport::TransportConfigBuilder; let config = TransportConfigBuilder::for_target(Alice, ()) - .with(Bob, ("localhost".to_string(), 8081)) - .with(Carol, ("localhost".to_string(), 8082)) + .with(Bob, ("localhost", 8081)) + .with(Carol, ("localhost", 8082)) .build(); ``` diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 3cc4518..04f8655 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -296,14 +296,11 @@ fn main() { 'X' => { let config = HttpTransportConfigBuilder::for_target( PlayerX, - (args.hostname.as_str().to_string(), args.port), + (args.hostname.as_str(), args.port), ) .with( PlayerO, - ( - args.opponent_hostname.as_str().to_string(), - args.opponent_port, - ), + (args.opponent_hostname.as_str(), args.opponent_port), ) .build(); @@ -317,14 +314,11 @@ fn main() { 'O' => { let config = HttpTransportConfigBuilder::for_target( PlayerO, - (args.hostname.as_str().to_string(), args.port), + (args.hostname.as_str(), args.port), ) .with( PlayerX, - ( - args.opponent_hostname.as_str().to_string(), - args.opponent_port, - ), + (args.opponent_hostname.as_str(), args.opponent_port), ) .build(); diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 8ab9a2c..ed2d853 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -317,7 +317,7 @@ pub trait Choreography { /// The type parameter `TargetLocation` is the target `ChoreographyLocation`. pub trait Transport { /// Returns a list of locations. - fn locations(&self) -> Vec; + fn locations(&self) -> Vec<&'static str>; /// Sends a message from `from` to `to`. fn send(&self, from: &str, to: &str, data: &V) -> (); /// Receives a message from `from` to `at`. @@ -396,7 +396,7 @@ where > { target: PhantomData, transport: &'a B, - locations: Vec, + locations: Vec<&'static str>, marker: PhantomData, projector_location_set: PhantomData, } @@ -478,15 +478,14 @@ where &self, choreo: C, ) -> R { - let locs_vec = - Vec::from_iter(S::to_string_list().into_iter().map(|s| s.to_string())); + let locs_vec = S::to_string_list(); for location in &locs_vec { if *location == T::name().to_string() { let op = EppOp { target: PhantomData::, transport: self.transport, - locations: locs_vec.clone(), + locations: locs_vec, marker: PhantomData::, projector_location_set: PhantomData::, }; diff --git a/chorus_lib/src/transport.rs b/chorus_lib/src/transport.rs index 418e45f..090cc1a 100644 --- a/chorus_lib/src/transport.rs +++ b/chorus_lib/src/transport.rs @@ -9,13 +9,14 @@ use std::marker::PhantomData; /// A generic struct for configuration of `Transport`. #[derive(Clone)] -pub struct TransportConfig { +pub struct TransportConfig<'a, Target: ChoreographyLocation, TargetInfo, L: LocationSet, Info> { /// The information about locations - pub info: HashMap, + pub info: HashMap<&'static str, Info>, /// The information about the target choreography pub target_info: (Target, TargetInfo), /// The struct is parametrized by the location set (`L`). location_set: PhantomData, + lifetime: PhantomData<&'a ()>, } /// A builder for `TransportConfig`. @@ -38,14 +39,21 @@ pub struct TransportConfig { +pub struct TransportConfigBuilder< + 'a, + Target: ChoreographyLocation, + TargetInfo, + L: LocationSet, + Info, +> { target: (Target, TargetInfo), location_set: PhantomData, - info: HashMap, + info: HashMap<&'static str, Info>, + lifetime: PhantomData<&'a ()>, } -impl - TransportConfigBuilder +impl<'a, Target: ChoreographyLocation, TargetInfo, Info> + TransportConfigBuilder<'a, Target, TargetInfo, LocationSet!(Target), Info> { /// Creates a new `TransportConfigBuilder` instance for a given target. pub fn for_target(target: Target, info: TargetInfo) -> Self { @@ -53,37 +61,40 @@ impl target: (target, info), location_set: PhantomData, info: HashMap::new(), + lifetime: PhantomData, } } } -impl - TransportConfigBuilder +impl<'a, Target: ChoreographyLocation, TargetInfo, L: LocationSet, Info> + TransportConfigBuilder<'a, Target, TargetInfo, L, Info> { /// Adds information about a new `ChoreographyLocation`. /// /// This method tells the builder that the choreography involves a new location and how to communicate with it. - pub fn with( + pub fn with<'b, NewLocation: ChoreographyLocation>( self, location: NewLocation, info: Info, - ) -> TransportConfigBuilder, Info> { + ) -> TransportConfigBuilder<'b, Target, TargetInfo, HCons, Info> { _ = location; let mut new_info = self.info; - new_info.insert(NewLocation::name().to_string(), info); + new_info.insert(NewLocation::name(), info); TransportConfigBuilder { target: self.target, location_set: PhantomData, info: new_info, + lifetime: PhantomData, } } /// Builds a `TransportConfig` instance. - pub fn build(self) -> TransportConfig { + pub fn build<'b>(self) -> TransportConfig<'b, Target, TargetInfo, L, Info> { TransportConfig { info: self.info, target_info: self.target, location_set: PhantomData, + lifetime: PhantomData, } } } diff --git a/chorus_lib/src/transport/http.rs b/chorus_lib/src/transport/http.rs index 4e6024d..8427758 100644 --- a/chorus_lib/src/transport/http.rs +++ b/chorus_lib/src/transport/http.rs @@ -18,10 +18,11 @@ use crate::{ utils::queue::BlockingQueue, }; -type QueueMap = HashMap>; +type QueueMap = HashMap<&'static str, BlockingQueue>; /// Config for `HttpTransport`. -pub type HttpTransportConfig = TransportConfig; +pub type HttpTransportConfig<'a, L, Target> = + TransportConfig<'a, Target, (&'a str, u16), L, (&'a str, u16)>; /// A builder for `HttpTransportConfig`. /// @@ -37,19 +38,19 @@ pub type HttpTransportConfig = TransportConfig = - TransportConfigBuilder; +pub type HttpTransportConfigBuilder<'a, Target, L> = + TransportConfigBuilder<'a, Target, (&'a str, u16), L, (&'a str, u16)>; /// The header name for the source location. const HEADER_SRC: &str = "X-CHORUS-SOURCE"; /// The HTTP transport. -pub struct HttpTransport { - config: HashMap, +pub struct HttpTransport<'a, L: LocationSet, TLocation> { + config: HashMap<&'static str, (&'a str, u16)>, agent: Agent, server: Arc, join_handle: Option>, @@ -58,16 +59,16 @@ pub struct HttpTransport { target_location: PhantomData, } -impl HttpTransport { +impl<'a, L: LocationSet, TLocation: ChoreographyLocation> HttpTransport<'a, L, TLocation> { /// Creates a new `HttpTransport` instance from the configuration. - pub fn new(http_config: HttpTransportConfig) -> Self + pub fn new(http_config: HttpTransportConfig<'a, L, TLocation>) -> Self where TLocation: Member, { let queue_map: Arc = { let mut m = HashMap::new(); for loc in L::to_string_list() { - m.insert(loc.to_string(), BlockingQueue::new()); + m.insert(loc, BlockingQueue::new()); } Arc::new(m.into()) }; @@ -119,18 +120,18 @@ impl HttpTransport Drop for HttpTransport { +impl<'a, L: LocationSet, TLocation> Drop for HttpTransport<'a, L, TLocation> { fn drop(&mut self) { self.server.unblock(); self.join_handle.take().map(thread::JoinHandle::join); } } -impl Transport - for HttpTransport +impl<'a, L: LocationSet, TLocation: ChoreographyLocation> Transport + for HttpTransport<'a, L, TLocation> { - fn locations(&self) -> Vec { - Vec::from_iter(self.config.keys().map(|s| s.clone())) + fn locations(&self) -> Vec<&'static str> { + self.config.keys().cloned().collect() } fn send(&self, from: &str, to: &str, data: &V) -> () { @@ -176,10 +177,9 @@ mod tests { let mut handles = Vec::new(); { - let config = - HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9010)) - .with(Bob, ("localhost".to_string(), 9011)) - .build(); + let config = HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0", 9010)) + .with(Bob, ("localhost", 9011)) + .build(); handles.push(thread::spawn(move || { wait.recv().unwrap(); // wait for Bob to start @@ -188,8 +188,8 @@ mod tests { })); } { - let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0".to_string(), 9011)) - .with(Alice, ("localhost".to_string(), 9010)) + let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0", 9011)) + .with(Alice, ("localhost", 9010)) .build(); handles.push(thread::spawn(move || { @@ -211,10 +211,9 @@ mod tests { let mut handles = Vec::new(); { - let config = - HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0".to_string(), 9020)) - .with(Bob, ("localhost".to_string(), 9021)) - .build(); + let config = HttpTransportConfigBuilder::for_target(Alice, ("0.0.0.0", 9020)) + .with(Bob, ("localhost", 9021)) + .build(); handles.push(thread::spawn(move || { signal.send(()).unwrap(); @@ -223,8 +222,8 @@ mod tests { })); } { - let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0".to_string(), 9021)) - .with(Alice, ("localhost".to_string(), 9020)) + let config = HttpTransportConfigBuilder::for_target(Bob, ("0.0.0.0", 9021)) + .with(Alice, ("localhost", 9020)) .build(); handles.push(thread::spawn(move || { diff --git a/chorus_lib/src/transport/local.rs b/chorus_lib/src/transport/local.rs index 9419f53..22be7d1 100644 --- a/chorus_lib/src/transport/local.rs +++ b/chorus_lib/src/transport/local.rs @@ -124,7 +124,7 @@ impl LocalTransportChannelBuilder { /// /// All locations must share the same `LocalTransportChannel` instance. `LocalTransportChannel` implements `Clone` so that it can be shared across threads. pub struct LocalTransport { - internal_locations: Vec, + internal_locations: Vec<&'static str>, location_set: PhantomData, local_channel: LocalTransportChannel, target_location: PhantomData, @@ -135,15 +135,8 @@ impl LocalTransport { pub fn new(target: TargetLocation, local_channel: LocalTransportChannel) -> Self { _ = target; - let locations_list = L::to_string_list(); - - let mut locations_vec = Vec::new(); - for loc in locations_list.clone() { - locations_vec.push(loc.to_string()); - } - LocalTransport { - internal_locations: locations_vec, + internal_locations: L::to_string_list(), location_set: PhantomData, local_channel, target_location: PhantomData, @@ -154,7 +147,7 @@ impl LocalTransport { impl Transport for LocalTransport { - fn locations(&self) -> Vec { + fn locations(&self) -> Vec<&'static str> { return self.internal_locations.clone(); }