Re: Speedup insert into treestore



Congratulations!  You've found a reference-handling bug in Glib::Object!

On Oct 11, 2005, at 1:46 AM, Florian Schaefer wrote:

My model has been created like this:

$model = Gtk2::TreeStore->new('Gtk2::Gdk::Pixbuf', 'Gtk2::Gdk::Pixbuf', 'Gtk2::Gdk::Pixbuf', 'Glib::String', 'Glib::String', 'Glib::String', 'Glib::String', 'Glib::String', 'Glib::Uint', 'Glib::Boolean', 'Glib::Uint', 'Gtk2::Gdk::Pixbuf');

If I do a print $model after having it recovered from the TreeView, Perl
gives me Gtk2::TreeStore=HASH(0x82af694). So everything looks like a
normel TreeStore to me.

Okay, i tried creating a very simple testcase:

  use Gtk2 -init;

  sub make_model {
      my $model = Gtk2::TreeStore->new ('Glib::String');
      $model->set_sort_column_id (0, 'ascending');
      warn "$model";
      return $model;
  }

  my $view = Gtk2::TreeView->new ();
  $view->set_model (make_model ());
  $view->get_model->set_sort_column_id (3, 'ascending');

  {
      my $model = $view->get_model;
      warn "$model";
      $view->set_model (undef);
      warn "$model";
      my @cursort = $model->get_sort_column_id ();
      $model->set_sort_column_id (-2, 'descending');
      $model->clear;
      # ...repopulate...
      $model->set_sort_column_id (@cursort);
      $view->set_model ($model);
  }

and, lo and behold, this example prints the following:

  Gtk2::TreeStore=HASH(0x9af0ca8) at florian.pl line 6.
  Gtk2::TreeStore=HASH(0x9af0ca8) at florian.pl line 16.
  Gtk2::TreeStore=HASH(0x9af0ca8) at florian.pl line 18.
Gtk-CRITICAL **: gtk_tree_sortable_get_sort_column_id: assertion `GTK_IS_TREE_SORTABLE (sortable)' failed at florian.pl line 19. Gtk-CRITICAL **: gtk_tree_sortable_set_sort_column_id: assertion `GTK_IS_TREE_SORTABLE (sortable)' failed at florian.pl line 20. Gtk-CRITICAL **: gtk_tree_store_clear: assertion `GTK_IS_TREE_STORE (tree_store)' failed at florian.pl line 21. Usage: Gtk2::TreeSortable::set_sort_column_id(sortable, sort_column_id, order) at florian.pl line 23.


From what i can gather, it goes like this:

1. gtk_tree_store_new() creates the model with refcount 1.
2. Gtk2::TreeStore::new() takes ownership of that object. refcount is 1, perl wrapper attached and returned as a mortal SV. 3. gtk_tree_view_set_model() adds a ref to the C object. recount is now 2. 4. The temp SV goes out of scope, and Glib::Object::DESTROY swaps its C object reference over to the perl object. C refcount is now 1, held by the TreeView.

later:
5. Gtk2::TreeView::get_model() calls gtk_tree_view_get_model(), and finds the model object with refcount 1. 6. Gtk2::TreeView::get_model() calls gperl_get_object() to wrap the C object; it discovers the existing wrapper SV, so returns that. C object reference count is still 1. 7. perl code traps the SV from Gtk2::TreeView::get_model() and SvREFCNT_inc()s it. C object reference count is unchanged. 8. $view->set_model(undef) calls gtk_tree_view_set_model(view, NULL). The treeview sees that it already has a model, so it calls g_object_unref() on it. the model has a C refcount of 1 and an attached SV wrapper with refcount >1. 9. g_object_unref() sees that the model has a C refcount of 1 and that it is releasing the last C reference. ***So, it destroys the object.***

It's at this point that things blow up. The C object doesn't know that there is a perl wrapper attached, so it happily dies, assuming it has had a full-enough life. The perl wrapper object is left pointing to freed memory, which is why you keep getting assertions that your apple is not an apple -- it's actually a ghost apple.


The solution here will be some form of GObject trickery in the Glib binding, but i'm very late for work and have to leave it here.

I'm perturbed that i haven't seen this sort of thing before, because this is a pretty nasty bug. The situation going the other way is quite well handled by the reference-swapping in Glib::Object::DESTROY, but this is a different situation; we need a destruction notification so that we can keep the object from being destroyed in g_object_unref(). I know there's a way to do this, but finding it will require digging in the source for a couple of hours...

As a workaround, keep your own perl reference to the value returned from Gtk2::TreeStore::new, to prevent Glib::Object::DESTROY from releasing that C reference.
In the example code above, that's as simple as

  my $view = Gtk2::TreeView->new;
# XXX don't let $the_model go out of scope until you're *really* finished with it.
  my $the_model = make_model ();
  $view->set_model ($the_model);
  $view->get_model->set_sort_column_id (3, 'ascending');



--
Holy crap, dude, we have kids!
    -- Elysse, six days after giving birth to twins




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]