Async and glib::closure

Hi all,

I have a small technical question related to the use of GLib. I need to use an asynchronous piece of code within a glib::closure.

The problem can be illustrated with the following code (which does not compile):

signaller.connect_closure(
    "session-requested",
    false,
    glib::closure!(
        |_signaller: glib::Object,
         session_id: &str,
         peer_id: &str,
         offer: Option<&gstwebrtc::WebRTCSessionDescription>| {
            tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
        }
    ),
);

Something like this:

signaller.connect_closure(
    "session-requested",
    false,
    glib::closure!(
        |_signaller: glib::Object,
         session_id: &str,
         peer_id: &str,
         offer: Option<&gstwebrtc::WebRTCSessionDescription>| {
            let rt = tokio::runtime::Builder::new_current_thread()
                .enable_all()
                .build()
                .unwrap();

            rt.block_on(async {
                println!("Waiting...");
                tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
                println!("Waiting finished");
            });
        }
    ),
);

crashes with the error:

Cannot start a runtime from within a runtime. This happens because a function (like block_on) attempted to block the current thread while the thread is being used to drive asynchronous tasks.

This suggests that Tokio is already set up somewhere. I’ve tried something like:

let context = glib::MainContext::default();
context.block_on(async {
    println!("Waiting...");
    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
    println!("Waiting finished");
});

But it blocks forever, and I’m not exactly sure of what I’m doing. Any suggestions?

Thanks!


The problem here is that the signal needs to be handled synchronously and is not an async function. That means, that you can’t simply .await inside it (fails to compile).

As that signal is however emitted from inside the tokio runtime, you also can’t just block inside it. Not via tokio (panics as you saw), and also not via some other async runtime (will block a tokio runtime thread, which can cause all kinds of deadlocks).

That it didn’t work at all with the GLib main context is however caused by running a tokio future (sleep()) from inside another runtime. That doesn’t work: tokio-specific futures must be run from inside the tokio runtime.


The solution to your problem would be to spawn an async task (either on some tokio runtime or whatever else you want to use) and do the work from there asynchronously after returning from the signal handler.

1 Like

Sounds good I’ll try that.
Thanks!