Help required to migrate from dropped MainContext::channel api

I have an app that is using the MainContext::channel() api to update its state in response to events from other threads.

This api has now disappeared and I have no idea how I can achieve similar behaviour.

example is

// Set up the scheduled tasks to query the aircraft position
            let (tx, rx) = MainContext::channel(PRIORITY_DEFAULT);
            rx.attach(None, clone!(@weak self as view => @default-return glib::source::Continue(true), move |ap: Option<AircraftPositionInfo> | {
                if let Some(renderer) = view.renderer.borrow().as_ref() {
                    renderer.set_aircraft_position(ap);
                }
                view.gl_area.queue_draw();
                glib::source::Continue(true)
            }));

            let recurring_handle = scheduling::Scheduler::delayed_recurring(
                std::time::Duration::from_secs(2),
                std::time::Duration::from_secs(5),
                move || get_aircraft_position_task(tx.clone()),
            ).start();

The sheduled task then sends a message on tx

On Dec 11, 2023 @gdesmott added a comment in commit glib: improve message on deprecated channel API · gtk-rs/gtk-rs-core@5a17fef · GitHub

#[deprecated = “Use an async channel, from async-channel for example, on the main context using spawn_future_local() instead”]
But I can’t see how that enables me to attach any sort of event handler to process messages in the way the dropped channel allowed.

It seems like some really nice functionality has been discarded here.

Use any kind of async channel you have available. You seem to be using some async runtime already, which probably provides its own kind of channel implementation or maybe it’s providing a JoinHandle for tasks that could be directly awaited on.

If none of this exists, the simplest would be to use the async-channel crate and mechanically transform the above to

let (tx, rx) = async_channel::unbounded();

glib::MainContext::default().spawn_local(clone!(@weak self as video => async move {
    while let Ok(ap) = rx.recv().await {
        if let Some(renderer) = view.renderer.borrow().as_ref() {
            renderer.set_aircraft_position(ap);
        }
        view.gl_area.queue_draw();
    }
}));

let recurring_handle = scheduling::Scheduler::delayed_recurring(
    std::time::Duration::from_secs(2),
    std::time::Duration::from_secs(5),
    move || get_aircraft_position_task(tx.clone()),
).start();

It’s very limited functionality that often lead to bad solutions. Rust has built-in support for async/await so directly making use of that instead provides much more flexibility, while at the same time it’s simpler because you don’t have to learn yet another different concept.

Thanks @sdroege, that is very helpful.

I see what concept I was missing here, which was how and where to attach the await loop.

1 Like