moar fancy comments
cargo devel CI / cargo CI (push) Successful in 1m52s Details

This commit is contained in:
Christoph J. Scherr 2024-02-28 11:50:34 +01:00
parent 5b594122ba
commit 2a2dc41446
Signed by: cscherrNT
GPG Key ID: 8E2B45BC51A27EA7
1 changed files with 69 additions and 23 deletions

View File

@ -1,62 +1,108 @@
//! Example on how to use std::mpsc with 2 threads //! Example on how to use std::mpsc with 2 threads
use anyhow::Result; use std::{
use std::io::Write; io::Write,
use std::str::FromStr; str::FromStr,
use std::sync::{Arc, Barrier}; sync::{mpsc, Arc, Barrier},
use std::{sync::mpsc, thread}; thread,
};
// put anything into Results
use anyhow::Result;
// simulate a complex datatype with special meaning being sent somewhere
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Message { struct Message {
payload: String, payload: String,
} }
// be able to print the Message
impl std::fmt::Display for Message { impl std::fmt::Display for Message {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Message {{{}}}", self.payload) write!(f, "Message {{{}}}", self.payload)
} }
} }
// make a new Message from a string
impl std::str::FromStr for Message { impl std::str::FromStr for Message {
type Err = std::convert::Infallible; type Err = std::convert::Infallible;
fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> { fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self { Ok(Self {
payload: s.to_string(), payload: s.to_string(),
}) })
} }
} }
// printer thread function, will print any message received via the mpsc channel
fn printer(receiver: mpsc::Receiver<Message>, barrier: Arc<Barrier>) -> Result<()> { fn printer(receiver: mpsc::Receiver<Message>, barrier: Arc<Barrier>) -> Result<()> {
let mut stdout = std::io::stdout(); 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}"); println!("{msg}");
stdout.flush()?; 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<()> { 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 (sender, receiver) = mpsc::channel();
let barrier = Arc::new(Barrier::new(2));
let barrier_printer = barrier.clone(); // we need to wait for the printer thread to be done before we print the prompt "> " to stdout,
thread::spawn(|| printer(receiver, barrier_printer).expect("printer error")); // otherwise we mix up the prints.
let mut msg; //
let mut buf = String::new(); // We put it into an Arc, so that we can share it across threads.
let stdin = std::io::stdin(); let barrier = Arc::new(Barrier::new(
let mut stdout = std::io::stdout(); 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 { loop {
buf.clear(); buf.clear(); // we want an empty buf at the start
// print a prompt while staying in the same line
print!("> "); print!("> ");
stdout.flush()?; stdout.flush()?; // make sure that the stdout gets printed now instead of waiting for a
let _ = stdin.read_line(&mut buf)?; // newline (stdout flushes automatically at newlines)
buf = buf.replace('\n', "");
if buf.is_empty() { let _ = stdin.read_line(&mut buf)?; // read the user input
// check for special inputs
if buf == "\n" {
// enter
continue; 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(); msg = Message::from_str(&buf).unwrap();
// send the `Message` to the printer thread
sender.send(msg)?; sender.send(msg)?;
barrier.wait(); // wait until the printer is done printing the message, so we dont mix
// wait until the printer is done printing the message, so we dont mix
// stdout prints (we use print instead of println) // stdout prints (we use print instead of println)
barrier.wait();
} }
Ok(())
} }