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 301558 - GtkTreeModelSort do not clean it's iterator cache correctly when used together with GtkTreeModelFilter.
GtkTreeModelSort do not clean it's iterator cache correctly when used togethe...
Status: RESOLVED DUPLICATE of bug 300089
Product: gtk+
Classification: Platform
Component: Widget: GtkTreeView
2.6.x
Other All
: Normal normal
: ---
Assigned To: gtktreeview-bugs
gtktreeview-bugs
Depends on:
Blocks:
 
 
Reported: 2005-04-22 08:27 UTC by Markku Vire
Modified: 2005-07-20 09:35 UTC
See Also:
GNOME target: ---
GNOME version: 2.7/2.8


Attachments
Patch to fix internal reference counting issues. (3.05 KB, patch)
2005-04-25 10:31 UTC, Markku Vire
none Details | Review

Description Markku Vire 2005-04-22 08:27:00 UTC
Please describe the problem:
GtkTreeModel sort stores invalid iterators when using a stack of tree models.
This causes various Gtk-Criticals and other messages to appear. For example:

There is a disparity between the internal view of the GtkTreeView,
and the GtkTreeModel.  This generally means that the model has changed
without letting the view know.  Any display from now on is likely to
be incorrect.


Steps to reproduce:
Run the following test program:

#include <gtk/gtk.h>

static gint sorter(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer
data)
{
  gint i, j;

  gtk_tree_model_get(model, a, 0, &i, -1);
  gtk_tree_model_get(model, b, 0, &j, -1);

  return j - i;
}

static void row_inserted(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter
*iter, gpointer data)
{
  gchar *s;

  s = gtk_tree_path_to_string(path);
  g_print("Inserted %s\n", s);
  g_free(s);
}

static void row_deleted(GtkTreeModel *model, GtkTreePath *path, gpointer data)
{
  gchar *s;

  s = gtk_tree_path_to_string(path);
  g_print("Deleted %s\n", s);
  g_free(s);
}

static gboolean timeout_func(gpointer data)
{
  GtkTreeStore *tree;
  GtkTreeIter root, iter;

  static gboolean add = TRUE;

  tree = GTK_TREE_STORE(data);
  if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(tree), &root))
    g_assert_not_reached();

  if (add)
  {
    gtk_tree_store_append(tree, &iter, &root);
    gtk_tree_store_set(tree, &iter, 0, 456, 1, TRUE, -1);
  }
  else
  {
    gint n;
    n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(tree), &root);
    g_print("%d children in main model, deleting last one\n", n);
    gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(tree), &iter, &root, n-1);
    gtk_tree_store_remove(tree, &iter);
  }

  add = !add;

  return TRUE;
}

int main(int argc, char *argv[])
{
  GtkTreeStore *tree;
  GtkTreeIter iter, iter2;
  GtkWidget *window, *view;
  GtkTreeModel *sort, *filter;
  GtkTreeViewColumn *col;
  GtkCellRenderer *renderer;

  gtk_init(&argc, &argv);

  tree = gtk_tree_store_new(2, G_TYPE_INT, G_TYPE_BOOLEAN);
  gtk_tree_store_append(tree, &iter, NULL);
  gtk_tree_store_set(tree, &iter, 0, 123, 1, TRUE, -1);
  gtk_tree_store_append(tree, &iter2, &iter);
  gtk_tree_store_set(tree, &iter2, 0, 73, 1, TRUE, -1);

  sort = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(tree));
  gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(sort), sorter, NULL,
NULL);

  filter = gtk_tree_model_filter_new(sort, NULL);
  gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(filter), 1);

  g_signal_connect(sort, "row-inserted", G_CALLBACK(row_inserted), NULL);
  g_signal_connect(sort, "row-deleted", G_CALLBACK(row_deleted), NULL);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  view = gtk_tree_view_new_with_model(filter);

  col = gtk_tree_view_column_new();
  renderer = gtk_cell_renderer_text_new();
  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
  gtk_tree_view_column_pack_start(col, renderer, TRUE);
  gtk_tree_view_column_add_attribute(col, renderer, "text", 0);

  gtk_container_add(GTK_CONTAINER(window), view);
  gtk_widget_show_all(window);

  g_timeout_add(1000, timeout_func, tree);

  gtk_main();

  return 0;
}


Actual results:
Let the program run for a while. Notice that tree paths that are printed out
will increase their indices every time. Try to expand root folder. Depending on
when did you try that you can see various amount of items inside (though at most
2 should be visible). Or perhaps you cannot expand the root at all.

Expected results:
Root should be expanded and I would see 1 or 2 items under it.

Does this happen every time?
Yes. The only difference is the timing when you try to expand the root item. If
you do that quicly enough, then nothing wrong happens. After you see the nodes
everything works ok.

Other information:
This problem happens only when using sort model together with filter model. I
assume that following piece of code from gtktreemodelsort.c
(gtk_tree_model_sort_row_deleted function) do not work correctly.

   while (elt->ref_count > 0)
    gtk_tree_model_sort_real_unref_node (GTK_TREE_MODEL (data), &iter, FALSE);

  if (level->ref_count == 0)
    {
      /* This will prune the level, so I can just emit the signal and not worry
       * about cleaning this level up. */
      gtk_tree_model_sort_increment_stamp (tree_model_sort);
      gtk_tree_path_free (path);
      if (level == tree_model_sort->root)
        tree_model_sort->root = NULL;

      return;
    }

In a problem cases level->ref_count is 0 both before and after the while loop.
If whe exit in this case then the garbage starts to pile up. Anyway, just to do
the rest of the function to be executed each time gives me segfault when I try
to collapse the root again.
Comment 1 Markku Vire 2005-04-22 12:22:12 UTC
It's also possible that GtkTreeModel sort uses invalid iterators.
increment_stamp calls clear_cache and after that all unreffed iterators are invalid.
Comment 2 Markku Vire 2005-04-22 14:33:36 UTC
Actually this seems to be internal reference counting issue.
tree_model_sort->zero_ref_counts equals to zero, so clear_cache is not called at
all, so the level remains.
Comment 3 Markku Vire 2005-04-25 10:31:08 UTC
Created attachment 45643 [details] [review]
Patch to fix internal reference counting issues.

The real cause seemed to be flaws in internal reference counting. This caused
that cache levels we not freed even in a case they should. This patch fixed at
least the original testcase.
Comment 4 Kristian Rietveld 2005-07-20 09:35:13 UTC
Looks like the patch in #300089 fixes this issue. Marking as duplicate.

*** This bug has been marked as a duplicate of 300089 ***