From 10b3051a168cb49d8225738800a7bcffcfc83d3e Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Wed, 20 Nov 2019 17:55:30 +0100 Subject: [PATCH 1/9] tutorial/receiving_messages: fix future output type bound --- docs/src/tutorial/receiving_messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 213589c07..142d6e464 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -130,7 +130,7 @@ So let's use a helper function for this: # }; fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> where - F: Future> + Send + 'static, + F: Future> + Send + 'static, { task::spawn(async move { if let Err(e) = fut.await { From 9be6b52f117de2a0018d05dad6ee2ad95f6f392b Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Wed, 20 Nov 2019 18:47:50 +0100 Subject: [PATCH 2/9] tutorial/receiving_messages: remove unneeded message trimming Trimming was done twice on messages, so one of the two instances can be removed. I personally think removing the first instance, in which we are splitting names from messages makes the code more readable than removing the second instance, but other examples further in the tutorial show the second instance removed. --- docs/src/tutorial/receiving_messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 142d6e464..4ea99650b 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -46,7 +46,7 @@ async fn connection_loop(stream: TcpStream) -> Result<()> { Some(idx) => (&line[..idx], line[idx + 1 ..].trim()), }; let dest: Vec = dest.split(',').map(|name| name.trim().to_string()).collect(); - let msg: String = msg.trim().to_string(); + let msg: String = msg.to_string(); } Ok(()) } From a7b5c8a0818d98d56d60121c28e4b1201f78afa8 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Wed, 20 Nov 2019 18:59:39 +0100 Subject: [PATCH 3/9] tutorial/receiving_messages: declare use of TcpStream and io::BufReader Readers couldn't see the `use` lines corresponding to these two structures. --- docs/src/tutorial/receiving_messages.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 4ea99650b..4f705294e 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -10,14 +10,18 @@ We need to: ```rust,edition2018 # extern crate async_std; # use async_std::{ -# io::BufReader, -# net::{TcpListener, TcpStream, ToSocketAddrs}, +# net::{TcpListener, ToSocketAddrs}, # prelude::*, # task, # }; # # type Result = std::result::Result>; # +use async_std::{ + io::BufReader, + net::TcpStream, +}; + async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let mut incoming = listener.incoming(); From a33eb1aae4c0107433fb3ce75b2b96d96e50479b Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Wed, 20 Nov 2019 19:21:54 +0100 Subject: [PATCH 4/9] tutorial/connecting_readers_and_writers: typos and grammar fixes --- docs/src/tutorial/connecting_readers_and_writers.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index fcc42b63d..921cf90ca 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -2,12 +2,12 @@ ## Connecting Readers and Writers So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`? -We should somehow maintain an `peers: HashMap>` map which allows a client to find destination channels. +We should somehow maintain a `peers: HashMap>` map which allows a client to find destination channels. However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message. One trick to make reasoning about state simpler comes from the actor model. -We can create a dedicated broker tasks which owns the `peers` map and communicates with other tasks by channels. -By hiding `peers` inside such an "actor" task, we remove the need for mutxes and also make serialization point explicit. +We can create a dedicated broker task which owns the `peers` map and communicates with other tasks using channels. +By hiding `peers` inside such an "actor" task, we remove the need for mutexes and also make the serialization point explicit. The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue. ```rust,edition2018 @@ -92,9 +92,9 @@ async fn broker_loop(mut events: Receiver) -> Result<()> { } ``` -1. Broker should handle two types of events: a message or an arrival of a new peer. -2. Internal state of the broker is a `HashMap`. +1. The broker task should handle two types of events: a message or an arrival of a new peer. +2. The internal state of the broker is a `HashMap`. Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers 3. To handle a message, we send it over a channel to each destination -4. To handle new peer, we first register it in the peer's map ... +4. To handle a new peer, we first register it in the peer's map ... 5. ... and then spawn a dedicated task to actually write the messages to the socket. From 1fefc059ee3511de5bb7bee29829c4ccb1843639 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Wed, 20 Nov 2019 19:22:14 +0100 Subject: [PATCH 5/9] tutorial/all_together: remove unneeded use async_std::io --- docs/src/tutorial/all_together.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 789283e66..ead1d1222 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -6,7 +6,7 @@ At this point, we only need to start the broker to get a fully-functioning (in t # extern crate async_std; # extern crate futures; use async_std::{ - io::{self, BufReader}, + io::BufReader, net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, From ab8ef9563145ad85090951db0307ce4b9f1b8259 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Thu, 21 Nov 2019 16:01:48 +0100 Subject: [PATCH 6/9] tutorial: use SinkExt consistently from futures::sink::SinkExt --- docs/src/tutorial/all_together.md | 2 +- docs/src/tutorial/clean_shutdown.md | 4 ++-- docs/src/tutorial/handling_disconnection.md | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index ead1d1222..8bb01e943 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -12,7 +12,7 @@ use async_std::{ task, }; use futures::channel::mpsc; -use futures::SinkExt; +use futures::sink::SinkExt; use std::{ collections::hash_map::{HashMap, Entry}, sync::Arc, diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 5dcc7f263..bd112c934 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -30,7 +30,7 @@ Let's add waiting to the server: # task, # }; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, @@ -163,7 +163,7 @@ And to the broker: # task, # }; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index acb744b09..8a9940595 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -22,7 +22,7 @@ First, let's add a shutdown channel to the `connection_loop`: # extern crate futures; # use async_std::net::TcpStream; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; @@ -128,7 +128,8 @@ use async_std::{ task, }; use futures::channel::mpsc; -use futures::{select, FutureExt, SinkExt}; +use futures::sink::SinkExt; +use futures::{select, FutureExt}; use std::{ collections::hash_map::{Entry, HashMap}, future::Future, From f2526f99ea2af6ed4e794d61d6b7de789551deed Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Thu, 21 Nov 2019 16:04:21 +0100 Subject: [PATCH 7/9] tutorial/handling_disconnection: hide mpsc use clause and remove empty lines The empty lines translate to the output making it look weird. --- docs/src/tutorial/handling_disconnection.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 8a9940595..62b5f7514 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -71,14 +71,12 @@ We use the `select` macro for this purpose: # extern crate async_std; # extern crate futures; # use async_std::{net::TcpStream, prelude::*}; -use futures::channel::mpsc; +# use futures::channel::mpsc; use futures::{select, FutureExt}; # use std::sync::Arc; - # type Receiver = mpsc::UnboundedReceiver; # type Result = std::result::Result>; # type Sender = mpsc::UnboundedSender; - # #[derive(Debug)] # enum Void {} // 1 From f6e5fd4c5d63d9877db404a74152b15e928dda73 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Thu, 21 Nov 2019 16:05:15 +0100 Subject: [PATCH 8/9] tutorial/handling_disconnection: fix typos --- docs/src/tutorial/handling_disconnection.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 62b5f7514..87a6ac660 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -60,8 +60,8 @@ async fn connection_loop(mut broker: Sender, stream: Arc) -> R } ``` -1. To enforce that no messages are send along the shutdown channel, we use an uninhabited type. -2. We pass the shutdown channel to the writer task +1. To enforce that no messages are sent along the shutdown channel, we use an uninhabited type. +2. We pass the shutdown channel to the writer task. 3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped. In the `connection_writer_loop`, we now need to choose between shutdown and message channels. @@ -110,7 +110,7 @@ async fn connection_writer_loop( Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel. To not lose these messages completely, we'll return the messages channel back to the broker. -This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infailable. +This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infallible. ## Final Code From e4af038a0e17bd7fa62cf47a678b32667dc17f25 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Thu, 21 Nov 2019 16:07:08 +0100 Subject: [PATCH 9/9] tutorial/handling_disconnection: use ? in broker_handle.await We were happy to return an Err variant from the broker_handle before and nothing has changed in this regard, so bubbling it up to run(). --- docs/src/tutorial/handling_disconnection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 87a6ac660..9db9abd25 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -157,7 +157,7 @@ async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { spawn_and_log_error(connection_loop(broker_sender.clone(), stream)); } drop(broker_sender); - broker_handle.await; + broker_handle.await?; Ok(()) }