Smithay / calloop

A callback-based Event Loop

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Repeating future gets stuck forever

gabrielchiquini opened this issue · comments

I'm writing a code that polls an API repeated times to observe the status of a background job. I realized my code starts polling normally, then at some time, the future does not complete anymore but keeps calling the run callback. I reduced the async code to use a simple sleep call to ilustrate

use std::{time::Duration};
use calloop::{EventLoop, futures::executor};

#[tokio::main]
async fn main() {
    let mut app = App::default();
    let mut event_loop = EventLoop::<App>::try_new().unwrap();
    let handle = event_loop.handle();
    let (executor, scheduler) = executor::<()>().unwrap();

    scheduler.schedule(async_operation()).unwrap();
    
    handle
        .insert_source(executor, |event, _metadata, data| {
            data.tick = Some(event);
        }).unwrap();

    event_loop
        .run(Duration::from_millis(1000), &mut app, |data| {
            if let Some(_) = data.tick.take() {
                data.counter += 1;
                scheduler.schedule(async_operation()).unwrap();
            }
            eprintln!("Counter: {}", data.counter);
        }).unwrap();
}

pub async fn async_operation() {
    tokio::time::sleep(Duration::from_millis(100)).await;
}

#[derive(Default)]
struct App {
    tick: Option<()>,
    counter: u64,
}

It prints the counter value each run call and when the sleep is done, it increments the counter. The same counter value is printed three of four times, normally, but when it reaches 128, it does not increments the counter anymore, and begins to call the run faster

Switching tokio for async_std it worked properly, am I missing something?

The first question that comes is: why is your main function an async fn ran by tokio? There's nothing async in it and it's a blocking function, I'd not be surprised if tokio does not like it very much.

In the original code I have one async call from the main function and I didn't remove because tokio will complain I needed a reactor anyway. I didn't figured out in which place I should spawn tokio in my example and I'm learning async rust too, so, do you have any suggestions to how spawn the reactor here just to rerun the test?

I don't know much about tokio, but my overall impression is that it's really an "all or nothing" framework, which does not expect to be used alongside other event-loop systems, so... my overall recommendation would be "don't try to use tokio and calloop at the same time".

If you really want to do it, the symptoms of your problem seem to be that, for some reason, past 128 futures tokio starts waking the future constantly without it ever being ready. Why it does that I have no idea.

As you noted async-std should be much more friendly with being used with calloop, and calloop itself also provides IO and async primitives (like timers) if you want to go 100% calloop (and thus allowing your app to be single-threaded).

I understand better now. I used tokio in my example mainly because I was using reqwest, actually I did a proof of concept with surf + async-std and worked just fine. Almost certain this is not an issue with callop, right? I think you may close it now, thank you for your help!