After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 347273 - Segfault when removing a row during a foreach - only with glade
Segfault when removing a row during a foreach - only with glade
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Widget: GtkTreeView
2.10.x
Other All
: Normal normal
: ---
Assigned To: gtktreeview-bugs
gtktreeview-bugs
Depends on:
Blocks:
 
 
Reported: 2006-07-12 02:23 UTC by Stefano Maggiolo
Modified: 2006-09-16 14:29 UTC
See Also:
GNOME target: ---
GNOME version: 2.13/2.14


Attachments
The glade file of the bug (2.13 KB, application/x-glade)
2006-07-14 21:45 UTC, Stefano Maggiolo
Details

Description Stefano Maggiolo 2006-07-12 02:23:12 UTC
Steps to reproduce:
1. Create a glade project with a window and a treeview inside it.
2. Run this code:

import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade
import gobject

gladefile = "bug.glade"
w = gtk.glade.XML(gladefile)

treeview1 = w.get_widget("treeview1")
model = gtk.ListStore(gobject.TYPE_STRING)
treeview1.set_model(model)
col = gtk.TreeViewColumn("One", gtk.CellRendererText(), text = 0)
col.set_visible(True)
treeview1.append_column(col)
model.append(["a"])
model.append(["b"])
model.append(["c"])

def f(model, path, iter, data):
    print data
    if model.get_value(iter, 0) == data:
        print "ciao"
        model.remove(iter)

model.foreach(f, "c")

gtk.main()


Stack trace:

Thread 1 (Thread -1211681104 (LWP 17843))

  • #0 gtk_scrolled_window_new
    from /usr/lib/libgtk-x11-2.0.so.0
  • #1 ??
  • #2 ??
  • #3 ??
  • #4 gtk_scrolled_window_new
    from /usr/lib/libgtk-x11-2.0.so.0
  • #5 ??
  • #6 g_param_spec_types
    from /usr/lib/libgobject-2.0.so.0
  • #7 ??
  • #8 g_type_interface_peek
    from /usr/lib/libgobject-2.0.so.0
  • #9 gtk_list_store_get_type
    from /usr/lib/libgtk-x11-2.0.so.0
  • #10 gtk_list_store_set_value
    from /usr/lib/libgtk-x11-2.0.so.0
  • #11 ??
  • #12 ??
  • #13 g_param_spec_types
    from /usr/lib/libgobject-2.0.so.0
  • #14 ??
    from /usr/lib/libgtk-x11-2.0.so.0
  • #15 ??
    from /usr/lib/libgtk-x11-2.0.so.0
  • #16 ??
  • #17 ??
  • #18 gtk_tree_model_iter_next
    from /usr/lib/libgtk-x11-2.0.so.0
  • #19 ??
  • #20 ??
  • #21 ??
  • #22 ??
    from /usr/lib/libgtk-x11-2.0.so.0
  • #23 ??
  • #24 ??
  • #25 ??
  • #26 gtk_tree_model_iter_next
    from /usr/lib/libgtk-x11-2.0.so.0
  • #27 ??
  • #28 ??
  • #29 ??
  • #30 ??
  • #31 ??
  • #32 ??
  • #33 ??
  • #34 ??


Other information:
If I create window and treeview directly from python, the bug doesn't occur.
Comment 1 Gustavo Carneiro 2006-07-12 15:01:07 UTC
The problem appears to be that foreach does not stop when you delete the last row in the last iteration; instead it tries to keep iterating over the end of the list.  Doesn't look like something glade related.  Over to gtk+.
Comment 2 Kristian Rietveld 2006-07-14 21:33:34 UTC
Could you also attach "bug.glade", so I can get the test case running?
Comment 3 Stefano Maggiolo 2006-07-14 21:45:48 UTC
Created attachment 68949 [details]
The glade file of the bug

Here is it.
Comment 4 Kristian Rietveld 2006-07-14 22:20:54 UTC
Thanks, that helped ;)


The problem is that gtk_list_store_remove() will change the iter (it sets iter->stamp to zero, so that the iterator is marked as invalid).  But the python marshal function for GtkTreeModelForeachFuncs copies the iter, and the iter is not copied back after running the user defined foreach func.  So gtk_tree_model_foreach() does not get the updated, now invalid, iterator and gtk_tree_model_iter_next() still succeeds.

According to Johan it's probably better to not copy the iter at all.  Anyway, reassinging back to PyGtk.
Comment 5 Stefano Maggiolo 2006-07-14 22:38:22 UTC
Ok, I know that that wasn't the right way to remove some items (I switched to the right way right after), but it shoudn't segfault, as far as I know :)
Comment 6 John Finlay 2006-07-18 01:33:25 UTC
After a quick look it appears that there are several places where the TreeIter should not be copied:

pygtk_cell_data_func_marshal, 
pygtk_tree_selection_foreach_marshal, 
pygtk_tree_sortable_sort_cb, pygtk_tree_foreach_marshal, pygtk_tree_model_filter_visible_cb, 
pygtk_filter_modify_func_marshal, 
pygtk_set_search_equal_func_marshal, 
pygtk_set_row_separator_func_marshal

All these should probably wrap the incoming TreeIter as follows:

pyg_boxed_new(GTK_TYPE_TREE_ITER, iter, FALSE, FALSE)
Comment 7 Johan (not receiving bugmail) Dahlin 2006-07-18 01:42:53 UTC
It would be good to have a test for this, so we can verify that this continues to work in the future. Since apparently nobody tried to do something that changes the iter in any of these callbacks.
Comment 8 John Finlay 2006-07-18 07:39:21 UTC
* gtk/gtktreeview.override (pygtk_cell_data_func_marshal)
	(pygtk_tree_selection_foreach_marshal, pygtk_tree_sortable_sort_cb)
	(pygtk_tree_foreach_marshal, pygtk_tree_model_filter_visible_cb)
	(pygtk_filter_modify_func_marshal)
	(pygtk_set_search_equal_func_marshal): Don't copy TreeIter when passing
	to callback. #347273 (Stefano Maggiolo)

Checking in gtk/gtktreeview.override;
/cvs/gnome/pygtk/gtk/gtktreeview.override,v  <--  gtktreeview.override
new revision: 1.70; previous revision: 1.69
done


Now back to GTK to fix the bug that the foreach function doesn't properly handle the side effect of gtk_list_store_remove on the TreeIter.

e.g.

model = gtk.ListStore(str)
model.append(['a'])
model.append(['b'])
model.append(['c'])
model.append(['d'])
model.append(['e'])
def f(m,p,i):
  print m[i][0], p
  m.remove[i]
  return False
model.foreach(f)

produces:

a (0,)
c (1,)
e (2,)
Comment 9 Gustavo Carneiro 2006-07-18 09:59:20 UTC
For the record, pyg_boxed_new(GTK_TYPE_TREE_ITER, iter, FALSE, FALSE) is slightly dangerous and I wish we didn't need to use it; it creates a PyGBoxed wrapper for a boxed type it doesn't own.  It is possible for the callback to keep the boxed wrapper around, even after the original boxed object itself has been destroyed, with potentially dangerous effects if the boxed wrapper is used later.

This problem already appeared in signal handlers, and jpe solved it by copying all boxed types after the signal handler in case their refcount > 1, in pygtype.c pyg_signal_class_closure_marshal():

    /* Copy boxed values if others ref them, this needs to be done regardless of
       exception status. */
    len = PyTuple_Size(params);    
    for (i = 0; i < len; i++) {
	PyObject *item = PyTuple_GetItem(params, i);
	if (item != NULL && PyObject_TypeCheck(item, &PyGBoxed_Type)
	    && item->ob_refcnt != 1) {
	    PyGBoxed* boxed_item = (PyGBoxed*)item;
	    if (!boxed_item->free_on_dealloc) {
		boxed_item->boxed = g_boxed_copy(boxed_item->gtype, boxed_item->boxed);
		boxed_item->free_on_dealloc = TRUE;
	    }
	}
    }

It might be worth to export a pygobject API, like pyg_boxed_copy_if_alive(), which would copy the boxed if refcnt > 1; then we'd need to call this function for all boxed types created with pyg_boxed_new(TYPE, boxed, FALSE, FALSE) and passed into user code that might keep them alive.
Comment 10 Gustavo Carneiro 2006-09-16 13:24:56 UTC
Grr! I knew this was a bad idea :|

I am almost sure this change is causing this bug: https://launchpad.net/distros/ubuntu/+source/revelation/+bug/56744

The affected code is:

	def get_selected(self):
		"Get a list of currently selected rows"

		list = []
		self.selection.selected_foreach(lambda model, path, iter: list.append(iter))

		return list

(where self.selection is a gtk.TreeSelection object)

The PyGTk wrapper was modified in the course of fixing this bug:

1.70         (finlay   18-Jul-06):     py_iter = pyg_boxed_new(GTK_TYPE_TREE_ITER, iter, FALSE, FALSE);

So what I predicted a few months ago is actually happening: python code is keeping an iterator around, but this is an PyGtkTreeIter wrapper that contains shared pointer to a GtkTreeIter that is destroyed when it goes out of scope.
Comment 11 Gustavo Carneiro 2006-09-16 14:29:00 UTC
I have confirmed then fixed the bug.

Checking in ChangeLog;
/cvs/gnome/pygtk/ChangeLog,v  <--  ChangeLog
new revision: 1.1615; previous revision: 1.1614
done
Checking in gtk/gtkobject-support.c;
/cvs/gnome/pygtk/gtk/gtkobject-support.c,v  <--  gtkobject-support.c
new revision: 1.23; previous revision: 1.22
done
Checking in gtk/gtktreeview.override;
/cvs/gnome/pygtk/gtk/gtktreeview.override,v  <--  gtktreeview.override
new revision: 1.74; previous revision: 1.73
done
Checking in gtk/pygtk-private.h;
/cvs/gnome/pygtk/gtk/pygtk-private.h,v  <--  pygtk-private.h
new revision: 1.34; previous revision: 1.33
done
Checking in gtk/pygtkcellrenderer.c;
/cvs/gnome/pygtk/gtk/pygtkcellrenderer.c,v  <--  pygtkcellrenderer.c
new revision: 1.13; previous revision: 1.12
done