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

Add location-set as a generic type to Transport #14

Merged
merged 26 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2bd32d5
Add location-set as a generic type to Transport
ihaveint Sep 18, 2023
24ad199
Change the book examples. The text itself still needs to be updated
ihaveint Sep 18, 2023
3fee760
Update the book
ihaveint Sep 18, 2023
140e431
Refactor HttpConfig
ihaveint Sep 18, 2023
f992cf0
Fix import for PhantomData
ihaveint Sep 18, 2023
c4feb9c
Simplify User-API for building TransportConfig
ihaveint Sep 19, 2023
642b971
WIP: program doesn't terminate
ihaveint Sep 19, 2023
e486a7d
WIP: the library tests work
ihaveint Sep 19, 2023
5df2e94
Simplify things a bit
ihaveint Sep 19, 2023
d7e593c
Remove redundant target-location argument
ihaveint Sep 19, 2023
d835717
Fix some warnings and remove an extra layer of Arc
ihaveint Sep 19, 2023
65096dd
Use Equal trait instead of assuming equality of the location sets
ihaveint Sep 19, 2023
9db4537
Update examples
ihaveint Sep 19, 2023
1f42d17
Remove the TransportChannel from HttpTransport
ihaveint Sep 19, 2023
1739b1b
Move TransportChannel to LocalTransport and rename it to LocalTranspo…
ihaveint Sep 19, 2023
07f5021
Update examples and the book
ihaveint Sep 19, 2023
5bf932a
Merge pull request #15 from lsd-ucsc/target-specific-info
ihaveint Sep 20, 2023
ab005b2
Use std::marker::PhantomData instead of core::marker::PhantomData
ihaveint Sep 20, 2023
9c99e5c
Change some imports
ihaveint Sep 20, 2023
0b91d60
Change the API for building a TransportConfig; now we use builder pat…
ihaveint Sep 21, 2023
62f4a59
Change the examples and the book to reflect the new API
ihaveint Sep 21, 2023
b5dfba0
Remove a bunch of unnecessary Arcs
ihaveint Sep 21, 2023
2b379cd
Update the guide for Transport
ihaveint Sep 21, 2023
2597d37
Remove some empty spaces in the book
ihaveint Sep 21, 2023
f0b8461
Remove redundant comments
ihaveint Sep 21, 2023
83a5212
Change the API so that we have a builder pattern for LocalTransportCh…
ihaveint Sep 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence seems redundant.

Copy link
Collaborator Author

@ihaveint ihaveint Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, we have this sentence in the current version of the book: "To use the local transport, import the LocalTransport struct from the chorus_lib crate."
In this commit, it's just in a different paragraph. But if you do think that it shouldn't have been there in the first place, sure! I'll delete it (But let me know if you want it to be gone completely or you want me to put it before the snippet)


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