Skip to content

Commit

Permalink
Merge pull request #14 from lsd-ucsc/transport-location-set
Browse files Browse the repository at this point in the history
Add location-set as a generic type to Transport
  • Loading branch information
shumbo authored Sep 30, 2023
2 parents f7c2c1f + 83a5212 commit 6509a2c
Show file tree
Hide file tree
Showing 13 changed files with 397 additions and 200 deletions.
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!(LocationSet!(Alice), 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!(LocationSet!(Alice), 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!(LocationSet!(Alice, Bob), 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!(LocationSet!(Alice), 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!(LocationSet!(Alice), 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
49 changes: 11 additions & 38 deletions chorus_book/src/guide-projector.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ 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;
# use chorus_lib::transport::local::LocalTransport;
# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel};
# 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_channel = LocalTransportChannel::new().with(Alice).with(Bob);
# let alice_transport = LocalTransport::new(Alice, transport_channel.clone());
# #[derive(ChoreographyLocation)]
# struct Alice;
# #[derive(ChoreographyLocation)]
# struct Bob;
#

let projector = projector!(LocationSet!(Alice, Bob), 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 +28,11 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro

```rust
# extern crate chorus_lib;
# use chorus_lib::transport::local::LocalTransport;
# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel};
# 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_channel = LocalTransportChannel::new().with(Alice).with(Bob);
# let alice_transport = LocalTransport::new(Alice, transport_channel.clone());
# #[derive(ChoreographyLocation)]
# struct Alice;
# #[derive(ChoreographyLocation)]
Expand All @@ -43,35 +43,8 @@ To execute a choreography, you need to call the `epp_and_run` method on the `Pro
# fn run(self, op: &impl ChoreoOp<Self::L>) {
# }
# }
#

# let projector = projector!(LocationSet!(Alice), Alice, transport);
# let projector = Projector::new(Alice, alice_transport);
projector.epp_and_run(HelloWorldChoreography);
```

If the choreography has a return value, the `epp_and_run` method will return the value. We will discuss the return values in the [Input and Output](./guide-input-and-output.md) section.

### Note on the 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<Self::L>) {
}
}
let projector = projector!(LocationSet!(Alice), Alice, transport);
projector.epp_and_run(HelloWorldChoreography);
```
108 changes: 82 additions & 26 deletions chorus_book/src/guide-transport.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,46 @@ 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.

To use the `local` transport, import the `LocalTransport` struct from the `chorus_lib` crate.
The `local` transport is used to execute choreographies on the same machine on different threads. This is useful for testing and prototyping. 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;
use chorus_lib::transport::local::LocalTransport;
# 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 = LocalTransportChannel::new().with(Alice).with(Bob);
```

You can construct a `LocalTransport` instance by passing a slice of locations to the `from` method.
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`.

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.
```rust
# extern crate chorus_lib;
# use chorus_lib::core::{ChoreographyLocation};
# use chorus_lib::{LocationSet};
# #[derive(ChoreographyLocation)]
# struct Alice;
# use chorus_lib::transport::local::LocalTransportChannel;
# let transport_channel = LocalTransportChannel::new().with(Alice);
use chorus_lib::transport::local::{LocalTransport};

let alice_transport = LocalTransport::new(Alice, transport_channel.clone());
```

Because of the nature of the `Local` transport, you must use the same `LocalTransportChannel` instance for all locations. You can `clone` the `LocalTransprotChannel` instance and pass it to each `Projector::new` constructor.

```rust
# extern crate chorus_lib;
# use chorus_lib::transport::local::LocalTransport;
# use chorus_lib::transport::local::{LocalTransport, LocalTransportChannel};
# 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)]
Expand All @@ -37,23 +58,21 @@ 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 = LocalTransportChannel::new().with(Alice).with(Bob);
let mut handles: Vec<thread::JoinHandle<()>> = Vec::new();
let transport = LocalTransport::from(&[Alice::name(), Bob::name()]);
{
// create a clone for Alice
let transport = transport.clone();
// create a transport for Alice
let transport = LocalTransport::new(Alice, transport_channel.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);
}));
}
{
// create another for Bob
let transport = transport.clone();
let transport = LocalTransport::new(Bob, transport_channel.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);
}));
}
Expand All @@ -63,27 +82,64 @@ let transport = LocalTransport::from(&[Alice::name(), Bob::name()]);

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 `HttpTransportConfig` type alias from the `chorus_lib` crate.

```rust
# extern crate chorus_lib;
use chorus_lib::transport::http::HttpTransport;
use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig};
```

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 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;
# 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);
# use chorus_lib::transport::http::{HttpTransport, HttpTransportConfig};
let config = HttpTransportConfig::for_target(Alice, ("localhost".to_string(), 8080))
.with(Bob, ("localhost".to_string(), 8081));

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

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, LocalTransportChannel};
# 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<Self::L>) {
}
}
let transport_channel = LocalTransportChannel::<LocationSet!(Alice)>::new();
let transport = LocalTransport::new(Alice, transport_channel.clone());
let projector = Projector::new(Alice, transport);
projector.epp_and_run(HelloWorldChoreography);
```
9 changes: 6 additions & 3 deletions chorus_book/src/header.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# 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::transport::local::{LocalTransport, LocalTransportChannel};
# 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_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());
15 changes: 9 additions & 6 deletions chorus_lib/examples/bookseller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use std::thread;

use chrono::NaiveDate;

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

fn get_book(title: &str) -> Option<(i32, NaiveDate)> {
match title.trim() {
Expand Down Expand Up @@ -73,9 +73,12 @@ 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_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());

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
30 changes: 16 additions & 14 deletions chorus_lib/examples/bookseller2.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
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},
transport::local::LocalTransport,
core::{ChoreoOp, Choreography, ChoreographyLocation, Located, Projector},
transport::local::{LocalTransport, LocalTransportChannel},
};
use chorus_lib::{projector, LocationSet};
use chrono::NaiveDate;

#[derive(ChoreographyLocation)]
Expand Down Expand Up @@ -142,21 +143,22 @@ fn main() {
i
};

let transport = LocalTransport::from(&[Seller::name(), Buyer1::name(), Buyer2::name()]);
let seller_projector = Arc::new(projector!(
LocationSet!(Seller, Buyer1, Buyer2),
let transport_channel = LocalTransportChannel::new()
.with(Seller)
.with(Buyer1)
.with(Buyer2);

let seller_projector = Arc::new(Projector::new(
Seller,
transport.clone()
LocalTransport::new(Seller, transport_channel.clone()),
));
let buyer1_projector = Arc::new(projector!(
LocationSet!(Seller, Buyer1, Buyer2),
let buyer1_projector = Arc::new(Projector::new(
Buyer1,
transport.clone()
LocalTransport::new(Buyer1, transport_channel.clone()),
));
let buyer2_projector = Arc::new(projector!(
LocationSet!(Seller, Buyer1, Buyer2),
let buyer2_projector = Arc::new(Projector::new(
Buyer2,
transport.clone()
LocalTransport::new(Buyer2, transport_channel.clone()),
));

println!("Tries to buy HoTT with one buyer");
Expand Down
Loading

0 comments on commit 6509a2c

Please sign in to comment.