Generating List Item Widgets Asynchronously

So, I have been looking into making my Album application call ffmpeg asynchronously, via smol + async-process. Currently, I use ffmpeg to generate lightweight 100x100 jpeg thumbnails for photo and video files.

The app displays these thumbnails in a GtkGridView, so the GtkImages are inside GtkListItems. This means there is a factory generating these widgets, so the code to call ffmpeg / fetch thumbnails from cache is called within a GtkSignalListItemFactory bind signal handler.

Even though I have prepped all the thumbnail code to be async, I reached a bit of a ‘dead end’ with what scope of the program runs asynchronously. Since these calls are made within a GTK signal handler (which is a closure given as a callback) I cannot make the closure itself asynchronous. To start executing with an asynchronous runtime, there will always be a “block_on” function call that blocks the current thread until completion. So, this means that I cannot manufacture these list items asynchronously, only async within each list item manufactured, which blocks the thread each time, which defeats the purpose.

Hopefully the above made sense. Any help is appreciated. :slight_smile:
You can also read over the source of my application below:

Hi,

Why not displaying a placeholder image from the factory, then start a tread (GTask) to call ffmpeg and update the real image later in idle?

I could definitely try that. Thank you for the reply. :slight_smile:
I will update you if the solution works or if I run into any issues.

I was able to resolve my problem taking a slightly different approach, but I only found this solution after thinking about your solution. :slight_smile:

I used glib::spawn_future_local and async-channel to asynchronously await the spawned ffmpeg process until completion.

let (tx, rx) = async_channel::bounded(1);

glib::spawn_future_local(clone!(@strong tx => async move {
    if let Ok(path) = generate_thumbnail_image(&absolute_path).await {
        tx.send(path).await.expect("Async channel needs to be open.");
    } else {
        g_critical!("LibraryView", "FFmpeg failed to generate a thumbnail image.");
    }
}));

glib::spawn_future_local(clone!(@weak image => async move {
    while let Ok(path) = rx.recv().await {
        image.clear();
        image.set_file(Some(&path));
    }
}));

Thank you again for your help!

1 Like