Reputation: 1390
I'm trying to write a basic multithreaded application using gtk3-rs, where the main thread sends a message to a child thread when a button is clicked, and the child thread sends a message back in response, after doing some calculations, the results of which are displayed by the main thread in a dialog box.
This seems simple enough conceptually, but I'm running into a problem where the channels that I'm creating in the callback that is used by gtk::Application::connect_activate
to build the user interface are getting closed before the child thread (also created in that callback, and then detached) can even use them once, let alone how I intended, which is continually throughout the life of the application.
These are glib channels on the MainContext, not MSPC channels, so instead of busy-waiting for input like for normal channels, I was able to attach a listener on both receivers. I have one listening in the main thread (attached in the UI builder callback) and one listening in the spawned thread, but apparently that's not enough to keep the channels alive, because when I try to send a message to the thread's channel, it errors out saying that the thread is closed.
So the basic structure of my code is like this:
fn connect_events(/* event box, channel a */) {
event_box.connect_button_release_event(move |_, _| {
a.send("foo").unwrap();
});
}
fn build_ui(app: >k::Application) {
let (a, b) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let (c, d) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let event_box = /* GTK event box to capture events */;
connect_events(&event_box, a.clone());
thread::spawn(move || {
b.attach(/* handle receiving a message from the main thread by sending a message back on c */);
});
d.attach(/* pop up a dialog box with whatever was sent back */);
}
fn main() {
let application = gtk::Application::new(
Some("com.example.aaaaaaaa"),
Default::default(),
);
application.connect_activate(build_ui);
application.run();
}
So, how do I convince Rust to keep the channels alive? I tried doing some lazy_static magic and using .leak()
, but neither of those seemed to work, and moving all of this code out of the UI builder is unfortunately not an option.
Upvotes: 1
Views: 230
Reputation: 1434
My pragmatic answer is: Don't use glib channels.
I'm using async rust channels for things like this. In your case, a oneshot channel could be useful. But many crates provide async channels, async-std or tokio for example.
You can spawn a function via glib::MainContext::default().spawn_local()
that .await
s the message(s) from the channel and show the dialog there.
Upvotes: 1