Crash from disposing ColumnView when underlying ListStore is changed
Steps to reproduce
- Have a ColumnView (unsure if this affects other ListViews)
- Have a backing ListStore with few items in it - this seems either racy or sensitive to the number of items (my particular chain is ListStore -> MultiSelection -> ColumnView)
- Remove the ColumnView from its parent widget and dispose of it
- Mutate the ListStore without much delay (in my case, I am calling extend() with some items)
- Segfault
Getting a minimal reproduction is kind of hard and it seems racy, or at least the other work my application is doing to get the items to extend the list with may be sometimes too fast or slow to trigger it. By the time the segfault happens, my application has no references at all to the ColumnView.
A ColumnView (or other list view) that is being disposed of should not need to respond to changes to the backing list store, and if it does it shouldn't crash.
I have worked around the issue by explicitly calling column_view_set_model(NULL)
before unreffing the ColumnView, which, so far, seems to eliminate the crashes.
Version information
GTK 4.12.5, also GTK 4.14.1 git head
Warnings
None
Backtrace
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007fa32b279657 in gtk_column_list_view_create_list_widget (base=0x55ea27bd26b0) at ../gtk/gtkcolumnview.c:183
183 result = gtk_column_view_row_widget_new (gtk_list_view_get_factory (self->listview), FALSE);
[Current thread is 1 (Thread 0x7fa32903f000 (LWP 295065))]
warning: Missing auto-load script at offset 0 in section .debug_gdb_scripts
of file /cache/rust/debug/xxxx
Use `info auto-load python-scripts [REGEXP]' to list them.
Missing separate debuginfos, use: dnf debuginfo-install xorg-x11-drv-nvidia-libs-545.29.06-2.fc39.x86_64
(gdb) bt full
#0 0x00007fa32b279657 in gtk_column_list_view_create_list_widget (base=0x55ea27bd26b0) at ../gtk/gtkcolumnview.c:183
self = <optimized out>
result = <optimized out>
i = <optimized out>
#1 0x00007fa32b30eef3 in gtk_list_item_manager_ensure_items (self=<optimized out>, change=0x7fff3c458e40, update_start=39, update_diff=39) at ../gtk/gtklistitemmanager.c:1359
item = 0x55ea27bb51b0
tile = 0x55ea27d0f6e0
header = <optimized out>
insert_after = 0x0
position = 0
i = 0
n_items = <optimized out>
query_n_items = 39
offset = 0
tracked = 1
has_sections = 0
#2 0x00007fa32b312f8f in gtk_list_item_manager_model_items_changed_cb (model=<optimized out>, position=<optimized out>, removed=<optimized out>, added=<optimized out>, self=<optimized out>) at ../gtk/gtklistitemmanager.c:1567
change = {deleted_items = 0x0, recycled_items = {head = 0x0, tail = 0x0, length = 0}, recycled_headers = {head = 0x0, tail = 0x0, length = 0}}
l = <optimized out>
n_items = 39
#3 0x00007fa32ac3852a in g_closure_invoke (closure=0x55ea27bfd900, return_value=0x0, n_param_values=4, param_values=0x7fff3c4590a0, invocation_hint=0x7fff3c458ff0) at ../gobject/gclosure.c:832
marshal = 0x7fa32afecc10 <_g_cclosure_marshal_VOID__UINT_UINT_UINT>
marshal_data = 0x0
in_marshal = 0
real_closure = 0x55ea27bfd8e0
__func__ = "g_closure_invoke"
#4 0x00007fa32ac66fec in signal_emit_unlocked_R.isra.0 (node=node@entry=0x7fff3c4591c0, detail=detail@entry=0, instance=instance@entry=0x55ea26fa8c50, emission_return=emission_return@entry=0x0, instance_and_params=instance_and_params@entry=0x7fff3c4590a0)
at ../gobject/gsignal.c:3980
tmp = <optimized out>
handler = 0x55ea2790e8a0
accumulator = <optimized out>
emission = {next = 0x7fff3c4595d0, instance = 0x55ea26fa8c50, ihint = {signal_id = 2, detail = 0, run_type = (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN)}, state = EMISSION_RUN, chain_type = 0x4}
class_closure = <optimized out>
hlist = <optimized out>
handler_list = 0x55ea27111ec0
return_accu = <optimized out>
accu = {g_type = 0x0, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
signal_id = <optimized out>
max_sequential_handler_number = <optimized out>
return_value_altered = <optimized out>
n_params = <optimized out>
EMIT_RESTART = <optimized out>
__func__ = {<optimized out> <repeats 23 times>}
#5 0x00007fa32ac57d59 in signal_emit_valist_unlocked (instance=instance@entry=0x55ea26fa8c50, signal_id=signal_id@entry=2, detail=detail@entry=0, var_args=var_args@entry=0x7fff3c459330) at ../gobject/gsignal.c:3612
instance_and_params = <optimized out>
param_values = <optimized out>
node = <optimized out>
i = <optimized out>
node_copy = Python Exception <class 'gdb.error'>: value has been optimized out
{signal_id = 2, itype = , name = 0x7fa32b0e233d "items-changed", destroyed = 0, flags = 2, n_params = 3, single_va_closure_is_valid = 1, single_va_closure_is_after = 0, param_types = 0x55ea26d6d1b0, return_type = 0x4, class_closure_bsa = 0x0, accumulator = 0x0, c_marshaller = 0x7fa32afecc10 <_g_cclosure_marshal_VOID__UINT_UINT_UINT>, va_marshaller = 0x7fa32afec070 <_g_cclosure_marshal_VOID__UINT_UINT_UINTv>, emission_hooks = 0x0, single_va_closure = 0x0}
__func__ = "signal_emit_valist_unlocked"
#6 0x00007fa32ac57f91 in g_signal_emit_valist (instance=0x55ea26fa8c50, signal_id=2, detail=0, var_args=var_args@entry=0x7fff3c459330) at ../gobject/gsignal.c:3355
#7 0x00007fa32ac58053 in g_signal_emit (instance=instance@entry=0x55ea26fa8c50, signal_id=<optimized out>, detail=detail@entry=0) at ../gobject/gsignal.c:3675
var_args = {{gp_offset = 48, fp_offset = 48, overflow_arg_area = 0x7fff3c459410, reg_save_area = 0x7fff3c459350}}
#8 0x00007fa32b03b7d4 in g_list_model_items_changed (list=list@entry=0x55ea26fa8c50, position=position@entry=0, removed=removed@entry=0, added=added@entry=39) at ../gio/glistmodel.c:323
__func__ = "g_list_model_items_changed"
#9 0x00007fa32b329b85 in gtk_multi_selection_items_changed_cb (model=0x55ea27cd9ae0, position=0, removed=0, added=39, self=0x55ea26fa8c50) at ../gtk/gtkmultiselection.c:289
iter = {dummy1 = 0x55ea26fa8cd0, dummy2 = 0x7fff3c4595e0, dummy3 = 0x7fff3c459470, dummy4 = 8, dummy5 = 32675, dummy6 = 0x4}
item = 0x0
pos_pointer = 0x7fff3c459690
pending = <optimized out>
i = <optimized out>
#10 0x00007fa32ac3852a in g_closure_invoke (closure=0x55ea26fa8d90, return_value=0x0, n_param_values=4, param_values=0x7fff3c459690, invocation_hint=0x7fff3c4595e0) at ../gobject/gclosure.c:832
marshal = 0x7fa32afecc10 <_g_cclosure_marshal_VOID__UINT_UINT_UINT>
marshal_data = 0x0
in_marshal = 0
real_closure = 0x55ea26fa8d70
__func__ = "g_closure_invoke"
#11 0x00007fa32ac66fec in signal_emit_unlocked_R.isra.0 (node=node@entry=0x7fff3c4597b0, detail=detail@entry=0, instance=instance@entry=0x55ea27cd9ae0, emission_return=emission_return@entry=0x0, instance_and_params=instance_and_params@entry=0x7fff3c459690)
at ../gobject/gsignal.c:3980
tmp = <optimized out>
handler = 0x55ea270e8480
accumulator = <optimized out>
emission = {next = 0x0, instance = 0x55ea27cd9ae0, ihint = {signal_id = 2, detail = 0, run_type = (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACCUMULATOR_FIRST_RUN)}, state = EMISSION_RUN, chain_type = 0x4}
class_closure = <optimized out>
hlist = <optimized out>
handler_list = 0x55ea270e8480
return_accu = <optimized out>
accu = {g_type = 0x0, data = {{v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
signal_id = <optimized out>
max_sequential_handler_number = <optimized out>
return_value_altered = <optimized out>
n_params = <optimized out>
EMIT_RESTART = <optimized out>
__func__ = {<optimized out> <repeats 23 times>}
#12 0x00007fa32ac57d59 in signal_emit_valist_unlocked (instance=instance@entry=0x55ea27cd9ae0, signal_id=signal_id@entry=2, detail=detail@entry=0, var_args=var_args@entry=0x7fff3c459920) at ../gobject/gsignal.c:3612
instance_and_params = <optimized out>
param_values = <optimized out>
node = <optimized out>
i = <optimized out>
node_copy = Python Exception <class 'gdb.error'>: value has been optimized out
{signal_id = 2, itype = , name = 0x7fa32b0e233d "items-changed", destroyed = 0, flags = 2, n_params = 3, single_va_closure_is_valid = 1, single_va_closure_is_after = 0, param_types = 0x55ea26d6d1b0, return_type = 0x4, class_closure_bsa = 0x0, accumulator = 0x0, c_marshaller = 0x7fa32afecc10 <_g_cclosure_marshal_VOID__UINT_UINT_UINT>, va_marshaller = 0x7fa32afec070 <_g_cclosure_marshal_VOID__UINT_UINT_UINTv>, emission_hooks = 0x0, single_va_closure = 0x0}
__func__ = "signal_emit_valist_unlocked"
#13 0x00007fa32ac57f91 in g_signal_emit_valist (instance=0x55ea27cd9ae0, signal_id=2, detail=0, var_args=var_args@entry=0x7fff3c459920) at ../gobject/gsignal.c:3355
#14 0x00007fa32ac58053 in g_signal_emit (instance=instance@entry=0x55ea27cd9ae0, signal_id=<optimized out>, detail=detail@entry=0) at ../gobject/gsignal.c:3675
var_args = {{gp_offset = 48, fp_offset = 48, overflow_arg_area = 0x7fff3c459a00, reg_save_area = 0x7fff3c459940}}
#15 0x00007fa32b03b7d4 in g_list_model_items_changed (list=list@entry=0x55ea27cd9ae0, position=position@entry=0, removed=removed@entry=0, added=added@entry=39) at ../gio/glistmodel.c:323
__func__ = "g_list_model_items_changed"
#16 0x00007fa32b03c00d in g_list_store_items_changed (added=39, removed=0, position=0, store=0x55ea27cd9ae0) at ../gio/gliststore.c:92
#17 g_list_store_splice (store=0x55ea27cd9ae0, position=0, n_removals=0, additions=<optimized out>, n_additions=39) at ../gio/gliststore.c:519
it = <optimized out>
n_items = <optimized out>
__func__ = "g_list_store_splice"
#18 0x000055ea2456fed0 in gio::auto::list_store::ListStore::splice<glib::object::Object> (self=0x55ea2708cbb0, position=0, n_removals=0, additions=...) at /home/tszy/.cargo/registry/src/index.crates.io-6f17d22bba15001f/gio-0.19.2/src/list_store.rs:63
additions = 0x7fa30401e9c0
n_additions = 39
#19 0x000055ea2456fa8c in gio::list_store::{impl#3}::extend<xxx::com::entry::EntryObject, alloc::vec::Vec<xxx::com::entry::EntryObject, alloc::alloc::Global>> (self=0x55ea2708cbb0, iter=...)
at /home/tszy/.cargo/registry/src/index.crates.io-6f17d22bba15001f/gio-0.19.2/src/list_store.rs:222
additions = alloc::vec::Vec<glib::object::Object, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<glib::object::Object, alloc::alloc::Global> {ptr: core::ptr::unique::Unique<glib::object::Object> {pointer: core::ptr::non_null::NonNull<glib::object::Object> {pointer: 0x7fa30401e9c0}, _marker: core::marker::PhantomData<glib::object::Object>}, cap: alloc::raw_vec::Cap (1472), alloc: alloc::alloc::Global}, len: 39}
#20 0x000055ea24658dc8 in ::gui::tabs::contents::Contents::apply_snapshot (self=0x55ea2708cba8, snap=...) at src/gui/tabs/contents.rs:106
... bunch of Rust frames
Edited by Thomas Szymanski