Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broken ColumnViewRowWidget refcounting during SignalListItemFactory bind callback in gtk 4.12 #1560

Open
awused opened this issue Jan 14, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@awused
Copy link

awused commented Jan 14, 2024

Bug description

This is an unusual one. I have some code that was grabbing the grandparents of columnview listcell entries so that I could use them for dragging and drop, since painting a single cell looked bizarre. It worked fine in GTK 4.10, but with 4.12 gtk-rs in particular breaks.

Just calling drop(child.parent().unwrap().parent()) crashes the application by inadvertently freeing the last reference to child's grandparent, which is obviously wrong and should never happen.

I am not completely certain this is a gtk-rs bug, but I've tried reproducing this with similar code against gtk itself in C (manually using ref/unref, so if the problem was objects being created with 0 refcount I would have triggered it), and I could not reproduce it despite trying multiple ways.

Reproduction

Apply the patch to examples/column_view_datagrid/main.rs and run with cargo run --bin column_view_datagrid against gtk4.12.

The output I get is honestly bizarre. Notably the grandparent refcount is 1, not 2, so as soon as gp is dropped the grandparent GtkColumnViewRowWidget is disposed, which breaks everything going forward. The timeout seems necessary as if I exclude it the breakage doesn't happen. Without the timeout, the grandparent refcount is properly 2 in these logs.

Parent Widget { inner: TypedObjectRef { inner: 0x55ee326f4040, type: GtkColumnViewCellWidget } }, refcount: 2
Grandparent Widget { inner: TypedObjectRef { inner: 0x55ee326f37b0, type: GtkColumnViewRowWidget } }, refcount: 1

(column_view_datagrid:1828183): Gtk-CRITICAL **: 21:27:15.295: gtk_widget_set_state_flags: assertion 'GTK_IS_WIDGET (widget)' failed

(column_view_datagrid:1828183): Gtk-CRITICAL **: 21:27:15.296: gtk_accessible_update_state: assertion 'GTK_IS_ACCESSIBLE (self)' failed

(column_view_datagrid:1828183): Gtk-CRITICAL **: 21:27:15.296: gtk_widget_get_next_sibling: assertion 'GTK_IS_WIDGET (widget)' failed

(column_view_datagrid:1828183): Gtk-CRITICAL **: 21:27:15.296: gtk_widget_set_state_flags: assertion 'GTK_IS_WIDGET (widget)' failed

(column_view_datagrid:1828183): Gtk-CRITICAL **: 21:27:15.296: gtk_accessible_update_state: assertion 'GTK_IS_ACCESSIBLE (self)' failed

(column_view_datagrid:1828183): Gtk-CRITICAL **: 21:27:15.296: gtk_widget_insert_after: assertion 'GTK_IS_WIDGET (widget)' failed
Parent Widget { inner: TypedObjectRef { inner: 0x55ee32e823b0, type: GtkColumnViewCellWidget } }, refcount: 2
Grandparent Widget { inner: TypedObjectRef { inner: 0x55ee32e7fb10, type: GtkColumnViewRowWidget } }, refcount: 1

(column_view_datagrid:1828183): Gtk-CRITICAL **: 21:27:15.296: gtk_widget_get_next_sibling: assertion 'GTK_IS_WIDGET (widget)' failed

(column_view_datagrid:1828183): Gtk-CRITICAL **: 21:27:15.296: gtk_widget_insert_after: assertion 'GTK_IS_WIDGET (widget)' failed

.... many repetitions

zsh: segmentation fault (core dumped)  cargo run --bin column_view_datagrid

Grandparent Widget { inner: TypedObjectRef { inner: 0x55ee326f37b0, type: GtkColumnViewRowWidget } }, refcount: 1 should have a refcount of 2, not 1.

Backtrace

This is a segfault, and the gdb output isn't terribly interesting either.

patch here
diff --git a/examples/column_view_datagrid/main.rs b/examples/column_view_datagrid/main.rs
index 90c877f2a0..8c2cbf6bdf 100644
--- a/examples/column_view_datagrid/main.rs
+++ b/examples/column_view_datagrid/main.rs
@@ -33,11 +33,14 @@ fn build_ui(application: &gtk::Application) {

     let store = gio::ListStore::new::<BoxedAnyObject>();

-    (0..10000).for_each(|i| {
-        store.append(&BoxedAnyObject::new(Row {
-            col1: format!("col1 {i}"),
-            col2: format!("col2 {i}"),
-        }))
+    let store_2 = store.clone();
+    glib::timeout_add_local_once(std::time::Duration::from_secs(1), move || {
+        (0..10000).for_each(|i| {
+            store_2.append(&BoxedAnyObject::new(Row {
+                col1: format!("col1 {i}"),
+                col2: format!("col2 {i}"),
+            }))
+        });
     });
     let sel = gtk::SingleSelection::new(Some(store));
     let columnview = gtk::ColumnView::new(Some(sel));
@@ -58,7 +61,12 @@ fn build_ui(application: &gtk::Application) {
         let ent = Entry {
             name: r.col1.to_string(),
         };
+
         child.set_entry(&ent);
+        let p = child.parent().unwrap();
+        let gp = p.parent().unwrap();
+        println!("Parent {p:?}, refcount: {}", p.ref_count());
+        println!("Grandparent {gp:?}, refcount: {}", gp.ref_count());
     });
     col2factory.connect_setup(move |_factory, item| {
         let item = item.downcast_ref::<gtk::ListItem>().unwrap();
@awused awused added the bug Something isn't working label Jan 14, 2024
@bilelmoussaoui
Copy link
Member

I don't think the cause of whatever issue you are facing is caused by the bindings. A good way to ensure that is to write a similar code to reproduce the issue in C.

@awused
Copy link
Author

awused commented Jan 20, 2024

I did attempt to do the same thing in C in gtk-demo, as I said in the bug report, but the refcounts appear correct. Maybe you can see where my attempted C reproduction is different from that gtk4-rs is doing.

refcount_patch.patch.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants