Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the Transport to have a different kind of information for the target ChoreographyLocation #15

Merged
merged 11 commits into from
Sep 20, 2023
10 changes: 5 additions & 5 deletions chorus_book/src/guide-input-and-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
```

Expand Down Expand Up @@ -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<String, Alice> = projector_for_alice.local("Hello, World!".to_string());
// Instantiate the choreography with the located value
Expand All @@ -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
Expand Down Expand Up @@ -161,7 +161,7 @@ impl Choreography<String> 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());
```
Expand All @@ -183,7 +183,7 @@ impl Choreography<Located<String, Alice>> 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());
Expand Down
16 changes: 10 additions & 6 deletions chorus_book/src/guide-projector.md
Original file line number Diff line number Diff line change
Expand Up @@ -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::<LocationSet!(Alice, Bob)>::new();
# let transport_channel = Arc::new(LocalTransportChannel::<LocationSet!(Alice, Bob)>::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.
Expand All @@ -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::<LocationSet!(Alice, Bob)>::new();
# let transport_channel = Arc::new(LocalTransportChannel::<LocationSet!(Alice, Bob)>::new());
# let alice_transport = LocalTransport::new(Alice, Arc::clone(&transport_channel));
# #[derive(ChoreographyLocation)]
# struct Alice;
# #[derive(ChoreographyLocation)]
Expand All @@ -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);
```

Expand Down
69 changes: 48 additions & 21 deletions chorus_book/src/guide-transport.md
Original file line number Diff line number Diff line change
Expand Up @@ -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::<LocationSet!(Alice, Bob)>::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::<LocationSet!(Alice)>::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};
Expand All @@ -37,21 +62,19 @@ Because of the nature of the `Local` transport, you must use the same `LocalTran
# fn run(self, op: &impl ChoreoOp<Self::L>) {
# }
# }


let transport_channel = Arc::new(LocalTransportChannel::<LocationSet!(Alice, Bob)>::new());
let mut handles: Vec<thread::JoinHandle<()>> = Vec::new();
let transport = LocalTransport::<LocationSet!(Alice, Bob)>::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);
}));
}
{
// 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);
Expand All @@ -63,24 +86,26 @@ let transport = LocalTransport::<LocationSet!(Alice, Bob)>::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.
Expand All @@ -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};

Expand All @@ -111,7 +137,8 @@ impl Choreography for HelloWorldChoreography {
}
}

let transport = LocalTransport::<LocationSet!(Alice)>::new();
let transport_channel = Arc::new(LocalTransportChannel::<LocationSet!(Alice)>::new());
let transport = LocalTransport::new(Alice, Arc::clone(&transport_channel));
let projector = Projector::new(Alice, transport);
projector.epp_and_run(HelloWorldChoreography);
```
8 changes: 6 additions & 2 deletions chorus_book/src/header.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# 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;
# #[derive(ChoreographyLocation)]
# struct Bob;
# #[derive(ChoreographyLocation)]
# struct Carol;
# let transport = LocalTransport::<LocationSet!(Alice, Bob, Carol)>::new();
# let transport_channel = Arc::new(LocalTransportChannel::<LocationSet!(Alice, Bob, Carol)>::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));
13 changes: 9 additions & 4 deletions chorus_lib/examples/bookseller.rs
Original file line number Diff line number Diff line change
@@ -1,12 +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::local::{LocalTransport, LocalTransportChannel};
use chorus_lib::LocationSet;

fn get_book(title: &str) -> Option<(i32, NaiveDate)> {
Expand Down Expand Up @@ -73,9 +74,13 @@ impl Choreography for BooksellerChoreography {
}

fn main() {
let transport = LocalTransport::<LocationSet!(Seller, Buyer)>::new();
let seller_projector = Projector::new(Seller, transport.clone());
let buyer_projector = Projector::new(Buyer, transport.clone());
let transport_channel = Arc::new(LocalTransportChannel::<LocationSet!(Seller, Buyer)>::new());

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);

let mut handles: Vec<thread::JoinHandle<()>> = Vec::new();
handles.push(thread::spawn(move || {
Expand Down
36 changes: 24 additions & 12 deletions chorus_lib/examples/bookseller2.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
extern crate chorus_lib;

use std::collections::HashMap;
use std::sync::Arc;
use std::thread;
use std::{collections::HashMap, sync::Arc};

use chorus_lib::LocationSet;
use chorus_lib::{
core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector},
transport::local::LocalTransport,
transport::local::{LocalTransport, LocalTransportChannel},
};
use chrono::NaiveDate;

Expand Down Expand Up @@ -142,16 +143,16 @@ fn main() {
i
};

let transport = LocalTransport::<LocationSet!(Seller, Buyer1, Buyer2)>::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()));
let transport_channel =
Arc::new(LocalTransportChannel::<LocationSet!(Seller, Buyer1, Buyer2)>::new());

println!("Tries to buy HoTT with one buyer");
type OneBuyerBooksellerChoreography = BooksellerChoreography<OneBuyerDecider>;
let mut handles = Vec::new();
{
let seller_projector = seller_projector.clone();
let transport = LocalTransport::new(Seller, Arc::clone(&transport_channel));
let seller_projector = Projector::new(Seller, transport);

let inventory = inventory.clone();
handles.push(thread::spawn(move || {
seller_projector.epp_and_run(OneBuyerBooksellerChoreography {
Expand All @@ -162,7 +163,9 @@ fn main() {
}));
}
{
let buyer1_projector = buyer1_projector.clone();
let transport = LocalTransport::new(Buyer1, Arc::clone(&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,
Expand All @@ -176,7 +179,9 @@ fn main() {
}));
}
{
let buyer2_projector = buyer2_projector.clone();
let transport = LocalTransport::new(Buyer2, Arc::clone(&transport_channel));
let buyer2_projector = Projector::new(Buyer2, transport);

handles.push(thread::spawn(move || {
buyer2_projector.epp_and_run(OneBuyerBooksellerChoreography {
_marker: std::marker::PhantomData,
Expand All @@ -193,7 +198,9 @@ fn main() {
type TwoBuyerBooksellerChoreography = BooksellerChoreography<TwoBuyerDecider>;
let mut handles = Vec::new();
{
let seller_projector = seller_projector.clone();
let transport = LocalTransport::new(Seller, Arc::clone(&transport_channel));
let seller_projector = Projector::new(Seller, transport);

let inventory = inventory.clone();
handles.push(thread::spawn(move || {
seller_projector.epp_and_run(TwoBuyerBooksellerChoreography {
Expand All @@ -204,7 +211,9 @@ fn main() {
}));
}
{
let buyer1_projector = buyer1_projector.clone();
let transport = LocalTransport::new(Buyer1, Arc::clone(&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,
Expand All @@ -218,7 +227,10 @@ fn main() {
}));
}
{
let buyer2_projector = buyer2_projector.clone();
let transport = LocalTransport::new(Buyer2, Arc::clone(&transport_channel));

let buyer2_projector = Projector::new(Buyer2, transport);

handles.push(thread::spawn(move || {
buyer2_projector.epp_and_run(TwoBuyerBooksellerChoreography {
_marker: std::marker::PhantomData,
Expand Down
Loading