Skip to content
Merged
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
51 changes: 25 additions & 26 deletions book/src/g_object_memory_management.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ With our first example, we have window with a single button.
Every button click should increment an integer `number` by one.

```rust ,no_run,compile_fail
#use gtk::prelude::*;
#use gtk::{self, glib, Application, ApplicationWindow, Button};
# use gtk::prelude::*;
# use gtk::{self, glib, Application, ApplicationWindow, Button};
#
#const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0";
# const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0";
#
// DOES NOT COMPILE!
fn main() -> glib::ExitCode {
Expand Down Expand Up @@ -72,7 +72,7 @@ note: function requires argument type to outlive `'static`
help: to force the closure to take ownership of `number` (and any other referenced variables), use the `move` keyword
|
32 | button_increase.connect_clicked(move |_| number += 1);
|
|
```

Our closure only borrows `number`.
Expand All @@ -81,31 +81,31 @@ The compiler also suggests how to fix this.
By adding the `move` keyword in front of the closure, `number` will be moved into the closure.

```rust ,no_run,compile_fail
#use gtk::prelude::*;
#use gtk::{self, glib, Application, ApplicationWindow, Button};
# use gtk::prelude::*;
# use gtk::{self, glib, Application, ApplicationWindow, Button};
#
#const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0";
# const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0";
#
#fn main() -> glib::ExitCode {
# // Create a new application
# let app = Application::builder().application_id(APP_ID).build();
# 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);
# // Connect to "activate" signal of `app`
# app.connect_activate(build_ui);
#
# // Run the application
# app.run()
#}
# // Run the application
# app.run()
# }
#
#fn build_ui(application: &Application) {
# // Create two buttons
# let button_increase = Button::builder()
# .label("Increase")
# .margin_top(12)
# .margin_bottom(12)
# .margin_start(12)
# .margin_end(12)
# .build();
# fn build_ui(application: &Application) {
# // Create two buttons
# let button_increase = Button::builder()
# .label("Increase")
# .margin_top(12)
# .margin_bottom(12)
# .margin_start(12)
# .margin_end(12)
# .build();
#
// DOES NOT COMPILE!
// A mutable integer
Expand All @@ -124,7 +124,7 @@ By adding the `move` keyword in front of the closure, `number` will be moved int
#
# // Present the window
# window.present();
#}
# }
```

This still leaves the following error message:
Expand Down Expand Up @@ -169,7 +169,6 @@ That is exactly what the [`std::rc::Rc`](https://doc.rust-lang.org/std/rc/struct
If we want to modify the content of our [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html),
we can again use the [`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html) type.


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

```rust
Expand Down
56 changes: 26 additions & 30 deletions book/src/main_event_loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ We can't even move the window.
The `sleep` call is an artificial example,
but frequently, we want to run a slightly longer operation in one go.


<div style="text-align:center">
<video autoplay muted loop>
<source src="vid/main_event_loop_1.webm" type="video/webm">
<p>A video which shows that after pressing the button, the window can still be moved</p>
</video>
</div>


## How to Avoid Blocking the Main Loop

In order to avoid blocking the main loop, we can spawn a new task with [`gio::spawn_blocking`](https://gtk-rs.org/gtk-rs-core/stable/latest/docs/gio/fn.spawn_blocking.html) and let the operation run on the thread pool.
Expand All @@ -56,7 +54,6 @@ This is not necessarily what we want.
</video>
</div>


> If you come from another language than Rust, you might be uncomfortable with the thought of running tasks in separate threads before even looking at other options.
> Luckily, Rust's safety guarantees allow you to stop worrying about the nasty bugs that concurrency tends to bring.

Expand Down Expand Up @@ -123,12 +120,12 @@ But why did we not do the same thing with our multithreaded example?

```rust ,no_run,compile_fail
# use std::{thread, time::Duration};
#
#
# use glib::{clone, MainContext, PRIORITY_DEFAULT};
# use gtk::{glib, gio};
# use gtk::prelude::*;
# use gtk::{Application, ApplicationWindow, Button};
#
#
# fn main() {
# // Create a new application
# let app = Application::builder()
Expand All @@ -137,21 +134,21 @@ But why did we not do the same thing with our multithreaded example?
#
# // Connect to "activate" signal
# app.connect_activate(build_ui);
#
#
# // Get command-line arguments
# let args: Vec<String> = args().collect();
# // Run the application
# app.run(&args);
# }
#
#
# // When the application is launched…
# fn build_ui(application: &Application) {
# // Create a window
# let window = ApplicationWindow::builder()
# .application(application)
# .title("My GTK App")
# .build();
#
#
# // Create a button
# let button = Button::builder()
# .label("Press me!")
Expand All @@ -160,7 +157,7 @@ But why did we not do the same thing with our multithreaded example?
# .margin_start(12)
# .margin_end(12)
# .build();
#
#
// DOES NOT COMPILE!
// Connect to "clicked" signal of `button`
button.connect_clicked(move |button| {
Expand All @@ -175,7 +172,7 @@ But why did we not do the same thing with our multithreaded example?
button.set_sensitive(true);
});
});
#
#
# // Add button
# window.set_child(Some(&button));
# window.present();
Expand Down Expand Up @@ -281,32 +278,32 @@ Doing this will block one of the runtime's threads with the GLib main loop, whic
Instead, we bind [`tokio::runtime::Runtime`](https://docs.rs/tokio/latest/tokio/runtime/struct.Runtime.html) to a static variable.

```rust
#use std::sync::OnceLock;
# use std::sync::OnceLock;
#
#use glib::clone;
#use gtk::glib;
#use gtk::prelude::*;
#use gtk::{Application, ApplicationWindow, Button};
#use tokio::runtime::Runtime;
# use glib::clone;
# use gtk::glib;
# use gtk::prelude::*;
# use gtk::{Application, ApplicationWindow, Button};
# use tokio::runtime::Runtime;
#
#const APP_ID: &str = "org.gtk_rs.MainEventLoop0";
# const APP_ID: &str = "org.gtk_rs.MainEventLoop0";
#
// DOES NOT COMPILE!
static RUNTIME: Runtime =
Runtime::new().expect("Setting up tokio runtime needs to succeed.");
#
#fn main() -> glib::ExitCode {
# // Create a new application
# let app = Application::builder().application_id(APP_ID).build();
# 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);
# // Connect to "activate" signal of `app`
# app.connect_activate(build_ui);
#
# // Run the application
# app.run()
#}
# // Run the application
# app.run()
# }
#
#fn build_ui(app: &Application) {
# fn build_ui(app: &Application) {
# // Create a button
# let button = Button::builder()
# .label("Press me!")
Expand Down Expand Up @@ -347,7 +344,7 @@ static RUNTIME: Runtime =
#
# // Present window
# window.present();
#}
# }
```

Unfortunately, this doesn't compile.
Expand All @@ -362,7 +359,6 @@ consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` cr
We could follow the advice directly, but the standard library also provides solutions for that.
With [`std::sync::OnceLock`](https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html) we can initialize the static with the const function `OnceLock::new()` and initialize it the first time our function `runtime` is called.


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

```rust
Expand Down Expand Up @@ -391,12 +387,12 @@ cargo remove tokio reqwest ashpd

How to find out whether you can spawn an `async` task on the `glib` main loop?
`glib` should be able to spawn the task when the called functions come from libraries that either:

- come from the `glib` ecosystem,
- don't depend on a runtime but only on the `futures` family of crates (`futures-io`, `futures-core` etc),
- depend on the `async-std` or `smol` runtimes, or
- have cargo features that let them depend on `async-std`/`smol` instead of `tokio`.


## Conclusion

You don't want to block the main thread long enough that it is noticeable by the user.
Expand All @@ -408,7 +404,7 @@ That means you have to run the task in a separate thread and let it send results

If your task is [IO bound](https://en.wikipedia.org/wiki/I/O_bound), the answer depends on the crates at your disposal and the type of work to be done.

- Light I/O work with functions from crates using `glib`, `smol`, `async-std` or the `futures` trait family can be spawned on the main loop. This way, you can often avoid synchronization via channels.
- Light I/O work with functions from crates using `glib`, `smol`, `async-std` or the `futures` trait family can be spawned on the main loop. This way, you can often avoid synchronization via channels.
- Heavy I/O work might still benefit from running in a separate thread / an async executor to avoid saturating the main loop. If you are unsure, benchmarking is advised.

If the best crate for the job relies on `tokio`, you will have to spawn it with the tokio runtime and communicate via channels.
Loading