I am trying to create a future polling for inputs from the crossterm crate, which does not provide an asynchronous API, as far as I know.
At first I tried to do something like the following :
use crossterm::event::poll as crossterm_poll;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::time::{sleep, timeout};
struct Polled {}
impl Polled {
pub fn new() -> Polled {
Polled {}
}
}
impl Future for Polled {
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// If there are events pending, it returns "Ok(true)", else it returns instantly
let poll_status = crossterm_poll(Duration::from_secs(0));
if poll_status.is_ok() && poll_status.unwrap() {
return Poll::Ready(true);
}
Poll::Pending
}
}
pub async fn poll(d: Duration) -> Result<bool, ()> {
let polled = Polled::new();
match timeout(d, polled).await {
Ok(b) => Ok(b),
Err(_) => Err(()),
}
}
It technically works but obivously the program started using 100% CPU all the time since the executor always try to poll the future in case there's something new. Thus I wanted to add some asynchronous equivalent to sleep, that would delay the next time the executor tries to poll the Future, so I tried adding the following (right before returning Poll::Pending), which obviously did not work since sleep_future::poll() just returns Pending :
let mut sleep_future = sleep(Duration::from_millis(50));
tokio::pin!(sleep_future);
sleep_future.poll(cx);
cx.waker().wake_by_ref();
The fact that poll is not async forbids the use of async functions, and I'm starting to wonder if what I want to do is actually feasible, or if I'm not diving in my first problem the wrong way.
Is finding a way to do some async sleep the good way to go ?
If not, what is it ? Am I missing something in the asynchronous paradigm ?
Or is it just sometimes impossible to wrap some synchronous logic into a Future if the crate does not give you the necessary tools to do so ?
Thanks in advance anyway !
EDIT : I found a way to do what I want using an async block :
pub async fn poll(d: Duration) -> Result<bool, ()> {
let mdr = async {
loop {
let a = crossterm_poll(Duration::from_secs(0));
if a.is_ok() && a.unwrap() {
break;
}
sleep(Duration::from_millis(50)).await;
}
true
};
match timeout(d, mdr).await {
Ok(b) => Ok(b),
Err(_) => Err(()),
}
}
Is it the idiomatic way to do so ? Or did I miss something more elegant ?
Yes, using an async block is a good way to compose futures, like your custom poller and tokio's sleep.
However, if you did want to write your own Future which also invokes tokio's sleep, here's what you would need to do differently:
Don't call wake_by_ref() immediately — the sleep future will take care of that when its time comes, and that's how you avoid spinning (using 100% CPU).
You must construct the sleep() future once when you intend to sleep (not every time you're polled), then store it in your future (this will require pin-projection) and poll the same future again the next time you're polled. That's how you ensure you wait the intended amount of time and not shorter.
Async blocks are usually a much easier way to get the same result.
Related
I am developing Rust Tokio library for ISO-TP. CAN protocol, which lets you send larger messages. The program is aimed towards linux only.
For this, I am using Tokio structure AsyncFd. When the write is called, I create the Future and then poll it. The problem is when I do two consecutive writes, one after the other.
socket_tx1.write_packet(packet.clone())?.await?;
socket_tx1.write_packet(packet.clone())?.await?;
The first write will end successfully, however second will end with
std::io::ErrorKind::WouldBlock
Which is OK and expected. The buffer is full and we should wait until it's clear and ready for the next write. The poll does not guarantee, that if it returns OK, the following write will be successful.
The problem is that I don't know how to handle this behavior correctly.
I tried the following implementations:
impl Future for IsoTpWriteFuture {
type Output = io::Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
let guard = ready!(self.socket.0.poll_write_ready(cx))?;
match self.socket.0.get_ref().0.write(&self.packet) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => continue,
Ok(_) => return Poll::Ready(Ok(())),
Err(err) => return Poll::Ready(Err(err))
}
}
}
}
This one works, but after I get WouldBlock, this loop results in busy waiting, which I would like to avoid. Since Socket is ready from poll perspective, write is immediately called, Wouldblock is again returned, and routine spins sometime before resolving the write.
The second implementation is more correct, from my perspective, but it doesn't work right now, and I am not sure how to make it work.
impl Future for IsoTpWriteFuture {
type Output = io::Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
let guard = ready!(self.socket.0.poll_write_ready(cx))?;
match guard.try_io(|inner|inner.get_ref().0.write(&self.packet)) {
Err(err) => continue,
Ok(_) => return Poll::Ready(Ok(())),
}
}
}
}
This doesn't work since once try_io() encounters WouldBlock, it will clear readiness from guard. And since poll is edge triggered, this will hang at poll_write_ready and won't make progress.
Is it possible to poll for change after the write returns WouldBlock? Or is the bussy waiting approach unavoidable?
In an asynchronous system, after you get a WouldBlock, you need to return control to the asynchronous runtime (tokio in this case), as well as tell it what it needs to wait on (the file descriptor being ready).
You say you are using Tokio's AsyncFd. It offers several methods, such as poll_read_ready, to check on the fd's readiness and register it with tokio if it needs to wait.
In Rust, I would like to do multiple tasks in parallel and when each task finishes, I would like to do another task handled by the main process.
I know that tasks will finish at different timings, and I don't want to wait for all the tasks to do the next task.
I've tried doing multiple threads handled by the main process but I have to wait for all the threads to finish before doing another action or maybe I did not understand.
for handle in handles {
handle.join().unwrap();
}
How can I manage to do a task handled by the main process after each end of threads without blocking the whole main thread?
Here is a diagram to explain what I want to do :
If i'm not clear or if you have a better idea to handle my problem, don't mind to tell me!
Here's an example how to implement this using FuturesUnordered and Tokio:
use futures::{stream::FuturesUnordered, StreamExt};
use tokio::time::sleep;
use std::{time::Duration, future::ready};
#[tokio::main]
async fn main() {
let tasks = FuturesUnordered::new();
tasks.push(some_task(1000));
tasks.push(some_task(2000));
tasks.push(some_task(500));
tasks.push(some_task(1500));
tasks.for_each(|result| {
println!("Task finished after {} ms.", result);
ready(())
}).await;
}
async fn some_task(delay_ms: u64) -> u64 {
sleep(Duration::from_millis(delay_ms)).await;
delay_ms
}
If you run this code, you can see that the closure passed to for_each() is executed immediately whenever a task finishes, even though they don't finish in the order they were created.
Note that Tokio takes care of scheduling the tasks to different threads for you. By default, there will be one thread per CPU core.
To compile this, you need to add this to your Cargo.toml file:
[dependencies]
futures = "0.3"
tokio = { version = "1", features = ["full"] }
If you want to add some proper error propagation, the code becomes only slightly more complex – most of the added code is for the custom error type:
use futures::{stream::FuturesUnordered, TryStreamExt};
use tokio::time::sleep;
use std::{time::Duration, future::ready};
#[tokio::main]
async fn main() -> Result<(), MyError> {
let tasks = FuturesUnordered::new();
tasks.push(some_task(1000));
tasks.push(some_task(2000));
tasks.push(some_task(500));
tasks.push(some_task(1500));
tasks.try_for_each(|result| {
println!("Task finished after {} ms.", result);
ready(Ok(()))
}).await
}
async fn some_task(delay_ms: u64) -> Result<u64, MyError> {
sleep(Duration::from_millis(delay_ms)).await;
Ok(delay_ms)
}
#[derive(Debug)]
struct MyError {}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MyError occurred")
}
}
impl std::error::Error for MyError {}
In async/await, what's the most idiomatic/efficient way to take an input buffer and, for each item, route it to an existing Future or a new one based on a condition? For example, you're running a server and each incoming packet might either be the start of a new interaction with a client or a continuation of an existing one. You can tell the difference by something in the packet, such as a UUID identifying the interaction.
I'm trying to do this and it's working pretty well for the most part, but every time a new client interaction starts, the latency between the initial request and response increases and I can't figure out why. I'm trying it using these two structs that I made:
pub struct InputBuffer<T> {
queue_send: SyncSender<(T, Instant)>,
queue_recv: Arc<Mutex<Receiver<(T, Instant)>>>,
waker_send: SyncSender<Waker>,
waker_recv: Arc<Mutex<Receiver<Waker>>>,
}
pub struct InputBufferNext<'a, T, F: Fn(&T) -> bool>(pub InputBuffer<T>, pub &'a F);
The relevant functions on InputBuffer are below. Every time we add a new item to the queue using push, we wake all the interested wakers. The choose function takes a Fn(&T) -> bool and returns the next item that satisfies this condition. The choose_and_then function repeatedly chooses items off the queue based on a given condition and runs a given function on them. This is what I'm using to start new client interactions. I've got an Arc<Mutex<HashSet<u64>>> storing UUIDs for active client interactions and this is shared with a closure that returns whether or not the UUID is new and this is the F parameter. If it's not already in the set, then the G parameter is a function that starts a new interaction. (In my use case, the key is more complicated than a u64, but this is adapted for simplicity).
impl<T> InputBuffer<T> {
pub fn push(&self, t:T) {
self.queue_send.send((t, Instant::now())).unwrap();
if let Ok(waker_recv) = self.waker_recv.lock() {
while let Ok(waker) = waker_recv.try_recv() {
waker.wake();
}
}
}
pub async fn choose<F: Fn(&T) -> bool>(self, choose: &F) -> T {
InputBufferNext::<T, F>(Clone::clone(&self), choose).await
}
pub async fn choose_and_then<F: Fn(&T) -> bool, G: Fn(T)>(self, choose: F, and_then: G) {
loop {
and_then(self.clone().choose(&choose).await);
}
}
}
The type that actually implements Future is InputBufferNext and it does it the way shown below. Each time we poll it, we receive items until we find one that matches our condition, then put back the ones that don't. There's a timeout implemented to make sure the buffer can't just accumulate unclaimed items indefinitely. If we don't find what we're looking for, we send our waker to the InputBuffer.
impl<'a, T, F: Fn(&T) -> bool> std::future::Future for InputBufferNext<'a, T, F> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut put_back = vec![];
if let Ok(queue_recv) = self.0.queue_recv.lock() {
let mut accepted: Option<T> = None;
'inner: while let Ok((x, t0)) = queue_recv.try_recv() {
if (self.1)(&x) {
accepted = Some(x);
break 'inner;
} else if t0.elapsed() < INPUT_BUFFER_TIMEOUT {
put_back.push((x, t0));
}
}
for x_t0 in put_back {
self.0.queue_send.send(x_t0).unwrap();
}
if let Some(x) = accepted {
return Poll::Ready(x);
}
}
// We made it all the way through what's available and didn't find what we were looking for
// Let us know when we should try again
self.0.waker_send.send(cx.waker().clone()).unwrap();
Poll::Pending
}
}
Since InputBuffer can be cloned (I had to implement Clone by hand because it didn't like the derive macro, but it's made up of all cloneable types), I just clone it and give it to each async fn that gets spawned with each new interaction. When an interaction wants its next item, it calls choose with a function that picks out its UUID and gets back a Future.
Like I said, this implementation seems to be mostly working, but there's some kind of latency accumulating somewhere. It takes longer and longer for the server to respond to initial requests. I've checked the amount of time that each poll call takes along with the number of calls per second and those both seem to be stable, despite the growing latency. I've also checked the size of the queue of wakers and it seems steady at a low, reasonable number. The waker queue also gets emptied every time a new item is added. The timeout also ensures that the number of items in the queue stays limited.
I'm using a timeout around 100ms and seeing the latency grow to unacceptable levels after 80+ seconds. My use cases starts a new client interaction about 30-50 times per second, to give a rough idea. Each interaction also ends after a short time (<1 sec), so we're not just accumulating active interactions indefinitely. I'm using the ThreadPool executor from futures.
Any suggestions for how I could improve my implementation or abandon it completely in favor of a more highly-optimized library or different design pattern would be much appreciated. Thanks!
I'm processing data via a number of channels where each channel feeds into the next (pipeline processing). I end up with a spawn at the top that looks like this:
let future = async move {
while let Ok(msg) = r.recv().await {
forwarder.receive(msg).await;
}
};
executor_pool::ExecutorPool::spawn(future).detach();
The Forwarder looks like this:
Forwarder {
pub fn validate_sequence(&mut self, msg: TestMessage) -> Result<TestMessage, TestMessage>
pub async fn handle_action(&mut self, cmd: TestMessage);
pub async fn handle_notification(&mut self);
pub async fn receive(&mut self, cmd: TestMessage) {
match self.handle_config(cmd) {
Ok(_) => (),
Err(msg) => match self.validate_sequence(msg) {
Ok(msg) => {
self.handle_action(msg).await;
self.handle_notification().await;
},
Err(msg) => panic!("{} sequence error: expecting {} not cmd {:#?}", self.header(), self.next_seq, msg),
},
}
}
}
Both handle_action and handle_notification call into a sender which is another async fn. My concern is two-fold. The entire pathway to the send (or any other async fn) seems to require an async/await wrapper. In my case I'm 3 deep at the send. Which seems to be a bit ugly, particularly if I have to do any refactoring. Second, is there a runtime cost for each level of async/await or is the compiler smart enough to collapse these? If it helps to make this more concrete, think of it as audio processing where the first stage decodes, the next does leveling, the next does mixing, and then the final does encoding.
To expand of the refactoring concern, let's look at refactoring a for loop.
pub async fn handle_action(&mut self, cmd: FwdMessage) {
match cmd {
FwdMessage::TestData(_) => {
for sender in &self.senders {
for _ in 0 .. self.forwarding_multiplier {
sender.send(FwdMessage::TestData(self.send_count)).await.ok();
self.send_count += 1;
}
}
},
_ => panic!("unhandled action"),
}
}
Rather than a for loop, we'd like to use an iterator. However, that's going to require an async closure -- which is something I haven't figured out how to even express.
Just the act of nesting async functions does not introduce any overhead.
The async/await syntax compiles to a Future that is basically a state-machine that can execute your code in a way that it can be suspended when it needs to wait for something and resumed later. An async function and all (statically known) await'd futures are compiled together into the same Future; the function boundaries somewhat melt away. So yes, the compiler is "smart enough to collapse these" together.
I have written a simple future based on this tutorial which looks like this:
extern crate chrono; // 0.4.6
extern crate futures; // 0.1.25
use std::{io, thread};
use chrono::{DateTime, Duration, Utc};
use futures::{Async, Future, Poll, task};
pub struct WaitInAnotherThread {
end_time: DateTime<Utc>,
running: bool,
}
impl WaitInAnotherThread {
pub fn new(how_long: Duration) -> WaitInAnotherThread {
WaitInAnotherThread {
end_time: Utc::now() + how_long,
running: false,
}
}
pub fn run(&mut self, task: task::Task) {
let lend = self.end_time;
thread::spawn(move || {
while Utc::now() < lend {
let delta_sec = lend.timestamp() - Utc::now().timestamp();
if delta_sec > 0 {
thread::sleep(::std::time::Duration::from_secs(delta_sec as u64));
}
task.notify();
}
println!("the time has come == {:?}!", lend);
});
}
}
impl Future for WaitInAnotherThread {
type Item = ();
type Error = Box<io::Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if Utc::now() < self.end_time {
println!("not ready yet! parking the task.");
if !self.running {
println!("side thread not running! starting now!");
self.run(task::current());
self.running = true;
}
Ok(Async::NotReady)
} else {
println!("ready! the task will complete.");
Ok(Async::Ready(()))
}
}
}
So the question is how do I replace pub fn run(&mut self, task: task::Task) with something that will not create a new thread for the future to resolve. It be useful if someone could rewrite my code with replaced run function without separate thread it will help me to understand how things should be. Also I know that tokio has an timeout implementation but I need this code for learning.
I think I understand what you mean.
Lets say you have two task, the Main and the Worker1, in this case you are polling the worker1 to wait for an answer; BUT there is a better way, and this is to wait for competition of the Worker1; and this can be done without having any Future, you simply call from Main the Worker1 function, when the worker is over the Main will go on. You need no future, you are simply calling a function, and the division Main and Worker1 is just an over-complication.
Now, I think your question became relevant in the moment you add at least another worker, last add Worker2, and you want the Main to resume the computation as soon as one of the two task complete; and you don't want those task to be executed in another thread/process, maybe because you are using asynchronous call (which simply mean the threading is done somewhere else, or you are low level enough that you receive Hardware Interrupt).
Since your Worker1 and Worker2 have to share the same thread, you need a way to save the current execution Main, create the one for one of the worker, and after a certain amount of work, time or other even (Scheduler), switch to the other worker, and so on. This is a Multi-Tasking system, and there are various software implementation for it in Rust; but with HW support you could do things that in software only you could not do (like have the hardware prevent one Task to access the resource from the other), plus you can have the CPU take care of the task switching and all... Well, this is what Thread and Process are.
Future are not what you are looking for, they are higher level and you can find some software scheduler that support them.