From 2bd32d53b40d400045143a88de7346f84327d214 Mon Sep 17 00:00:00 2001 From: Soroush Zare Date: Mon, 18 Sep 2023 04:08:16 -0700 Subject: [PATCH 01/25] 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 02/25] 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 03/25] 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 04/25] 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 05/25] 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 06/25] 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 07/25] 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 08/25] 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 09/25] 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 10/25] 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 11/25] 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 12/25] 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 13/25] 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 14/25] 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 15/25] 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 16/25] 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 17/25] 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 18/25] 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 19/25] 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 20/25] 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 21/25] 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 22/25] 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 23/25] 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 24/25] 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 25/25] 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(); {