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

[Gtk3] fix for SWTException on Table.remove(...) with focused row #1604 #1605

Closed
wants to merge 1 commit into from

Conversation

om-11-2024
Copy link

@om-11-2024 om-11-2024 commented Nov 20, 2024

Fix for SWTException on Table.remove(...) with focused row.

The cause: gtk_list_store_remove(...) for a focused row invokes Table.cellDataProc(...) with not-yet-updated Table.items.
Fix: remove TableItem from Table.items before gtk_list_store_remove(...).

Fixes #1604:

This bug happens in Gtk3, I haven't tried to reproduce this bug in Gtk4.

Java stacktrace:

org.eclipse.swt.SWTException: Widget is disposed
	at org.eclipse.swt.SWT.error(SWT.java:4922)
	at org.eclipse.swt.SWT.error(SWT.java:4837)
	at org.eclipse.swt.SWT.error(SWT.java:4808)
	at org.eclipse.swt.widgets.Widget.error(Widget.java:597)
	at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:512)
	at org.eclipse.swt.widgets.TableItem.setText(TableItem.java:1363)
	at org.eclipse.swt.tests.gtk.Test_Gtk3_Table_Remove_Focused_Row.lambda$3(Test_Gtk3_Table_Remove_Focused_Row.java:68)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5857)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1617)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1643)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1626)
	at org.eclipse.swt.widgets.Table.checkData(Table.java:289)
	at org.eclipse.swt.widgets.Table.cellDataProc(Table.java:227)
	at org.eclipse.swt.widgets.Display.cellDataProc(Display.java:995)
	at org.eclipse.swt.internal.gtk.GTK.gtk_list_store_remove(Native Method)
	at org.eclipse.swt.widgets.Table.remove(Table.java:2668)
	...

The main cause of the error is that:

  1. when a row with focus is deleted with gtk_list_store_remove(...), then cell data function Table.cellDataProc(...) is called for a row after the removed one
  2. but inside Table.cellDataProc(...) Table.items isn't yet updated , therefore Table.cellDataProc(...) operates with TableItem, which is already disposed but not yet removed from Table.items

Inside gtk_list_store_remove(...) the row is removed from the GtkTreeModel, and only then GtkTreeView callbacks are invoked.
It means that when Table.cellDataProc(...) is invoked, the row has already been removed in Gtk3.

The fix: remove TableItem from Table.items before gtk_list_store_remove(...) instead of after.

Os: Ubuntu 24.04.1 LTS
Gtk: 3.24.41
Swt: 4.967.8

=================================================

Why gtk_list_store_remove(...) invokes Table.cellDataProc(...) only for a row with focus and not for ordinary rows?

The reason is that in gtk3 when a row with focus is deleted, the next row receives focus, which among other things creates GtkCellAccessible (a "cell with focus" for assistive technology in gtk3).
Creation of GtkCellAccessible invokes cell data function Table.cellDataProc(...) - just like it happens when a standard cell in GtkTreeView gets rendered.

Here is the stacktrace in gtk3 code:

apply_cell_attributes() at gtkcellarea.c:1257
g_hash_table_foreach() at ghash.c:2117
gtk_cell_area_real_apply_attributes() at gtkcellarea.c:1286
gtk_cell_area_box_apply_attributes() at gtkcellareabox.c:1310
_gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEANv() at gtkmarshalers.c:5447
g_type_class_meta_marshalv() at gclosure.c:1062
_g_closure_invoke_va() at gclosure.c:897
signal_emit_valist_unlocked() at gsignal.c:3424
g_signal_emit_valist() at gsignal.c:3263
g_signal_emit() at gsignal.c:3583
gtk_cell_area_apply_attributes() at gtkcellarea.c:2373
gtk_tree_view_column_cell_set_cell_data() at gtktreeviewcolumn.c:2821
set_cell_data() at gtktreeviewaccessible.c:347
create_cell() at gtktreeviewaccessible.c:439
_gtk_tree_view_accessible_add_state() at gtktreeviewaccessible.c:2053
gtk_tree_view_real_set_cursor() at gtktreeview.c:13377
gtk_tree_view_row_deleted() at gtktreeview.c:9430
g_cclosure_marshal_VOID__BOXED() at gmarshal.c:1628
g_closure_invoke() at gclosure.c:834
signal_emit_unlocked_R() at gsignal.c:3888
signal_emit_valist_unlocked() at gsignal.c:3520
g_signal_emit_valist() at gsignal.c:3263
g_signal_emit() at gsignal.c:3583
gtk_tree_model_row_deleted() at gtktreemodel.c:1914
gtk_list_store_remove() at gtkliststore.c:1219
Java_org_eclipse_swt_internal_gtk_GTK_gtk_1list_1store_1remove()

The code that leads to execution of cell data function for a row with focus is in gtktreeview.c:

static void
gtk_tree_view_row_deleted (GtkTreeModel *model,
			   GtkTreePath  *path,
			   gpointer      data)
{
  ...
  /* If the cursor row got deleted, move the cursor to the next row */
  if (tree_view->priv->cursor_node &&
      (tree_view->priv->cursor_node == node ||
       (node->children && (tree_view->priv->cursor_tree == node->children ||
                           _gtk_rbtree_contains (node->children, tree_view->priv->cursor_tree)))))
    {
      ...
      cursor_changed = TRUE;
    }
  ...
  if (cursor_changed)
    {
      if (cursor_node)
        {
          ...
          gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CURSOR_INVALID);
          ...
        }
      ...
    }
  ...
}

Copy link
Contributor

Test Results

   428 files   -    55     428 suites   - 55   5m 55s ⏱️ - 6m 39s
 4 095 tests ±    0   4 088 ✅ +    3   7 💤 ±0  0 ❌  - 3 
14 046 runs   - 2 127  13 961 ✅  - 2 119  85 💤  - 5  0 ❌  - 3 

Results for commit 10d0e01. ± Comparison against base commit e6588c2.

…ipse-platform#1604

Fix for SWTException on Table.remove(...) with focused row.
This bug happens only in Gtk3.
The cause: `gtk_list_store_remove(...)` for a focused row invokes `Table.cellDataProc(...)` with not-yet-updated `Table.items`.
Fix: remove `TableItem` from `Table.items` before `gtk_list_store_remove(...)`.

Fixes eclipse-platform#1604:

The exception only happens in Gtk3.

Java stacktrace:
```java
org.eclipse.swt.SWTException: Widget is disposed
	at org.eclipse.swt.SWT.error(SWT.java:4922)
	at org.eclipse.swt.SWT.error(SWT.java:4837)
	at org.eclipse.swt.SWT.error(SWT.java:4808)
	at org.eclipse.swt.widgets.Widget.error(Widget.java:597)
	at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:512)
	at org.eclipse.swt.widgets.TableItem.setText(TableItem.java:1363)
	at org.eclipse.swt.tests.gtk.Test_Gtk3_Table_Remove_Focused_Row.lambda$3(Test_Gtk3_Table_Remove_Focused_Row.java:68)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5857)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1617)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1643)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1626)
	at org.eclipse.swt.widgets.Table.checkData(Table.java:289)
	at org.eclipse.swt.widgets.Table.cellDataProc(Table.java:227)
	at org.eclipse.swt.widgets.Display.cellDataProc(Display.java:995)
	at org.eclipse.swt.internal.gtk.GTK.gtk_list_store_remove(Native Method)
	at org.eclipse.swt.widgets.Table.remove(Table.java:2668)
	...
```

The main cause of the error is that:
 1. when a row with focus is deleted with `gtk_list_store_remove(...)`, then cell data function `Table.cellDataProc(...)` is called for a row after the removed one
 2. but inside `Table.cellDataProc(...)` `Table.items` isn't yet updated , therefore `Table.cellDataProc(...)` operates with `TableItem`, which is already disposed but not yet removed from `Table.items`

Inside `gtk_list_store_remove(...)` the row is removed from the `GtkTreeModel`, and only then `GtkTreeView` callbacks are invoked.
It means that when `Table.cellDataProc(...)` is invoked, the row has already been removed in Gtk3.

The fix: remove `TableItem` from `Table.items` before `gtk_list_store_remove(...)` instead of after.

Important: I haven't tried to reproduce this bug in GTK4.

Os:  Ubuntu 24.04.1 LTS
Gtk: 3.24.41
Swt: 4.967.8

=================================================

Why `gtk_list_store_remove(...)` invokes `Table.cellDataProc(...)` only for a row with focus and not for ordinary rows?

The reason is that in gtk3 when a row with focus is deleted, the next row receives focus, which among other things creates `GtkCellAccessible` (a "cell with focus" for assistive technology in gtk3).
Creation of `GtkCellAccessible` invokes cell data function `Table.cellDataProc(...)` - just like it happens when a standard cell in `GtkTreeView` gets rendered.

Here is the stacktrace in gtk3 code:
```c
apply_cell_attributes() at gtkcellarea.c:1257
g_hash_table_foreach() at ghash.c:2117
gtk_cell_area_real_apply_attributes() at gtkcellarea.c:1286
gtk_cell_area_box_apply_attributes() at gtkcellareabox.c:1310
_gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEANv() at gtkmarshalers.c:5447
g_type_class_meta_marshalv() at gclosure.c:1062
_g_closure_invoke_va() at gclosure.c:897
signal_emit_valist_unlocked() at gsignal.c:3424
g_signal_emit_valist() at gsignal.c:3263
g_signal_emit() at gsignal.c:3583
gtk_cell_area_apply_attributes() at gtkcellarea.c:2373
gtk_tree_view_column_cell_set_cell_data() at gtktreeviewcolumn.c:2821
set_cell_data() at gtktreeviewaccessible.c:347
create_cell() at gtktreeviewaccessible.c:439
_gtk_tree_view_accessible_add_state() at gtktreeviewaccessible.c:2053
gtk_tree_view_real_set_cursor() at gtktreeview.c:13377
gtk_tree_view_row_deleted() at gtktreeview.c:9430
g_cclosure_marshal_VOID__BOXED() at gmarshal.c:1628
g_closure_invoke() at gclosure.c:834
signal_emit_unlocked_R() at gsignal.c:3888
signal_emit_valist_unlocked() at gsignal.c:3520
g_signal_emit_valist() at gsignal.c:3263
g_signal_emit() at gsignal.c:3583
gtk_tree_model_row_deleted() at gtktreemodel.c:1914
gtk_list_store_remove() at gtkliststore.c:1219
Java_org_eclipse_swt_internal_gtk_GTK_gtk_1list_1store_1remove()
```

The code that leads to execution of cell data function for a row with focus is in gtktreeview.c:
```
static void
gtk_tree_view_row_deleted (GtkTreeModel *model,
			   GtkTreePath  *path,
			   gpointer      data)
{
  ...
  /* If the cursor row got deleted, move the cursor to the next row */
  if (tree_view->priv->cursor_node &&
      (tree_view->priv->cursor_node == node ||
       (node->children && (tree_view->priv->cursor_tree == node->children ||
                           _gtk_rbtree_contains (node->children, tree_view->priv->cursor_tree)))))
    {
      ...
      cursor_changed = TRUE;
    }
  ...
  if (cursor_changed)
    {
      if (cursor_node)
        {
          ...
          gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CURSOR_INVALID);
          ...
        }
      ...
    }
  ...
}
```
@om-11-2024
Copy link
Author

If it's possible, it would be great if this fix is also applied to the older versions of eclipse RCP which are still maintained.

@om-11-2024 om-11-2024 closed this Nov 21, 2024
@akurtakov
Copy link
Member

Did you found some fundamental issue in the PR thus you closed it? We are in the process of wrapping up December release so there are no reviews going on right now.

@om-11-2024
Copy link
Author

Hello @akurtakov.

No, there were no fundamental issues.
I simple noticed that the code in Table.destroyItem() also contains the code that I've fixed in remove*(...) methods: GTK.gtk_list_store_remove followed by update of Table.items.
And so I moved there Table.items update before Table.destroyItem() just like in remove*() methods.

I thought no one looked at this PR yet.
That's why I used force pushes in my repo.
Since this was the 3rd force push and since I also discovered that there is a special button to create Bug reports, I decided to re-create both the issue and the PR: #1606 #1608

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Gtk3] Bug: SWTException on Table.remove(...) with focused row
2 participants