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

Segmentation fault with list filtering/deleting/selecting #1673

Closed
keirlawson opened this issue Apr 2, 2024 · 7 comments
Closed

Segmentation fault with list filtering/deleting/selecting #1673

keirlawson opened this issue Apr 2, 2024 · 7 comments
Labels
bug Something isn't working

Comments

@keirlawson
Copy link

keirlawson commented Apr 2, 2024

Bug description

This code causes a segmentation fault. I've minimised this as best I can, apologies as the snippet is still quite long.

use gtk::glib::clone;
use gtk::{glib, prelude::*, Box, Button, Entry, FilterListModel, ListItem, SingleSelection, StringFilter};
use gtk::{
    Application, ApplicationWindow, Label, ListView,
    ScrolledWindow, SignalListItemFactory, StringList, StringObject, Widget,
};

const APP_ID: &str = "org.gtk_rs.ListWidgets6";

fn main() -> glib::ExitCode {
    let app = Application::builder().application_id(APP_ID).build();

    app.connect_activate(build_ui);

    app.run()
}

fn build_ui(app: &Application) {
    let model: StringList = (0..=100_000).map(|number| number.to_string()).collect();

    let factory = SignalListItemFactory::new();
    factory.connect_setup(move |_, list_item| {
        let label = Label::new(None);
        let list_item = list_item
            .downcast_ref::<ListItem>()
            .expect("Needs to be ListItem");
        list_item.set_child(Some(&label));

        list_item
            .property_expression("item")
            .chain_property::<StringObject>("string")
            .bind(&label, "label", Widget::NONE);
    });

    let delete_button = Button::builder().label("delete top of underlying").build();
    delete_button.connect_clicked(clone!(@strong model, => move |_|{
        model.remove(0);
    }));

    let filter_entry = Entry::builder().build();
    let filter = StringFilter::builder()
        .match_mode(gtk::StringFilterMatchMode::Substring)
        .expression(StringObject::this_expression("string"))
        .build();
    filter_entry.bind_property("text", &filter, "search").build();

    let filter_model = FilterListModel::new(Some(model), Some(filter));
    let selection_model = SingleSelection::new(Some(filter_model));
    selection_model.connect_items_changed(|selection, _, _, _| {
        selection.set_selected(0)
    });

    let list_view = ListView::new(Some(selection_model), Some(factory));

    let scrolled_window = ScrolledWindow::builder()
        .child(&list_view)
        .vexpand(true)
        .build();

    let layout = Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .build();

    layout.append(&filter_entry);
    layout.append(&scrolled_window);
    layout.append(&delete_button);

    let window = ApplicationWindow::builder()
        .application(app)
        .title("My GTK App")
        .default_width(600)
        .default_height(300)
        .child(&layout)
        .build();

    window.present();

    filter_entry.set_text("999");
    delete_button.emit_clicked();
    filter_entry.set_text("");
}
@keirlawson keirlawson added the bug Something isn't working label Apr 2, 2024
@sdroege
Copy link
Member

sdroege commented Apr 3, 2024

Backtrace of the crash

#0  gtk_list_item_manager_model_selection_changed_cb (model=<optimized out>, position=280, n_items=719, self=0x555555f3c5e0) at ../gtk/gtklistitemmanager.c:1677
#1  0x00007ffff722a52a in g_closure_invoke (closure=0x555555f3da90, return_value=0x0, n_param_values=3, param_values=0x7fffffff8a50, invocation_hint=0x7fffffff89a0)
    at ../gobject/gclosure.c:832
#2  0x00007ffff7258fec in signal_emit_unlocked_R.isra.0 (node=node@entry=0x7fffffff8b50, detail=detail@entry=0, instance=instance@entry=0x555555f29240, 
    emission_return=emission_return@entry=0x0, instance_and_params=instance_and_params@entry=0x7fffffff8a50) at ../gobject/gsignal.c:3980
#3  0x00007ffff7249d59 in signal_emit_valist_unlocked (instance=instance@entry=0x555555f29240, signal_id=signal_id@entry=139, detail=detail@entry=0, 
    var_args=var_args@entry=0x7fffffff8cc0) at ../gobject/gsignal.c:3612
#4  0x00007ffff7249f91 in g_signal_emit_valist (instance=0x555555f29240, signal_id=139, detail=0, var_args=var_args@entry=0x7fffffff8cc0) at ../gobject/gsignal.c:3355
#5  0x00007ffff724a053 in g_signal_emit (instance=<optimized out>, signal_id=<optimized out>, detail=<optimized out>) at ../gobject/gsignal.c:3675
#6  0x00007ffff7801bba in gtk_single_selection_set_selected (position=0, self=0x555555f29240) at ../gtk/gtksingleselection.c:678
#7  gtk_single_selection_set_selected (self=0x555555f29240, position=<optimized out>) at ../gtk/gtksingleselection.c:648
#8  0x00005555555747f3 in gtk4::auto::single_selection::SingleSelection::set_selected (self=0x7fffffff8e70, position=0) at gtk4/src/auto/single_selection.rs:128
#9  0x0000555555570c8d in foo::build_ui::{closure#2} (selection=0x7fffffff8e70) at foo/src/main.rs:53
#10 0x000055555556c5a9 in gio::auto::list_model::ListModelExt::connect_items_changed::items_changed_trampoline<gtk4::auto::single_selection::SingleSelection, foo::build_ui::{closure_env#2}> (this=0x555555f29240, position=0, removed=280, added=100000, f=0x1)
    at /home/slomo/.cargo/git/checkouts/gtk-rs-core-7be42ca38bd6361c/92dc873/gio/src/auto/list_model.rs:86
#11 0x00007ffff722a52a in g_closure_invoke (closure=0x555555f294b0, return_value=0x0, n_param_values=4, param_values=0x7fffffff9090, invocation_hint=0x7fffffff8fe0)
    at ../gobject/gclosure.c:832
#12 0x00007ffff7258fec in signal_emit_unlocked_R.isra.0 (node=node@entry=0x7fffffff91b0, detail=detail@entry=0, instance=instance@entry=0x555555f29240, 
    emission_return=emission_return@entry=0x0, instance_and_params=instance_and_params@entry=0x7fffffff9090) at ../gobject/gsignal.c:3980
#13 0x00007ffff7249d59 in signal_emit_valist_unlocked (instance=instance@entry=0x555555f29240, signal_id=signal_id@entry=16, detail=detail@entry=0, 
    var_args=var_args@entry=0x7fffffff9320) at ../gobject/gsignal.c:3612
#14 0x00007ffff7249f91 in g_signal_emit_valist (instance=0x555555f29240, signal_id=16, detail=0, var_args=var_args@entry=0x7fffffff9320) at ../gobject/gsignal.c:3355
#15 0x00007ffff724a053 in g_signal_emit (instance=instance@entry=0x555555f29240, signal_id=<optimized out>, detail=detail@entry=0) at ../gobject/gsignal.c:3675
#16 0x00007ffff74f67d4 in g_list_model_items_changed (list=list@entry=0x555555f29240, position=position@entry=0, removed=removed@entry=280, added=added@entry=100000)
    at ../gio/glistmodel.c:323
#17 0x00007ffff77faed5 in gtk_single_selection_items_changed_cb (model=<optimized out>, position=0, removed=280, added=100000, self=0x555555f29240)
    at ../gtk/gtksingleselection.c:296
#18 0x00007ffff722a52a in g_closure_invoke (closure=0x555555f29310, return_value=0x0, n_param_values=4, param_values=0x7fffffff9630, invocation_hint=0x7fffffff9580)
    at ../gobject/gclosure.c:832
#19 0x00007ffff7258fec in signal_emit_unlocked_R.isra.0 (node=node@entry=0x7fffffff9750, detail=detail@entry=0, instance=instance@entry=0x555555f28490, 
    emission_return=emission_return@entry=0x0, instance_and_params=instance_and_params@entry=0x7fffffff9630) at ../gobject/gsignal.c:3980
#20 0x00007ffff7249d59 in signal_emit_valist_unlocked (instance=instance@entry=0x555555f28490, signal_id=signal_id@entry=16, detail=detail@entry=0, 
    var_args=var_args@entry=0x7fffffff98c0) at ../gobject/gsignal.c:3612
--Type <RET> for more, q to quit, c to continue without paging--
#21 0x00007ffff7249f91 in g_signal_emit_valist (instance=0x555555f28490, signal_id=16, detail=0, var_args=var_args@entry=0x7fffffff98c0) at ../gobject/gsignal.c:3355
#22 0x00007ffff724a053 in g_signal_emit (instance=instance@entry=0x555555f28490, signal_id=<optimized out>, detail=detail@entry=0) at ../gobject/gsignal.c:3675
#23 0x00007ffff74f67d4 in g_list_model_items_changed (list=list@entry=0x555555f28490, position=<optimized out>, removed=<optimized out>, added=<optimized out>)
    at ../gio/glistmodel.c:323
#24 0x00007ffff773d1a2 in gtk_filter_list_model_refilter (self=0x555555f28490, change=<optimized out>) at ../gtk/gtkfilterlistmodel.c:547
#25 0x00007ffff7228b38 in g_cclosure_marshal_VOID__ENUMv (closure=<optimized out>, return_value=<optimized out>, instance=<optimized out>, args=<optimized out>, 
    marshal_data=<optimized out>, n_params=<optimized out>, param_types=0x555555f1dfe0) at ../gobject/gmarshal.c:1028
#26 0x00007ffff7249e85 in _g_closure_invoke_va (param_types=0x555555f1dfe0, n_params=<optimized out>, args=0x7fffffff9c50, instance=0x555555f26fa0, return_value=0x0, 
    closure=0x555555f285d0) at ../gobject/gclosure.c:895
#27 signal_emit_valist_unlocked (instance=instance@entry=0x555555f26fa0, signal_id=signal_id@entry=137, detail=detail@entry=0, var_args=var_args@entry=0x7fffffff9c50)
    at ../gobject/gsignal.c:3516
#28 0x00007ffff7249f91 in g_signal_emit_valist (instance=0x555555f26fa0, signal_id=137, detail=0, var_args=var_args@entry=0x7fffffff9c50) at ../gobject/gsignal.c:3355
#29 0x00007ffff724a053 in g_signal_emit (instance=<optimized out>, signal_id=<optimized out>, detail=<optimized out>) at ../gobject/gsignal.c:3675
#30 0x00007ffff7817467 in gtk_string_filter_set_search (search=<optimized out>, self=0x555555f26fa0) at ../gtk/gtkstringfilter.c:377
#31 gtk_string_filter_set_search (self=0x555555f26fa0, search=<optimized out>) at ../gtk/gtkstringfilter.c:350
#32 0x00007ffff723a75a in object_set_property (object=object@entry=0x555555f26fa0, pspec=0x555555f26ed0, value=value@entry=0x7fffffff9e50, nqueue=nqueue@entry=0x0, 
    user_specified=user_specified@entry=1) at ../gobject/gobject.c:1811
#33 0x00007ffff723d697 in g_object_setv (values=<optimized out>, names=<optimized out>, n_properties=<optimized out>, object=0x555555f26fa0) at ../gobject/gobject.c:2722
#34 g_object_setv (object=0x555555f26fa0, n_properties=<optimized out>, names=<optimized out>, values=<optimized out>) at ../gobject/gobject.c:2693
#35 0x00007ffff723d8d1 in g_object_set_property (object=<optimized out>, property_name=<optimized out>, value=<optimized out>) at ../gobject/gobject.c:3022
#36 0x00007ffff722bab7 in on_source_notify (source=<optimized out>, pspec=<optimized out>, context=<optimized out>) at ../gobject/gbinding.c:556
#37 0x00007ffff722a52a in g_closure_invoke (closure=0x555555f27aa0, return_value=0x0, n_param_values=2, param_values=0x7fffffffa0b0, invocation_hint=0x7fffffffa000)
    at ../gobject/gclosure.c:832
#38 0x00007ffff7258fec in signal_emit_unlocked_R.isra.0 (node=node@entry=0x7fffffffa1a0, detail=detail@entry=600, instance=instance@entry=0x555555f11130, 
    emission_return=emission_return@entry=0x0, instance_and_params=instance_and_params@entry=0x7fffffffa0b0) at ../gobject/gsignal.c:3980
#39 0x00007ffff7249d59 in signal_emit_valist_unlocked (instance=instance@entry=0x555555f11130, signal_id=signal_id@entry=1, detail=detail@entry=600, 
    var_args=var_args@entry=0x7fffffffa310) at ../gobject/gsignal.c:3612
#40 0x00007ffff7249f91 in g_signal_emit_valist (instance=0x555555f11130, signal_id=1, detail=600, var_args=var_args@entry=0x7fffffffa310) at ../gobject/gsignal.c:3355
#41 0x00007ffff724a053 in g_signal_emit (instance=<optimized out>, signal_id=<optimized out>, detail=<optimized out>) at ../gobject/gsignal.c:3675
#42 0x00007ffff72356f4 in g_object_dispatch_properties_changed (object=0x555555f11130, n_pspecs=<optimized out>, pspecs=<optimized out>) at ../gobject/gobject.c:1427
#43 0x00007ffff722aaf0 in g_object_notify_queue_thaw (object=0x555555f11130, nqueue=<optimized out>) at ../gobject/gobject.c:358
#44 0x00007ffff7238dfe in g_object_thaw_notify (object=0x555555f11130) at ../gobject/gobject.c:1700
#45 g_object_thaw_notify (object=0x555555f11130) at ../gobject/gobject.c:1675
#46 0x00007ffff7714d04 in gtk_editable_set_text (text=0x5555555e1e97 "", editable=0x555555f11130) at ../gtk/gtkeditable.c:602
--Type <RET> for more, q to quit, c to continue without paging--
#47 gtk_editable_set_text (editable=0x555555f11130, text=0x5555555e1e97 "") at ../gtk/gtkeditable.c:591
#48 0x000055555556e923 in gtk4::auto::editable::EditableExt::set_text<gtk4::auto::entry::Entry> (self=0x7fffffffab90, text=...) at gtk4/src/auto/editable.rs:228
#49 0x000055555556d112 in foo::build_ui (app=0x7fffffffd340) at foo/src/main.rs:82
#50 0x000055555556ee29 in core::ops::function::Fn::call<fn(&gtk4::auto::application::Application), (&gtk4::auto::application::Application)> ()
    at /rustc/aedd173a2c086e558c2b66d3743b344f977621a7/library/core/src/ops/function.rs:79
#51 0x000055555556b4ec in gio::auto::application::ApplicationExt::connect_activate::activate_trampoline<gtk4::auto::application::Application, fn(&gtk4::auto::application::Application)> (this=0x5555556321e0, f=0x1) at /home/slomo/.cargo/git/checkouts/gtk-rs-core-7be42ca38bd6361c/92dc873/gio/src/auto/application.rs:458
#52 0x00007ffff722a52a in g_closure_invoke (closure=0x5555556340b0, return_value=0x0, n_param_values=1, param_values=0x7fffffffd550, invocation_hint=0x7fffffffd4a0)
    at ../gobject/gclosure.c:832
#53 0x00007ffff7258fec in signal_emit_unlocked_R.isra.0 (node=node@entry=0x7fffffffd620, detail=detail@entry=0, instance=instance@entry=0x5555556321e0, 
    emission_return=emission_return@entry=0x0, instance_and_params=instance_and_params@entry=0x7fffffffd550) at ../gobject/gsignal.c:3980
#54 0x00007ffff7249d59 in signal_emit_valist_unlocked (instance=instance@entry=0x5555556321e0, signal_id=signal_id@entry=8, detail=detail@entry=0, 
    var_args=var_args@entry=0x7fffffffd790) at ../gobject/gsignal.c:3612
#55 0x00007ffff7249f91 in g_signal_emit_valist (instance=0x5555556321e0, signal_id=8, detail=0, var_args=var_args@entry=0x7fffffffd790) at ../gobject/gsignal.c:3355
#56 0x00007ffff724a053 in g_signal_emit (instance=instance@entry=0x5555556321e0, signal_id=<optimized out>, detail=detail@entry=0) at ../gobject/gsignal.c:3675
#57 0x00007ffff750d8c7 in g_application_activate (application=0x5555556321e0) at ../gio/gapplication.c:2316
#58 0x00007ffff7512718 in g_application_real_local_command_line (application=0x5555556321e0, arguments=0x7fffffffd8e8, exit_status=0x7fffffffd8e4) at ../gio/gapplication.c:1152
#59 0x00007ffff75128c3 in g_application_run (application=0x5555556321e0, argc=<optimized out>, argv=0x555555634170) at ../gio/gapplication.c:2546
#60 0x0000555555567a6a in gio::application::ApplicationExtManual::run_with_args<gtk4::auto::application::Application, alloc::string::String> (self=0x7fffffffdad8, args=...)
    at /home/slomo/.cargo/git/checkouts/gtk-rs-core-7be42ca38bd6361c/92dc873/gio/src/application.rs:29
#61 0x0000555555567b6a in gio::application::ApplicationExtManual::run<gtk4::auto::application::Application> (self=0x7fffffffdad8)
    at /home/slomo/.cargo/git/checkouts/gtk-rs-core-7be42ca38bd6361c/92dc873/gio/src/application.rs:22
#62 0x000055555556c9b9 in foo::main () at foo/src/main.rs:17

@sdroege
Copy link
Member

sdroege commented Apr 3, 2024

@sdroege sdroege closed this as completed Apr 3, 2024
@benjamin-otte
Copy link

    selection_model.connect_items_changed(|selection, _, _, _| {
        selection.set_selected(0)
    });

This is going to cause reentrancy and therefor isn't allowed.

The code is triggering a selection change while the previous item change has not finished propagating to all other change handlers.

@keirlawson
Copy link
Author

Ok, what is the correct approach to trigger a selection change on item change?

@benjamin-otte
Copy link

The absolutely best and most correct way is to write a SelectionModel that does what you want. GtkSingleSelection with does trigger a selection change on item change, just not the one you want I guess.

The less involved but somewhat hacky way would be to add a new main loop source via g_idle_add or so that runs after the items-changed has finished.

And the very-hacky-but-maybe-good-enough-i-hope-it-doesnt-break method would be to connect_after() to items-changed.

Also, I have no idea why you want to trigger a selection-changed - do you just want set_autoselect()?

@keirlawson
Copy link
Author

The use case is I am writing a notes app where I want to keep the current note "selected" so long as the note list is not filtered such that it is not visible, and restore that selection when the filter is cleared such that it is visible again

@benjamin-otte
Copy link

I wrote some code for a FixedSelection for that use case and people have asked about it a few times, but nobody ended up using it so we didn't merge it. https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4140 has the code.

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

3 participants