Why doesn't a nested await point yield up to tokio::select - rust

I think I'm missing something very basic. My expectation is that when tick.tick() completes and starts the sleep loop, tokio::time::sleep(...).await should yield back to the select statement, allowing longer_tick a chance to complete. But once the shorter tick completes, this gets stuck in the sleep loop, never yielding back to select.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ddb47ab0cd5669def5ea0596803381a8
use std::time::Duration;
use tokio::time::interval;
#[tokio::main]
async fn main() {
let mut tick = interval(Duration::from_millis(500));
let mut longer_tick = interval(Duration::from_millis(1000));
loop {
tokio::select! {
_ = longer_tick.tick() => {
println!("longer tick");
},
_ = tick.tick() => {
println!("sleeping");
sleep().await;
},
}
}
}
async fn sleep() {
let mut idx = 0;
loop {
let time = idx.min(5);
println!("Sleeping for {} s", time);
tokio::time::sleep(Duration::from_secs(time)).await;
idx += 1;
}
}

Tokio's select only waits for the futures in the branch section, not the handler. So the sleep future is not considered when using select!.
Also, per the documentation
Waits on multiple concurrent branches, returning when the first branch completes, cancelling the remaining branches.
When one of the branch completes (the interval's in this case), the other branches are cancelled.
Therefore when you are using select, it will only ever execute one of the branch handlers.

Related

If "futures do nothing unless awaited", why does `tokio::spawn` work anyway?

I have read here that futures in Rust do nothing unless they are awaited. However, I tried a more complex example and it is a little unclear why I get a message printed by the 2nd print in this example because task::spawn gives me a JoinHanlde on which I do not do any .await.
Meanwhile, I tried the same example, but with an await above the 2nd print, and now I get printed only the message in the 1st print.
If I wait for all the futures at the end, I get printed both messages, which I understood. My question is why the behaviour in the previous 2 cases.
use futures::stream::{FuturesUnordered, StreamExt};
use futures::TryStreamExt;
use rand::prelude::*;
use std::collections::VecDeque;
use std::sync::Arc;
use tokio::sync::Semaphore;
use tokio::task::JoinHandle;
use tokio::{task, time};
fn candidates() -> Vec<i32> {
Vec::from([2, 2])
}
async fn produce_event(nanos: u64) -> i32 {
println!("waiting {}", nanos);
time::sleep(time::Duration::from_nanos(nanos)).await;
1
}
async fn f(seconds: i64, semaphore: &Arc<Semaphore>) {
let mut futures = vec![];
for (i, j) in (0..1).enumerate() {
for (i, event) in candidates().into_iter().enumerate() {
let permit = Arc::clone(semaphore).acquire_owned().await;
let secs = 500;
futures.push(task::spawn(async move {
let _permit = permit;
produce_event(500); // 2nd example has an .await here
println!("Event produced at {}", seconds);
}));
}
}
}
#[tokio::main()]
async fn main() {
let semaphore = Arc::new(Semaphore::new(45000));
for _ in 0..1 {
let mut futures: FuturesUnordered<_> = (0..2).map(|moment| f(moment, &semaphore)).collect();
while let Some(item) = futures.next().await {
let () = item;
}
}
}
However, I tried a more complex example and it is a little unclear why I get a message printed by the 2nd print in this example because task::spawn gives me a JoinHanlde on which I do not do any .await.
You're spawning tasks. A task is a separate thread of execution which can execute concurrently to the current task, and can be scheduled in parallel.
All the JoinHandle does there is wait for that task to end, it doesn't control the task running.
Meanwhile, I tried the same example, but with an await above the 2nd print, and now I get printed only the message in the 1st print.
You spawn a bunch of tasks and make them sleep. Since you don't wait for them to terminate (don't join them) nor is there any sort of sleep in their parent task, once all the tasks have been spawned the loops terminate, you reach the end of the main function and the program terminates.
At this point all the tasks are still sleeping.

rust: Awaiting future causes block when using a simple future from the futures_signals crate

Unfortunately the tutorial and documentation for futures_signals doesn't show how to make the code behind the future actually execute.
I have tried using:
use futures_signals::signal::Mutable;
use futures_signals::signal::SignalExt; //for Iterator trait (gives for_each)
use futures::executor::LocalPool;
fn main(){
let my_state = Mutable::new(5);
let future = my_state.signal().for_each(|value| {
// This code is run for the current value of my_state,
// and also every time my_state changes
println!("{}", value);
async {}
});
println!("Awaiting...");
let mut pool = LocalPool::new();
pool.run_until(future);
println!("Done!");
}
This prints the "5", but then blocks forever. It does not print "Done!".
Why does it not see that the future has completed?
To expand upon jmbs comment, the future will only complete when the Mutable that returned it is dropped. This is because the Mutable can change state until it is dropped, and the callback will be called every time Mutable changes. The future is only complete when no more callbacks can be called.
See below for a fully working example:
use futures_signals::signal::Mutable;
use futures_signals::signal::SignalExt; //for Iterator trait (gives for_each)
use futures::executor::LocalPool;
use std::thread;
use futures::join;
fn main(){
//create my_state, and a clone that will be passed to the thread
let my_state = Mutable::new(5);
let my_state_shared = my_state.clone();
//increment my_state by 1 in a loop, until it reaches 10
thread::spawn(move || {
loop {
my_state_shared.set(my_state_shared.get() + 1);
thread::sleep(std::time::Duration::from_secs(1));
if my_state_shared.get() == 10 {
break;
}
}
});
//create observers
let obs_a_future = my_state.signal().for_each(|value| {
println!("Observer A {}", value);
async {}
});
let obs_b_future = my_state.signal().for_each(|value| {
println!("Observer B {}", value);
async {}
});
drop(my_state);
//run the app
let mut pool = LocalPool::new();
pool.run_until( async {
join!(obs_a_future, obs_b_future);
});
println!("!");
}

How to make the timer start from 1 to 50000 millisecond in rust?

Cargo.toml
[dependencies]
hookmap = "0.4.7"
delay_timer = "0.10.1"
main.rs
use hookmap::*;
use delay_timer::prelude::*;
use anyhow::Result;
use smol::Timer;
use std::time::Duration;
fn main() {
let hotkey = Hotkey::new();
hotkey!(hotkey => {
modifier(F1) {
//1、When I press F1,the timer start from 1 to 50000 millisecond. how to implement this.
//let mut current_time= 1;
//2、if the current_time equals some numbers, do somethings.
if current_time =2000 {
//do something 1
}
if current_time =39999 {
//do something 2
}
//...
}
});
hotkey.handle_input();
}
You can bind the key_press of F1 to the creation of a DelayTimer which will
execute the various tasks you need after a defined duration has passed.
The following code will create a separate timer every time the F1 key is pressed. Every scheduled task will print a line after 2000, 39999 and 50000 ms. Since it is not specified, with this solution it is possible that two instances of the created task runs at the same time.
use hookmap::*;
use anyhow::Result;
use delay_timer::prelude::*;
use std::time::Duration;
use smol::Timer;
fn main() -> Result<()> {
let hotkey = Hotkey::new();
hotkey!(hotkey => {
on_press F1 => |_event| {
let delay_timer = DelayTimerBuilder::default().build();
// Create the task
// A chain of task instances.
let task_instance_chain = delay_timer.insert_task(build_task_async_print().unwrap()).unwrap();
// Get the running instance of task 1.
let task_instance = task_instance_chain.next_with_wait().unwrap();
// This while loop avoids removing the task before it completes
while task_instance.get_state() != delay_timer::prelude::instance::COMPLETED {};
// Remove task which id is 1.
delay_timer.remove_task(1).unwrap();
};
});
hotkey.handle_input();
Ok(())
}
// Actually build the task
fn build_task_async_print() -> Result<Task, TaskError> {
let mut task_builder = TaskBuilder::default();
// This is the function in which you want to wait
let body = create_async_fn_body!({
Timer::after(Duration::from_millis(2000)).await;
println!("First something");
Timer::after(Duration::from_millis(39999)).await;
println!("Second something");
Timer::after(Duration::from_millis(50000)).await;
println!("Timer end");
});
// Build the task and assign 1 to it
task_builder
.set_task_id(1)
.spawn(body)
}
Mind that this solution works only on Windows since the hookmap crate supports only that OS.

Why do Rust mutexes not seem to give the lock to the thread that wanted to lock it last?

I wanted to write a program that spawns two threads that lock a Mutex, increase it, print something, and then unlock the Mutex so the other thread can do the same. I added some sleep time to make it more consistent, so I thought the output should be something like:
ping pong ping pong …
but the actual output is pretty random. Most of the time, it is just
ping ping ping … pong
But there's no consistency at all; sometimes there is a “pong” in the middle too.
I was of the belief that mutexes had some kind of way to determine who wanted to lock it last but it doesn’t look like that’s the case.
How does the locking actually work?
How can I get the desired output?
use std::sync::{Arc, Mutex};
use std::{thread, time};
fn main() {
let data1 = Arc::new(Mutex::new(1));
let data2 = data1.clone();
let ten_millis = time::Duration::from_millis(10);
let a = thread::spawn(move || loop {
let mut data = data1.lock().unwrap();
thread::sleep(ten_millis);
println!("ping ");
*data += 1;
if *data > 10 {
break;
}
});
let b = thread::spawn(move || loop {
let mut data = data2.lock().unwrap();
thread::sleep(ten_millis);
println!("pong ");
*data += 1;
if *data > 10 {
break;
}
});
a.join().unwrap();
b.join().unwrap();
}
Mutex and RwLock both defer to OS-specific primitives and cannot be guaranteed to be fair. On Windows, they are both implemented with SRW locks which are specifically documented as not fair. I didn't do research for other operating systems but you definitely cannot rely on fairness with std::sync::Mutex, especially if you need this code to be portable.
A possible solution in Rust is the Mutex implementation provided by the parking_lot crate, which provides an unlock_fair method, which is implemented with a fair algorithm.
From the parking_lot documentation:
By default, mutexes are unfair and allow the current thread to re-lock the mutex before another has the chance to acquire the lock, even if that thread has been blocked on the mutex for a long time. This is the default because it allows much higher throughput as it avoids forcing a context switch on every mutex unlock. This can result in one thread acquiring a mutex many more times than other threads.
However in some cases it can be beneficial to ensure fairness by forcing the lock to pass on to a waiting thread if there is one. This is done by using this method instead of dropping the MutexGuard normally.
While parking_lot::Mutex doesn't claim to be fair without specifically using the unlock_fair method, I found that your code produced the same number of pings as pongs, by just making that switch (playground), not even using the unlock_fair method.
Usually mutexes are unlocked automatically, when a guard goes out of scope. To make it unlock fairly, you need to insert this method call before the guard is dropped:
let b = thread::spawn(move || loop {
let mut data = data1.lock();
thread::sleep(ten_millis);
println!("pong ");
*data += 1;
if *data > 10 {
break;
}
MutexGuard::unlock_fair(data);
});
The order of locking the mutex is not guaranteed in any way; it's possible for the first thread to acquire the lock 100% of the time, while the second thread 0%
The threads are scheduled by the OS and the following scenario is quite possible:
the OS gives CPU time to the first thread and it acquires the lock
the OS gives CPU time to the second thread, but the lock is taken, hence it goes to sleep
The fist thread releases the lock, but is still allowed to run by the OS. It goes for another iteration of the loop and re-acquires the lock
The other thread cannot proceed, because the lock is still taken.
If you give the second thread more time to acquire the lock you will see the expected ping-pong pattern, although there is no guarantee (a bad OS may decide to never give CPU time to some of your threads):
use std::sync::{Arc, Mutex};
use std::{thread, time};
fn main() {
let data1 = Arc::new(Mutex::new(1));
let data2 = data1.clone();
let ten_millis = time::Duration::from_millis(10);
let a = thread::spawn(move || loop {
let mut data = data1.lock().unwrap();
*data += 1;
if *data > 10 {
break;
}
drop(data);
thread::sleep(ten_millis);
println!("ping ");
});
let b = thread::spawn(move || loop {
let mut data = data2.lock().unwrap();
*data += 1;
if *data > 10 {
break;
}
drop(data);
thread::sleep(ten_millis);
println!("pong ");
});
a.join().unwrap();
b.join().unwrap();
}
You can verify that by playing with the sleep time. The lower the sleep time, the more irregular the ping-pong alternations will be, and with values as low as 10ms, you may see ping-ping-pong, etc.
Essentially, a solution based on time is bad by design. You can guarantee that "ping" will be followed by "pong" by improving the algorithm. For instance you can print "ping" on odd numbers and "pong" on even numbers:
use std::sync::{Arc, Mutex};
use std::{thread, time};
const MAX_ITER: i32 = 10;
fn main() {
let data1 = Arc::new(Mutex::new(1));
let data2 = data1.clone();
let ten_millis = time::Duration::from_millis(10);
let a = thread::spawn(move || 'outer: loop {
loop {
thread::sleep(ten_millis);
let mut data = data1.lock().unwrap();
if *data > MAX_ITER {
break 'outer;
}
if *data & 1 == 1 {
*data += 1;
println!("ping ");
break;
}
}
});
let b = thread::spawn(move || 'outer: loop {
loop {
thread::sleep(ten_millis);
let mut data = data2.lock().unwrap();
if *data > MAX_ITER {
break 'outer;
}
if *data & 1 == 0 {
*data += 1;
println!("pong ");
break;
}
}
});
a.join().unwrap();
b.join().unwrap();
}
This isn't the best implementation, but I tried to do it with as few modifications as possible to the original code.
You may also consider an implementation with a Condvar, a better solution, in my opinion, as it avoids the busy waiting on the mutex (ps: also removed the code duplication):
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
const MAX_ITER: i32 = 10;
fn main() {
let cv1 = Arc::new((Condvar::new(), Mutex::new(1)));
let cv2 = cv1.clone();
let a = thread::spawn(ping_pong_task("ping", cv1, |x| x & 1 == 1));
let b = thread::spawn(ping_pong_task("pong", cv2, |x| x & 1 == 0));
a.join().unwrap();
b.join().unwrap();
}
fn ping_pong_task<S: Into<String>>(
msg: S,
cv: Arc<(Condvar, Mutex<i32>)>,
check: impl Fn(i32) -> bool) -> impl Fn()
{
let message = msg.into();
move || {
let (condvar, mutex) = &*cv;
let mut value = mutex.lock().unwrap();
loop {
if check(*value) {
println!("{} ", message);
*value += 1;
condvar.notify_all();
}
if *value > MAX_ITER {
break;
}
value = condvar.wait(value).unwrap();
}
}
}
I was of the belief that mutexes had some kind of way to determine who wanted to lock it last but it doesn’t look like that’s the case.
Nope. The job of a mutex is just to make the code run as fast as possible. Alternation gives the worst performance because you're constantly blowing out the CPU caches. You are asking for the worst possible implementation of a mutex.
How does the locking actually work?
The scheduler tries to get as much work done as possible. It's your job to write code that only does the work you really want to get done.
How can I get the desired output?
Don't use two threads if you just want to do one thing then something else then the first thing again. Use threads when you don't care about the order in which work is done and just want to get as much work done as possible.

Is there an API to race N threads (or N closures on N threads) to completion?

Given several threads that complete with an Output value, how do I get the first Output that's produced? Ideally while still being able to get the remaining Outputs later in the order they're produced, and bearing in mind that some threads may or may not terminate.
Example:
struct Output(i32);
fn main() {
let mut spawned_threads = Vec::new();
for i in 0..10 {
let join_handle: ::std::thread::JoinHandle<Output> = ::std::thread::spawn(move || {
// pretend to do some work that takes some amount of time
::std::thread::sleep(::std::time::Duration::from_millis(
(1000 - (100 * i)) as u64,
));
Output(i) // then pretend to return the `Output` of that work
});
spawned_threads.push(join_handle);
}
// I can do this to wait for each thread to finish and collect all `Output`s
let outputs_in_order_of_thread_spawning = spawned_threads
.into_iter()
.map(::std::thread::JoinHandle::join)
.collect::<Vec<::std::thread::Result<Output>>>();
// but how would I get the `Output`s in order of completed threads?
}
I could solve the problem myself using a shared queue/channels/similar, but are there built-in APIs or existing libraries which could solve this use case for me more elegantly?
I'm looking for an API like:
fn race_threads<A: Send>(
threads: Vec<::std::thread::JoinHandle<A>>
) -> (::std::thread::Result<A>, Vec<::std::thread::JoinHandle<A>>) {
unimplemented!("so far this doesn't seem to exist")
}
(Rayon's join is the closest I could find, but a) it only races 2 closures rather than an arbitrary number of closures, and b) the thread pool w/ work stealing approach doesn't make sense for my use case of having some closures that might run forever.)
It is possible to solve this use case using pointers from How to check if a thread has finished in Rust? just like it's possible to solve this use case using an MPSC channel, however here I'm after a clean API to race n threads (or failing that, n closures on n threads).
These problems can be solved by using a condition variable:
use std::sync::{Arc, Condvar, Mutex};
#[derive(Debug)]
struct Output(i32);
enum State {
Starting,
Joinable,
Joined,
}
fn main() {
let pair = Arc::new((Mutex::new(Vec::new()), Condvar::new()));
let mut spawned_threads = Vec::new();
let &(ref lock, ref cvar) = &*pair;
for i in 0..10 {
let my_pair = pair.clone();
let join_handle: ::std::thread::JoinHandle<Output> = ::std::thread::spawn(move || {
// pretend to do some work that takes some amount of time
::std::thread::sleep(::std::time::Duration::from_millis(
(1000 - (100 * i)) as u64,
));
let &(ref lock, ref cvar) = &*my_pair;
let mut joinable = lock.lock().unwrap();
joinable[i] = State::Joinable;
cvar.notify_one();
Output(i as i32) // then pretend to return the `Output` of that work
});
lock.lock().unwrap().push(State::Starting);
spawned_threads.push(Some(join_handle));
}
let mut should_stop = false;
while !should_stop {
let locked = lock.lock().unwrap();
let mut locked = cvar.wait(locked).unwrap();
should_stop = true;
for (i, state) in locked.iter_mut().enumerate() {
match *state {
State::Starting => {
should_stop = false;
}
State::Joinable => {
*state = State::Joined;
println!("{:?}", spawned_threads[i].take().unwrap().join());
}
State::Joined => (),
}
}
}
}
(playground link)
I'm not claiming this is the simplest way to do it. The condition variable will awake the main thread every time a child thread is done. The list can show the state of each thread, if one is (about to) finish, it can be joined.
No, there is no such API.
You've already been presented with multiple options to solve your problem:
Use channels
Use a CondVar
Use futures
Sometimes when programming, you have to go beyond sticking pre-made blocks together. This is supposed to be a fun part of programming. I encourage you to embrace it. Go create your ideal API using the components available and publish it to crates.io.
I really don't see what's so terrible about the channels version:
use std::{sync::mpsc, thread, time::Duration};
#[derive(Debug)]
struct Output(i32);
fn main() {
let (tx, rx) = mpsc::channel();
for i in 0..10 {
let tx = tx.clone();
thread::spawn(move || {
thread::sleep(Duration::from_millis((1000 - (100 * i)) as u64));
tx.send(Output(i)).unwrap();
});
}
// Don't hold on to the sender ourselves
// Otherwise the loop would never terminate
drop(tx);
for r in rx {
println!("{:?}", r);
}
}

Resources