rust receive messages from multiple channels at once - rust

I have a Vec<tokio::sync::broadcast::Receiver<String>> (basically a vector of channel receivers). I want to subscribe to all of them and get messages from all of them. How can I do it?

broadcast::Receiver is not a stream yet, it's just an object that has a recv() function. To combine multiple of them, you have to convert them to streams first.
Luckily, there is the tokio-streams crate for just that.
Once the receivers are converted to streams, you can use futures::stream::select_all to combine them:
use futures::stream::select_all;
use futures::StreamExt;
use tokio::time::{sleep, Duration};
use tokio_stream::wrappers::BroadcastStream;
#[tokio::main]
async fn main() {
let (sender1, receiver1) = tokio::sync::broadcast::channel(5);
let (sender2, receiver2) = tokio::sync::broadcast::channel(5);
let receivers = vec![receiver1, receiver2];
// Send on all channels
tokio::spawn(async move {
for i in 0..5 {
sleep(Duration::from_millis(50)).await;
sender1.send(format!("A{i}")).unwrap();
sleep(Duration::from_millis(50)).await;
sender2.send(format!("B{i}")).unwrap();
}
});
// Receive on all channels simultaneously
let mut fused_streams = select_all(receivers.into_iter().map(BroadcastStream::new));
while let Some(value) = fused_streams.next().await {
println!("Got value: {}", value.unwrap());
}
}
Got value: A0
Got value: B0
Got value: A1
Got value: B1
Got value: A2
Got value: B2
Got value: A3
Got value: B3
Got value: A4
Got value: B4

Related

VST3 Sampler using NIH-Plug

I'm trying to make a NIH-Plug sample player. This wouldn't be a pitch shifting plugin, but per note a different sample. (note A1 -> A1.wav). Are there any resources, about this subject. I'm also trying to cache the samples, but it's not really succeeding.
Ultimately, I'm going to use include_dir instead of include_bytes.
Here's a PolyModSynth demo, which I'm using and trying to adapt to playing samples.
Thank you.
let C1 = include_bytes!("samples/c1.wav");
let CS1 = include_bytes!("samples/c#1.wav");
fn process(
&mut self,
buffer: &mut Buffer,
_aux: &mut AuxiliaryBuffers,
context: &mut impl ProcessContext<Self>
) -> ProcessStatus {
let mut next_event = context.next_event();
for (sample_id, wav_sample) in buffer.iter_samples().zip(wav_vec) {
// Act on the next MIDI event
while let Some(event) = next_event {
if event.timing() > (sample_id as u32) {
break;
}
// handle the MIDI event as needed
// ...
next_event = context.next_event();
}
// Set the sample value in the output buffer
for sample in channel_samples {
*sample = self.sample_value[sample_id];
}
}

Reading the DS18B20 temperature sensor with this Rust function

sorry, i'm a complete newbie to Rust. I try to read the temp from the sensor mentioned above on a Raspberry Pi using the code provided on this site: https://github.com/fuchsnj/ds18b20
Actually, i want to call the function
get_temperature
but i have no idea how to declare the parameters, especially delay and one_wire_bus.
I was able to resolve all the 'namespaces' or name bindings (sorry, coming from C++) but got stuck with the parameters. Can someone give me an example how to call and use this function like this:
use ds18b20::{Resolution, Ds18b20};
use embedded_hal::blocking::delay::{DelayUs, DelayMs};
use embedded_hal::digital::v2::{OutputPin, InputPin};
use one_wire_bus::{self, OneWire, OneWireResult};
use core::fmt::Debug;
use std::io::Write;
fn main() {
let mut delay = ?????;
let mut one_wire_bus = ?????;
let mut tx = ?????; //&mut Vec::new();
let temp = get_temperature(delay, tx, one_wire_bus);
...
//do something whit the temp
...
}
This is the implementation of the function from the website
fn get_temperature<P, E>(
delay: &mut (impl DelayUs<u16> + DelayMs<u16>),
tx: &mut impl Write,
one_wire_bus: &mut OneWire<P>,
) -> OneWireResult<(), E>
where
P: OutputPin<Error=E> + InputPin<Error=E>,
E: Debug
{
// initiate a temperature measurement for all connected devices
ds18b20::start_simultaneous_temp_measurement(one_wire_bus, delay)?;
// wait until the measurement is done. This depends on the resolution you specified
// If you don't know the resolution, you can obtain it from reading the sensor data,
// or just wait the longest time, which is the 12-bit resolution (750ms)
Resolution::Bits12.delay_for_measurement_time(delay);
// iterate over all the devices, and report their temperature
let mut search_state = None;
loop {
if let Some((device_address, state)) = one_wire_bus.device_search(search_state.as_ref(), false, delay)? {
search_state = Some(state);
if device_address.family_code() != ds18b20::FAMILY_CODE {
// skip other devices
continue;
}
// You will generally create the sensor once, and save it for later
let sensor = Ds18b20::new(device_address)?;
// contains the read temperature, as well as config info such as the resolution used
let sensor_data = sensor.read_data(one_wire_bus, delay)?;
writeln!(tx, "Device at {:?} is {}°C", device_address, sensor_data.temperature);
} else {
break;
}
}
Ok(())
}

How to create threads in a for loop and get the return value from each?

I am writing a program that pings a set of targets 100 times, and stores each RTT value returned from the ping into a vector, thus giving me a set of RTT values for each target. Say I have n targets, I would like all of the pinging to be done concurrently. The rust code looks like this:
let mut sample_rtts_map = HashMap::new();
for addr in targets.to_vec() {
let mut sampleRTTvalues: Vec<f32> = vec![];
//sample_rtts_map.insert(addr, sampleRTTvalues);
thread::spawn(move || {
while sampleRTTvalues.len() < 100 {
let sampleRTT = ping(addr);
sampleRTTvalues.push(sampleRTT);
// thread::sleep(Duration::from_millis(5000));
}
});
}
The hashmap is used to tell which vector of values belongs to which target. The problem is, how do I retrieve the updated sampleRTTvalues from each thread after the thread is done executing? I would like something like:
let (name, sampleRTTvalues) = thread::spawn(...)
The name, being the name of the thread, and sampleRTTvalues being the vector. However, since I'm creating threads in a for loop, each thread is being instantiated the same way, so how I differentiate them?
Is there some better way to do this? I've looked into schedulers, future, etc., but it seems my case can just be done with simple threads.
I go the desired behavior with the following code:
use std::thread;
use std::sync::mpsc;
use std::collections::HashMap;
use rand::Rng;
use std::net::{Ipv4Addr,Ipv6Addr,IpAddr};
const RTT_ONE: IpAddr = IpAddr::V4(Ipv4Addr::new(127,0,0,1));
const RTT_TWO: IpAddr = IpAddr::V6(Ipv6Addr::new(0,0,0,0,0,0,0,1));
const RTT_THREE: IpAddr = IpAddr::V4(Ipv4Addr::new(127,0,1,1));//idk how ip adresses work, forgive if this in invalid but you get the idea
fn ping(address: IpAddr) -> f32 {
rand::thread_rng().gen_range(5.0..107.0)
}
fn main() {
let targets = [RTT_ONE,RTT_TWO,RTT_THREE];
let mut sample_rtts_map: HashMap<IpAddr,Vec<f32>> = HashMap::new();
for addr in targets.into_iter() {
let (sample_values,moved_values) = mpsc::channel();
let mut sampleRTTvalues: Vec<f32> = vec![];
thread::spawn(move || {
while sampleRTTvalues.len() < 100 {
let sampleRTT = ping(addr);
sampleRTTvalues.push(sampleRTT);
//thread::sleep(Duration::from_millis(5000));
}
});
sample_rtts_map.insert(addr,moved_values.recv().unwrap());
}
}
note that the use rand::Rng can be removed when implementing, as it is only so the example works. what this does is pass data from the spawned thread to the main thread, and in the method used it waits until the data is ready before adding it to the hash map. If this is problematic (takes a long time, etc.) then you can use try_recv instead of recv which will add an error / option type that will return a recoverable error if the value is ready when unwrapped, or return the value if it's ready
You can use a std::sync::mpsc channel to collect your data:
use std::collections::HashMap;
use std::sync::mpsc::channel;
use std::thread;
fn ping(_: &str) -> f32 { 0.0 }
fn main() {
let targets = ["a", "b"]; // just for example
let mut sample_rtts_map = HashMap::new();
let (tx, rx) = channel();
for addr in targets {
let tx = tx.clone();
thread::spawn(move || {
for _ in 0..100 {
let sampleRTT = ping(addr);
tx.send((addr, sampleRTT));
}
});
}
drop(tx);
// exit loop when all thread's tx have dropped
while let Ok((addr, sampleRTT)) = rx.recv() {
sample_rtts_map.entry(addr).or_insert(vec![]).push(sampleRTT);
}
println!("sample_rtts_map: {:?}", sample_rtts_map);
}
This will run all pinging threads simultaneously, and collect data in main thread synchronously, so that we can avoid using locks. Do not forget to drop sender in main thread after cloning to all pinging threads, or the main thread will hang forever.

Rust Tokio mpsc::channel unexpected behavior for multi-task program

In the following program I use Tokio's mpsc channels. The Sender is moved to a task named input_message and the Receiver is moved to another task named printer. Both tasks are tokio::spawn()-ed in the main function. The input_message task is to read the user's input and send it through a Channel. The printer task recv() on the channel to get the user's input and simply prints it to stdout:
use std::error::Error;
use tokio::sync::mpsc;
use std::io::{BufRead, Write};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let (tx, mut rx) = mpsc::unbounded_channel::<String>();
let printer = tokio::spawn(async move {
loop {
let res = rx.recv().await; // (11) Comment this ..
// let res = rx.try_recv(); // (12) Uncomment this ,,
if let Some(m) = res { // .. and this
// if let Ok(m) = res { // ,, and this
if m.trim() == "q".to_string() {
break;
}
println!("Received: {}", m.trim());
}
}
println!("Printer exited");
});
let input_message = tokio::spawn(async move {
let stdin = std::io::stdin();
let mut bufr = std::io::BufReader::new(stdin);
let mut buf = String::new();
loop {
// Let the printer thread print the string before asking the user's input.
std::thread::sleep(std::time::Duration::from_millis(1));
print!("Enter input: ");
std::io::stdout().flush().unwrap();
bufr.read_line(&mut buf).unwrap();
if buf.trim() == "q".to_string() {
tx.send(buf).unwrap();
break;
}
tx.send(buf).unwrap();
buf = String::new();
}
println!("InputMessage exited");
});
tokio::join!(input_message, printer);
Ok(())
}
The expected behavior of the program is to:
Ask the user a random input (q to quit)
Print that same input to stdout
Using rx.recv().await as in line 11-13 the program seems to buffer the Strings representing the user's input: the various inputs are not received by the printer task that therefore does not print the strings to stdout. Once the quit message (i.e. q) is sent, the input_message task exits and the messages seems to be flushed out of the channel and the receiver processes them all at once, and so the printer task prints all the inputs at once. Here's an example of wrong output:
Enter input: Hello
Enter input: World
Enter input: q
InputMessage exited
Received: Hello
Received: World
Printer exited
My question here is, how is it possible that the channel buffers the messages and processes them in one go only when the sending thread exits, instead of receiving them as they are sent?
What I tried to do is to use the try_recv() function as in line 12-14 and indeed it fixes the problem. The output is correctly printed, here is an example:
Enter input: Hello
Received: Hello
Enter input: World
Received: World
Enter input: q
InputMessage exited
Printer exited
In light of this, I get confused. I get the difference between the recv().await and the try_recv() functions but I think there's something more in this case that I'm ignoring that makes the latter work and the former not work. Is anybody able to shed some light and elaborate on this? Why does try_recv() work and recv().await not, and why should recv().await not work in this scenario? In terms of efficiency is looping on try_recv() bad or "bad practice" at all?
There are a few things to point out here, but first of all, you are waiting for lines on std::io::stdin() which blocks the thread until a line arrives on that stream. While the thread waiting for input, no other future can be executed on this thread, this blog post is a great resource if you want to dive deeper why you shouldn't do that.
Tokio's io module offers an async handle to stdin(), you can work with this as a quick fix, although the documentation explicitly mentions that you should spin up a dedicated (non-async) thread for interactive user input instead of using the async handle.
Swapping std::io::stdin() for tokio::io::stdin() also entails swapping out the standard library BufReader for tokio's implementation that wraps an R: AsyncRead rather than R: Read.
To prevent interleaved writes between the input task and the output task, you can use a responder channel that signals to the input task when the output has been printed. Instead of sending String over the channel, you could send a Message with these fields:
struct Message {
payload: String,
done_tx: oneshot::Sender<()>,
}
After reading an input line, send the Message over the channel to the printer task. The printer task prints the String and signals through the done_tx that the input task can print the input prompt and wait for a new line.
Putting all that together with some other changes like a while loop to wait for messages, you'd end up with something like this:
use std::error::Error;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
use tokio::sync::{mpsc, oneshot};
#[derive(Debug)]
struct Message {
done_tx: oneshot::Sender<()>,
message: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let (tx, mut rx) = mpsc::unbounded_channel::<Message>();
let printer = tokio::spawn(async move {
while let Some(Message {
message: m,
done_tx,
}) = rx.recv().await
{
if m.trim() == "q".to_string() {
break;
}
println!("Received: {}", m.trim());
done_tx.send(()).unwrap();
}
println!("Printer exited");
});
let input_message = tokio::spawn(async move {
let stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
let mut bufr = tokio::io::BufReader::new(stdin);
let mut buf = String::new();
loop {
// Let the printer thread print the string before asking the user's input.
stdout.write(b"Enter input: ").await.unwrap();
stdout.flush().await.unwrap();
bufr.read_line(&mut buf).await.unwrap();
let end = buf.trim() == "q";
let (done_tx, done) = oneshot::channel();
let message = Message {
message: buf,
done_tx,
};
tx.send(message).unwrap();
if end {
break;
}
done.await.unwrap();
buf = String::new();
}
println!("InputMessage exited");
});
tokio::join!(input_message, printer);
Ok(())
}

Closing a Channel, like in Go

Does Rust have a way of "closing" a channel, similar to what is available in Go?
The idea is to iterate over the channel (receive continually) until the channel indicates that it will not produce any more values.
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;
fn main() {
let data = Arc::new(Mutex::new(0u32));
let (tx, rx) = mpsc::channel::<u32>();
{
let (data, tx) = (data.clone(), tx.clone());
thread::spawn(move || {
for _ in 0..10 {
let mut data = data.lock().unwrap();
*data += 1;
tx.send(*data).unwrap();
}
// *** How could I close the channel here, to signal the work is done?
});
}
// *** How can I detect a closed channel here? Pattern matching?
for _ in 0..10 {
let x = rx.recv().unwrap();
println!("{}", x);
}
}
The channel is closed when all Senders have dropped. In your code you clone and give one each to each thread, these drop as they should when the threads end. The last sender is in the main thread, and you should drop it as soon as all the threads have been spawned: drop(tx).
Finally, the easiest way to receive is this, after the drop(tx).
for elt in rx {
/* */
}
This loop ends when the channel is closed.

Resources