How to call a closure with a delay in Rust? Is tokio::Delay a proper tool for making asynchronous callbacks?
My code:
pub struct Object1
{
k1 : i32,
}
//
impl Object1
{
pub fn new() -> Object1
{
Object1
{
k1 : 13
}
}
}
//
fn main()
{
let object1 : Box<Object1> = Box::new( Object1::new() );
callback_call_1( Box::new( ||
{
println!( "{:?}", object1.k1 );
}));
}
//
fn callback_call_1<F>( mut callback1 : F )
where
F : FnMut()
{
println!( "inside callback_call_1" );
callback1();
/* how to call callback1 with delay 1 second, not blocking the thread?
is it possible to call the callback1 in the same thread?
is it possible to reach similar behavior setTimeout from JavaScript has?
*/
}
Here is playground.
Please help if you know the answer or give a hint where to look for the solution of the problem.
The fact that you are talking about tokio::timer::Delay reveals to me that you are using Tokio version 0.1.x, which is extremely old. I would encourage you to upgrade. Here's how you do it with latest Tokio:
Basically, you need to think about who should execute the delayed call. (Rust is a low-level language so you're not exempted from thinking about these details.) Delayed calls introduce concurrency: by the time the callback needs to be executed, there may be other activities going in your program (somewhat simplified).
Your best choice actually is to use an engine like tokio. Timers are surprisingly difficult-to-implement things, tokio provides a scalable timer wheel implementation that efficiently supports many simultaneous timers.
If you don't want to use tokio, you need to somehow arrange for the timer to go off. A simple approach for infrequent timers is to spawn a thread, let the thread sleep for the delay, then do whatever you want to do.
In both cases will this make your code multi-threaded so you're entering the world of Arc, Mutex, and/or message passing etc. if your closure needs to share anything with the outside world.
You want to read the chapters on threading in the Rust book and/or the Tokio tutorial.
ps: and there are probably crates out there as well that implement the mechanics of maintaining timers and executing them concurrently.
Thank you, Godmar. What about event loop mechanism similar to what JS have. Is such mechanism available out of the box for Rust? Can you recommend a crate solving the problem if there is no such thing in the standard library?
That's a good question I've been meaning to ask Alice as well.
Is there a single-threaded async executor framework for Rust (where you stay in single-threaded Rust, e.g. RefCell, Rc, etc.?) I think you have some control about the number of threads Tokio uses but I don't know if there's a way to avoid the overhead (Arc, etc.) of using primitives that are thread-safe.
Tokio is the primary library that provides such an event loop. In fact, as far as I am aware, Deno uses Tokio for exactly this purpose, and Deno is a Javascript runtime implemented in Rust.
If you want to do it without Tokio, you could spawn an ordinary thread with std::thread::spawn and do the same sleep.
@godmar If you wish to avoid synchronization overhead, you can combine Tokio's current thread runtime with the LocalSet type. Actix-web does this.
No. If you check out my article on blocking the thread, you will see that there's an important difference between std::thread::sleep and tokio::time::sleep. Only the first blocks the thread.
To be clear, this provides a single-threaded/sequentially consistent environment for the tasks in a LocalSet, but it doesn't make tokio itself single-threaded/avoid synchronization? And it's otherwise orthogonal, right, so all async I/O etc. works as before?
Well the internals of the LocalSet also avoid synchronization where possible. Though if you want to talk about LocalSet, we should probably start a new topic.