Search code examples
rustasync-awaitrust-tokio

how can I use a random number and a tokio::time::sleep within an async function in rust


I have a gRPC server method for which I want to simulate some work for this I have this

async fn write(&self, request: Request<WriteRequest>)
                     -> Result<Response<WriteResponse>, Status> {
    let mut rng = rand::thread_rng();
    let random = rng.gen_range(1000..5000);
    // Sleep for the random duration
    sleep(Duration::from_millis(random)).await;
    return Ok(Response::new(WriteResponse { ... }));
}

but the code is not happy about the random value, I get the error

future cannot be sent between threads safely future created by async block is not `Send`

I know I can add + Send in the function but in this case, I cannot modify the signature of the method because it is an interface generated by the gRPC compiler

When I used hardcoded value within the Duration in works.

How can I call the random number generation? or how can I sleep for a random value using sleep? is there another way?


Solution

  • This happens because rng is held past an await point. You can get around this by enclosing it in a block, which ensures it is not stored in the async function's state machine.

    let random_secs = {
        let mut rng = rand::thread_rng();
        rng.gen_range(1000..5000)
    };
    

    If you do need to store an RNG, you can store a normal PRNG (such as StdRng) that is seeded from thread_rng.

    use rand::{Rng, SeedableRng, rngs::StdRng};
        
    let mut rng = {
        let rng = rand::thread_rng();
        StdRng::from_rng(rng).unwrap()
    };
        
    loop {
        let millis = rng.gen_range(1..10);
        tokio::time::sleep(std::time::Duration::from_millis(millis)).await;
        println!("{millis}");
    }