How to let a mutex timeout? - multithreading

I want to let my modem-AT-command-writing-thread only write to the modem's /dev/ttyUSB3 when the modem-AT-command-reading-thread has seen an "OK" or an "ERROR".
This initially sounds like a job for a Mutex<()>, but I have an additional requirement: If the modem-AT-command-reading-thread does not see an "OK" or "ERROR" within three seconds, the writing thread should just get on with sending the next AT command. i.e. If the reading thread gets nothing, the writing thread should still send one of its AT commands every three seconds. (Modems' AT command interfaces are often not nicely behaved.)
At the moment, I have a workaround using mpsc::channel:
Set-up:
let (sender, receiver) = channel::<()>();
modem-AT-command-reading-thread:
if line.starts_with("OK") || line.contains("ERROR") {
debug!("Sending go-ahead to writing_thread.");
sender.send(()).unwrap();
}
modem-AT-command-writing-thread:
/* This receive is just a way of blocking until the modem is ready. */
match receiver.recv_timeout(Duration::from_secs(3)) {
Ok(_) => {
debug!("Received go-ahead from reading thread.");
/*
* Empty the channel, in case the modem was too effusive. We don't want
* to "bank" earlier OK/ERRORs to allow multiple AT commands to be sent in
* quick succession.
*/
while let Ok(_) = receiver.try_recv() {}
}
Err(err) => match err {
RecvTimeoutError::Timeout => {
debug!("Timed-out waiting for go-ahead from reading thread.");
}
RecvTimeoutError::Disconnected => break 'outer
},
}
I cannot find a Mutex::lock_with_timeout().
How can I implement this properly, using a Mutex<()> or similar?

You can use parking_lot's Mutex, it has try_lock_for().

Related

How to cheaply send a delay message?

My requirement is very simple, which is a very reasonable requirement in many programs. It is to send a specified message to my Channel after a specified time.
I've checked tokio for topics related to delay, interval or timeout, but none of them seem that straightforward to implement.
What I've come up with now is to spawn an asynchronous task, then wait or sleep for a certain amount of time, and finally send the message.
But, obviously, spawning an asynchronous task is a relatively heavy operation. Is there a better solution?
async fn my_handler(sender: mpsc::Sender<i32>, dur: Duration) {
tokio::spawn(async {
time::sleep(dur).await;
sender.send(0).await;
}
}
You could try adding a second channel and a continuously running task that buffers messages until the time they are to be received. Implementing this is more involved than it sounds, I hope I'm handling cancellations right here:
fn make_timed_channel<T: Ord + Send + Sync + 'static>() -> (Sender<(Instant, T)>, Receiver<T>) {
// Ord is an unnecessary requirement arising from me stuffing both the Instant and the T into the Binary heap
// You could drop this requirement by using the priority_queue crate instead
let (sender1, receiver1) = mpsc::channel::<(Instant, T)>(42);
let (sender2, receiver2) = mpsc::channel::<T>(42);
let mut receiver1 = Some(receiver1);
tokio::spawn(async move {
let mut buf = std::collections::BinaryHeap::<Reverse<(Instant, T)>>::new();
loop {
// Pretend we're a bounded channel or exit if the upstream closed
if buf.len() >= 42 || receiver1.is_none() {
match buf.pop() {
Some(Reverse((time, element))) => {
sleep_until(time).await;
if sender2.send(element).await.is_err() {
break;
}
}
None => break,
}
}
// We have some deadline to send a message at
else if let Some(Reverse((then, _))) = buf.peek() {
if let Ok(recv) = timeout_at(*then, receiver1.as_mut().unwrap().recv()).await {
match recv {
Some(recv) => buf.push(Reverse(recv)),
None => receiver1 = None,
}
} else {
if sender2.send(buf.pop().unwrap().0 .1).await.is_err() {
break;
}
}
}
// We're empty, wait around
else {
match receiver1.as_mut().unwrap().recv().await {
Some(recv) => buf.push(Reverse(recv)),
None => receiver1 = None,
}
}
}
});
(sender1, receiver2)
}
Playground
Whether this is more efficient than spawning tasks, you'd have to benchmark. (I doubt it. Tokio iirc has some much fancier solution than a BinaryHeap for waiting for waking up at the next timeout, e.g.)
One optimization you could make if you don't need a Receiver<T> but just something that .poll().await can be called on: You could drop the second channel and maintain the BinaryHeap inside a custom receiver.

How to understand FusedFuture

Recently, I am reading source code in the repo parity-bridges-common. There are some rs files full of incomprehensibly asynchronous syntax that I am not familiar with. Especially about FusedFuture. Example file:
sync_loop.rs
message_loop.rs
According to the futures::future::fuse, what I understand is that we can transfer a Future into FusedFuture and then poll it, again and again, for many times. Besides this, I find a function named terminated() in FuseFurture and example below.
Creates a new Fuse-wrapped future which is already terminated.
This can be useful in combination with looping and the select! macro, which bypasses terminated futures.
let (sender, mut stream) = mpsc::unbounded();
// Send a few messages into the stream
sender.unbounded_send(()).unwrap();
sender.unbounded_send(()).unwrap();
drop(sender);
// Use `Fuse::terminated()` to create an already-terminated future
// which may be instantiated later.
// bear: We must terminated() here in order to use select!?
let foo_printer = Fuse::terminated();
pin_mut!(foo_printer);
loop {
select! {
_ = foo_printer => {},
() = stream.select_next_some() => {
if !foo_printer.is_terminated() {
println!("Foo is already being printed!");
} else {
// bear: here we reset the foo_printer pointed value?
foo_printer.set(async {
// do some other async operations
println!("Printing foo from `foo_printer` future");
}.fuse());
}
},
complete => break, // `foo_printer` is terminated and the stream is done
}
}
I cannot understand when to use this function and how to combine it with select!. Can anyone help me understand it more easily? Or are there better docs or examples about this usage?
Some posts i found, maybe useful:
Why doesn’t tokio::select! require FusedFuture?
What is the difference between futures::select! and tokio::select?

Can I miss a value by calling select on two async receivers?

Is it possible, if a task sends to a and an other (at the same time) sends to b, that tokio::select! on a and b drops one of the value by cancelling the remaining future? Or is it guaranteed to be received at the next loop iteration?
use tokio::sync::mpsc::Receiver;
async fn foo(mut a: Receiver<()>, mut b: Receiver<()>) {
loop {
tokio::select!{
_ = a.recv() => {
println!("A!");
}
_ = b.recv() => {
println!("B!");
}
}
}
}
My mind can't get around what is really happening behind the async magic in that case.
It doesn't appear to be guaranteed in the documentation anywhere, but is likely to work for reading directly from a channel because of the way rusts poll based architecture works. A select is equivalent to polling each of futures in a random order, until one of them is ready, or if none are, then waiting until a waker is signaled and then repeating the process. A message is only removed from the channel when returned by a successful poll. A successful poll stops the select, so the rest of the channels will not be touched. Thus they will be polled the next time the loop occurs and then return the message.
However, this is a dangerous approach, because if the receiver is replaced with something that returns a future that does anything more complex than a direct read, where it could potentially suspend after the read, then you could lose messages when that happens. As such it should probably be treated as if it wouldn't work. A safer approach would be to store the futures in mutable variables that you update when they fire:
use tokio::sync::mpsc::Receiver;
async fn foo(mut a: Receiver<()>, mut b: Receiver<()>) {
let mut a_fut = a.recv();
let mut b_fut = b.recv();
loop {
tokio::select!{
_ = a_fut => {
println!("A!");
a_fut = a.recv();
}
_ = b_fut => {
println!("B!");
b_fut = b.recv();
}
}
}
}

Is it possible to force resume a sleeping thread?

Is it possible to force resume a sleeping thread which has been paused? For example, by calling sleep:
std::thread::sleep(std::time::Duration::from_secs(60 * 20));
I know that I can communicate between threads using std::sync::mpsc but if the thread is asleep, this does not force it to wake up before the time indicated.
I have thought that using std::sync::mpsc and maybe
Builder and .name associated with the thread, but I do not know how to get the thread to wake up.
If you want to be woken up by an event, thread::sleep() is not the correct function to use, as it's not supposed to be stopped.
There are other methods of waiting while being able to be woken up by an event (this is usually called blocking). Probably the easiest way is to use a channel together with Receiver::recv_timeout(). Often it's also sufficient to send () through the channel. That way we just communicate a signal, but don't send actual data.
If you don't want to wake up after a specific timeout, but only when a signal arrives, just use Receiver::recv().
Example with timeout:
use std::thread;
use std::sync::mpsc::{self, RecvTimeoutError};
use std::time::Duration;
use std::io;
fn main() {
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
loop {
match receiver.recv_timeout(Duration::from_secs(2)) {
Err(RecvTimeoutError::Timeout) => {
println!("Still waiting... I'm bored!");
// we'll try later...
}
Err(RecvTimeoutError::Disconnected) => {
// no point in waiting anymore :'(
break;
}
Ok(_) => {
println!("Finally got a signal! ♥♥♥");
// doing work now...
}
}
}
});
loop {
let mut s = String::new();
io::stdin().read_line(&mut s).expect("reading from stdin failed");
if s.trim() == "start" {
sender.send(()).unwrap();
}
}
}
Here, the second thread is woken up at least every two seconds (the timeout), but also earlier once something was sent through the channel.
park_timeout allows timed sleeps with wakeups from unpark, but it can also wake up early.
See std::thread module documentation

How would you stream output from a Process in Rust?

This question refers to Rust as of October 2014.
If you are using Rust 1.0 or above, you best look elsewhere for a solution.
I have a long running Rust process that generates log values, which I'm running using Process.
It looks at though I might be able to periodically "check on" the running process using set_timeout() and wait() and do something kind of high level loop like:
let mut child = match Command::new("thing").arg("...").spawn() {
Ok(child) => child,
Err(e) => fail!("failed to execute child: {}", e),
};
loop {
child.set_timeout(Some(100));
match child.wait() {
// ??? Something goes here
}
}
The things I'm not 100% on are; how do I tell the difference between a timeout error and a process-return error from wait(), and how to a use the PipeStream to "read as much as you can without blocking from the stream" every interval to push out.
Is this the best approach? Should I start a task to monitor stdout and stderr instead?
For distinguishing the errors from the process from the timeout, you have to manage the returns from wait, an example here:
fn run() {
let mut child = match Command::new("sleep").arg("1").spawn() {
Ok(child) => child,
Err(e) => fail!("failed to execute child: {}", e),
};
loop {
child.set_timeout(Some(1000));
match child.wait() {
// Here assume any error is timeout, you can filter from IoErrorKind
Err(..) => println!("Timeout"),
Ok(ExitStatus(0)) => {
println!("Finished without errors");
return;
}
Ok(ExitStatus(a)) => {
println!("Finished with error number: {}", a);
return;
}
Ok(ExitSignal(a)) => {
println!("Terminated by signal number: {}", a);
return;
}
}
}
}
About using streams, check with wait_with_output, or implement something similar with channels and threads : http://doc.rust-lang.org/src/std/home/rustbuild/src/rust-buildbot/slave/nightly-linux/build/src/libstd/io/process.rs.html#601
Hope it helped
Have a look in cargo:
https://docs.rs/cargo-util/0.1.1/cargo_util/struct.ProcessBuilder.html#method.exec_with_streaming
The only downside is that cargo-util seems to need openssl even with default-features=false...
But you can at least see how it and read2 are done.

Resources