Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions book/listings/main_event_loop/10/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use glib::clone;
use gtk::{Application, ApplicationWindow, Button, glib};
use gtk::{gio, prelude::*};

use std::{
sync::{Arc, Mutex},
thread,
time::Duration,
};

const APP_ID: &str = "org.gtk_rs.MainEventLoop10";

fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();

// Connect to "activate" signal of `app`
app.connect_activate(build_ui);

// Run the application
app.run()
}

struct DemoStruct {
text: String,
}

impl DemoStruct {
fn mutate(&mut self, string: String) {
self.text = string.to_string();
}
}

fn build_ui(app: &Application) {
// Create a button
let button = Button::builder()
.label("START")
.margin_top(12)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();

let (sender, receiver) = async_channel::bounded(1);
// ANCHOR: callback
//
// Wrap the new structure in Arc/Mutext
let demo_struct = Arc::new(Mutex::new(DemoStruct {
text: "Start".to_string(),
}));

// Connect to "clicked" signal of `button`
button.connect_clicked(move |moved_button| {
let sender = sender.clone();
// Get mutable reference to the structure
let arc_struct = Arc::clone(&demo_struct);

moved_button.set_label("Working");

gio::spawn_blocking(move || {
let three_seconds = Duration::from_secs(3);
thread::sleep(three_seconds);
// Mutate the string on another thread
arc_struct
.lock()
.unwrap()
.mutate("Mutated on another thread".to_string());
// Send the structe back
sender.send_blocking(arc_struct).unwrap();
});
});

glib::spawn_future_local(clone!(
#[weak]
button,
async move {
while let Ok(mutated_struct) = receiver.recv().await {
// Set the label using the string from the received structure
button.set_label(&mutated_struct.lock().unwrap().text);
}
}
));
// ANCHOR_END: callback
//
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(&button)
.build();

// Present window
window.present();
}
12 changes: 12 additions & 0 deletions book/src/main_event_loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,18 @@ If you decide to share it, you user name will be printed on the console.

<div style="text-align:center"><img src="img/main_event_loop_ashpd.png" alt="Dialog requesting user information."/></div>

## Custom structs and multithreading

If you have a custom structure that needs to perform its function on a separate thread to avoid blocking, Rust's [Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html) and [Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html) are required for safe data transport. `Mutex` (mutual exclusion) allows data access from another thread while guaranteeing exclusivity of the request call. To request data from another thread, `lock()` is called on the `Mutext` to signal the attempt. But Rust's ownership system does not allow `Mutext` data to be safely exchanged between threads. To perform data transfer between threads, you need to utilize Rust's `Arc` structure. These `Arc`s (Atomic Reference Counting) are safe to share between the application threads. But keep in mind that this safety comes with the price and will affect the performance. In some cases it is cheaper to run operations on a single thread without spending resources on locks and reference counting.

Lets mutate the `text` string of the `DemoStruct` structure in another thread:

Filename: <a class=file-link href="https://github.com/gtk-rs/gtk4-rs/blob/main/book/listings/main_event_loop/10/main.rs">listings/main_event_loop/10/main.rs</a>

```rust
{{#rustdoc_include ../listings/main_event_loop/10/main.rs:callback}}
```

## Tokio

[`tokio`](https://docs.rs/tokio/latest/tokio/) is Rust's most popular asynchronous platform.
Expand Down
Loading