GdkParallelTask constantly recreates its worker threads
Steps to reproduce
- Open
gtk4-demo
withGSK_RENDERER=cairo
- Move mouse in and out the search button (the one with "system-search" icon
Alternatively, run a simple Rust program (sorry, I don't really know C)
Source code
Code explanation: GtkApplicationWindow
with its only child being GtkImage
, whose icon-name
property is changed once per second
main.rs
use async_io::Timer;
use futures_lite::StreamExt;
use gtk4::{glib, prelude::*, Application, ApplicationWindow, Image};
use std::time::Duration;
fn main() -> glib::ExitCode {
let application = Application::builder()
.application_id("com.example.FirstGtkApp")
.build();
application.connect_activate(|app| {
let window = ApplicationWindow::builder()
.application(app)
.title("First GTK Program")
.build();
let icon = Image::new();
let icon_cloned = icon.clone();
glib::MainContext::ref_thread_default().spawn_local(async move {
let mut counter = 0;
let mut timer = Timer::interval(Duration::from_secs(1));
while let Some(_instant) = timer.next().await {
counter += 1;
let icon_name = if counter % 2 == 0 {
"battery-caution-symbolic"
} else {
"battery-level-50-charging-symbolic"
};
icon_cloned.set_icon_name(Some(icon_name));
}
});
window.set_child(Some(&icon));
window.present();
});
application.run()
}
Cargo.toml:
[package]
name = "gtkplay"
version = "0.1.0"
edition = "2021"
[dependencies]
async-io = "2.3.4"
futures-lite = "2.3.0"
gtk4 = "0.9.2"
Current behavior
When hover state of the button is changed (or, in Rust example, the icon is redrawn) a bunch of threads on a thread pool is spawned and almost immediately reaped.
Expected outcome
Probably not killing the pool threads too eagerly
Version information
- GTK 4.16.3
Linux reimu 6.11.3-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 10 Oct 2024 20:11:06 +0000 x86_64 GNU/Linux
- Wayland (sway-git 1.10-dev-db76fefd, xorg-xwayland 24.1.3), both
GDK_BACKEND=wayland
andGDK_BACKEND=x11
- mesa 24.2.4
- vulkan: llvmpipe (LLVM 18.1.8, 256 bits)
- opengl: nouveau (NVD9)
- kernel: nouveau
- Arch Linux
Additional information
Setting breakpoint on g_thread_pool_push
reveals that (almost?) always threads are pushed from gdk_memory_convert
Breakpoint trace
gef➤ bt
#0 g_thread_pool_push (
pool=0x555555777180,
data=data@entry=0x7fffffffc6c0,
error=error@entry=0x0
) at ../glib/glib/gthreadpool.c:686
#1 0x00007ffff7694735 in gdk_parallel_task_run (
task_func=task_func@entry=0x7ffff7694f10 <gdk_memory_convert_generic>,
task_data=task_data@entry=0x7fffffffc720
) at ../gtk/gdk/gdkparalleltask.c:78
#2 0x00007ffff76948c9 in gdk_memory_convert (
dest_data=dest_data@entry=0x5555558b8240 "",
dest_stride=dest_stride@entry=0x40,
dest_format=dest_format@entry=GDK_MEMORY_B8G8R8A8_PREMULTIPLIED,
dest_cs=dest_cs@entry=0x7ffff7bd7020 <gdk_default_color_states>,
src_data=0x5555558658b0 "",
src_stride=src_stride@entry=0x40,
src_format=GDK_MEMORY_R8G8B8A8,
src_cs=0x7ffff7bd7020 <gdk_default_color_states>,
width=0x10,
height=0x10
) at ../gtk/gdk/gdkmemoryformat.c:2259
...
Increasing maximal number of unused threads by calling g_thread_pool_set_max_unused_threads(4)
(4 is also the count of cores of my CPU) seems to resolve the issue as pool threads are no longer short-lived.
Another workaround is to run the program under taskset -c 0
to trick gdk_parallel_task_run
into thinking it's being run on a single-core CPU.