Correct usage of async with tokio in Rust

458 Views Asked by At

I never worked with asynchronous code (but have exp with multithreading) in Rust. I thought it is mostly the same thing, however I got some problems. Here listing of my sample:

use tokio::{join, sync::RwLock};
use std::sync::Arc;
use tokio::time::{sleep, Duration};

struct MyThing {
    n: usize,
}

impl MyThing {
    fn new(x: usize) -> MyThing {
        MyThing{ n: x }
    }
}

async fn rr(v: Arc<RwLock<MyThing>>) {
    loop {
        let read = v.read().await;
        println!("Value : {}", (*read).n);
        // sleep(Duration::from_millis(50)).await;
    }
}

async fn ww(w: Arc<RwLock<MyThing>>) {
    loop {
            let mut write = w.write().await;
            (*write).n += 2;
            println!("Updated value: {}", (*write).n);
            sleep(Duration::from_millis(200)).await;
        } 
}

#[tokio::main]
async fn main() {
    let x = Arc::new(RwLock::new(MyThing::new(11)));
    let w = x.clone();
    let r = x.clone();
    let h = tokio::spawn(async move {
        ww(w).await
    });
    let j = tokio::spawn(async move {
        rr(r).await
    });
    
    join!(h);
    join!(j);
}

So i was expecting that I would get 2 async "threads", and while ww is sleeping I will get ton of readings, but, im getting next output:

Updated value: 13
Value : 13
Updated value: 15
Value : 15
Updated value: 17
Value : 17
Updated value: 19
Value : 19
Updated value: 21
...

So my question is: Is it correct behavior or I just dumb? (And additional question, if i delete await in tokio::spawn after i call function, those functions rr and ww aren't called?)

1

There are 1 best solutions below

1
On BEST ANSWER

You're sleeping while holding the lock. This bug can be caused in threaded programming too.

Because you don't drop the lock (write), it is not released, and rr() cannot acquire a read lock until after the sleep.

Drop the lock before the sleep, and you'll see the expected output:

async fn ww(w: Arc<RwLock<MyThing>>) {
    loop {
        let mut write = w.write().await;
        (*write).n += 2;
        println!("Updated value: {}", (*write).n);
        drop(write);
        sleep(Duration::from_millis(200)).await;
    }
}