From 2a2dc414465a6966b150dea142435beb51b00a24 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 28 Feb 2024 11:50:34 +0100 Subject: [PATCH] moar fancy comments --- members/mpsc/src/main.rs | 92 ++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/members/mpsc/src/main.rs b/members/mpsc/src/main.rs index 71e323e..005e885 100644 --- a/members/mpsc/src/main.rs +++ b/members/mpsc/src/main.rs @@ -1,62 +1,108 @@ //! Example on how to use std::mpsc with 2 threads -use anyhow::Result; -use std::io::Write; -use std::str::FromStr; -use std::sync::{Arc, Barrier}; -use std::{sync::mpsc, thread}; +use std::{ + io::Write, + str::FromStr, + sync::{mpsc, Arc, Barrier}, + thread, +}; +// put anything into Results +use anyhow::Result; + +// simulate a complex datatype with special meaning being sent somewhere #[derive(Clone, Debug)] struct Message { payload: String, } +// be able to print the Message impl std::fmt::Display for Message { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Message {{{}}}", self.payload) } } +// make a new Message from a string impl std::str::FromStr for Message { type Err = std::convert::Infallible; - fn from_str(s: &str) -> std::prelude::v1::Result { + fn from_str(s: &str) -> std::result::Result { Ok(Self { payload: s.to_string(), }) } } +// printer thread function, will print any message received via the mpsc channel fn printer(receiver: mpsc::Receiver, barrier: Arc) -> Result<()> { let mut stdout = std::io::stdout(); - loop { - let msg = receiver.recv()?; + + // run as long as we can receive something (err always means all senders have been dropped) + while let Ok(msg) = receiver.recv() { println!("{msg}"); stdout.flush()?; - barrier.wait(); // wait until the main thread wants us to print + barrier.wait(); // done with printing, the main thread can continue } + Ok(()) } fn main() -> Result<()> { + // channel that can be split across threads to send values between them + // mpsc means Multi-producer, single-consumer let (sender, receiver) = mpsc::channel(); - let barrier = Arc::new(Barrier::new(2)); - let barrier_printer = barrier.clone(); - thread::spawn(|| printer(receiver, barrier_printer).expect("printer error")); - let mut msg; - let mut buf = String::new(); - let stdin = std::io::stdin(); - let mut stdout = std::io::stdout(); + + // we need to wait for the printer thread to be done before we print the prompt "> " to stdout, + // otherwise we mix up the prints. + // + // We put it into an Arc, so that we can share it across threads. + let barrier = Arc::new(Barrier::new( + 2, /* number of threads, continue when this many are waiting */ + )); + let barrier_printer = barrier.clone(); // second one for the printer, this contains a reference + // to our original barrier. (An Arc is a special kind of reference) + + // we spawn a thread and give it something to run + let _handle = thread::spawn(|| printer(receiver, barrier_printer).expect("printer error")); + + let mut msg; // we store our messages here + let mut buf = String::new(); // we put the contents of the stdin here + let stdin = std::io::stdin(); // we read user input from here + let mut stdout = std::io::stdout(); // we need this to flush explicitly + + // do this forever loop { - buf.clear(); + buf.clear(); // we want an empty buf at the start + + // print a prompt while staying in the same line print!("> "); - stdout.flush()?; - let _ = stdin.read_line(&mut buf)?; - buf = buf.replace('\n', ""); - if buf.is_empty() { + stdout.flush()?; // make sure that the stdout gets printed now instead of waiting for a + // newline (stdout flushes automatically at newlines) + + let _ = stdin.read_line(&mut buf)?; // read the user input + + // check for special inputs + if buf == "\n" { + // enter continue; + } else if buf.to_lowercase() == "exit\n" || buf.is_empty() { + // exit or ctrl-d + break; } + + buf = buf.replace('\n', ""); // we don't need the newline, just accept the user input when + // the user presses return + // if the user input was empty start anew + + // convert the user input into a message (we could also just send a String, but this + // simulates a more complex behavior) msg = Message::from_str(&buf).unwrap(); + + // send the `Message` to the printer thread sender.send(msg)?; - barrier.wait(); // wait until the printer is done printing the message, so we dont mix - // stdout prints (we use print instead of println) + + // wait until the printer is done printing the message, so we dont mix + // stdout prints (we use print instead of println) + barrier.wait(); } + Ok(()) }