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 19 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
47 changes: 13 additions & 34 deletions chorus_book/src/guide-projector.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@ 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 std::sync::Arc;
# 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 = 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!(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 +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, projector};
# let transport = LocalTransport::from(&[Alice::name(), Bob::name()]);
# use chorus_lib::{LocationSet};
# 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,33 +49,8 @@ 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, 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);
```
102 changes: 79 additions & 23 deletions chorus_book/src/guide-transport.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,51 @@ 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.
shumbo marked this conversation as resolved.
Show resolved Hide resolved

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


```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;
# use chorus_lib::transport::local::LocalTransportChannel;
# let transport_channel = Arc::new(LocalTransportChannel::<LocationSet!(Alice)>::new());
use chorus_lib::transport::local::{LocalTransport};

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, projector};
# use chorus_lib::{LocationSet};
# #[derive(ChoreographyLocation)]
# struct Alice;
# #[derive(ChoreographyLocation)]
Expand All @@ -37,23 +63,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 = Arc::new(LocalTransportChannel::<LocationSet!(Alice, Bob)>::new());
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, Arc::clone(&transport_channel));
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, Arc::clone(&transport_channel));
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 +87,59 @@ 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 `transport_config` macro from the `chorus_lib` crate.

```rust
# extern crate chorus_lib;
use chorus_lib::transport::http::HttpTransport;
use chorus_lib::transport_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 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 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};
# use chorus_lib::transport_config;
let config = transport_config!(
Alice => ("0.0.0.0".to_string(), 9010),
Bob: ("localhost".to_string(), 9011)
);

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.


### 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 std::sync::Arc;
# 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 = 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);
```
10 changes: 7 additions & 3 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::{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 = 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));
17 changes: 11 additions & 6 deletions chorus_lib/examples/bookseller.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
extern crate chorus_lib;

use std::io;
use std::sync::Arc;
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 +74,13 @@ 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 = 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
Loading
Loading