treemodelfilter: Introduce notion of external ref count

We need to distinguish between the ref count objects have on us versus
the ref count we have on our child model.  To keep track of the former,
we introduce the "external ref count" in this commit.  The zero_ref_count
needs to be determined from the external ref count, because objects that
have a ref count on us have say in which levels must be cached and which
can be released.

Before the caching in GtkTreeModelFilter was essentially broken and
levels were never released.  This was caused because the zero_ref_count
was connected to the ref count the filter model had on its child model.
Now that this depends on the external ref count, this is working fine and
as to be expected.
This commit is contained in:
Kristian Rietveld 2011-06-26 22:04:05 +02:00
parent 058a9090c2
commit dc15124c6f

View File

@ -93,6 +93,7 @@ struct _FilterElt
FilterLevel *children;
gint offset;
gint ref_count;
gint ext_ref_count;
gint zero_ref_count;
gboolean visible;
};
@ -101,6 +102,7 @@ struct _FilterLevel
{
GArray *array;
gint ref_count;
gint ext_ref_count;
gint visible_nodes;
gint parent_elt_index;
@ -287,8 +289,12 @@ static gboolean gtk_tree_model_filter_visible (GtkTr
static void gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter,
FilterLevel *level);
static void gtk_tree_model_filter_real_ref_node (GtkTreeModel *model,
GtkTreeIter *iter,
gboolean external);
static void gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
GtkTreeIter *iter,
gboolean external,
gboolean propagate_unref);
static void gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
@ -572,6 +578,7 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
sizeof (FilterElt),
length);
new_level->ref_count = 0;
new_level->ext_ref_count = 0;
new_level->visible_nodes = 0;
new_level->parent_elt_index = parent_elt_index;
new_level->parent_level = parent_level;
@ -606,6 +613,7 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
filter_elt.offset = i;
filter_elt.zero_ref_count = 0;
filter_elt.ref_count = 0;
filter_elt.ext_ref_count = 0;
filter_elt.children = NULL;
filter_elt.visible = TRUE;
@ -620,7 +628,8 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1));
if (new_level->parent_level || filter->priv->virtual_root)
gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter),
&f_iter, FALSE);
if (emit_inserted)
{
@ -657,6 +666,7 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
filter_elt.offset = 0;
filter_elt.zero_ref_count = 0;
filter_elt.ref_count = 0;
filter_elt.ext_ref_count = 0;
filter_elt.children = NULL;
filter_elt.visible = FALSE;
@ -669,7 +679,8 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
f_iter.user_data = new_level;
f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1));
gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), &f_iter,
FALSE);
}
else if (new_level->array->len == 0)
gtk_tree_model_filter_free_level (filter, new_level, TRUE);
@ -700,11 +711,12 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
f_iter.user_data = filter_level;
f_iter.user_data2 = &(g_array_index (filter_level->array, FilterElt, i));
gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &f_iter);
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&f_iter, FALSE, TRUE);
}
}
if (filter_level->ref_count == 0)
if (filter_level->ext_ref_count == 0)
{
FilterLevel *parent_level = filter_level->parent_level;
gint parent_elt_index = filter_level->parent_elt_index;
@ -879,7 +891,8 @@ gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter,
gtk_tree_model_filter_clear_cache_helper (filter, g_array_index (level->array, FilterElt, i).children);
}
if (level->ref_count == 0 && level != filter->priv->root)
if (level->ext_ref_count == 0 && level != filter->priv->root &&
level->parent_level->ext_ref_count == 0)
{
gtk_tree_model_filter_free_level (filter, level, TRUE);
return;
@ -1119,6 +1132,7 @@ gtk_tree_model_filter_insert_elt_in_level (GtkTreeModelFilter *filter,
elt.offset = offset;
elt.zero_ref_count = 0;
elt.ref_count = 0;
elt.ext_ref_count = 0;
elt.children = NULL;
/* visibility should be FALSE as we don't emit row_inserted */
elt.visible = FALSE;
@ -1165,7 +1179,8 @@ gtk_tree_model_filter_insert_elt_in_level (GtkTreeModelFilter *filter,
f_iter.user_data = level;
f_iter.user_data2 = &g_array_index (level->array, FilterElt, *index);
gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), &f_iter,
FALSE);
}
return &g_array_index (level->array, FilterElt, *index);
@ -1271,7 +1286,8 @@ gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter,
/* we distinguish a couple of cases:
* - root level, length > 1: emit row-deleted and remove.
* - root level, length == 1: emit row-deleted and keep in cache.
* - level, length == 1: parent->ref_count > 1: emit row-deleted and keep.
* - level, length == 1: parent->ext_ref_count > 0: emit row-deleted
* and keep.
* - level, length > 1: emit row-deleted and remove.
* - else, remove level.
*
@ -1295,18 +1311,22 @@ gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter,
if (elt->children)
gtk_tree_model_filter_free_level (filter, elt->children, TRUE);
while (elt->ext_ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, TRUE, FALSE);
while (elt->ref_count > 1)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, FALSE);
&iter, FALSE, FALSE);
/* We must account for the filter model's reference, because the
* node is still present in the child model.
*/
if (parent_level || filter->priv->virtual_root)
gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &iter);
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), &iter,
FALSE, TRUE);
else if (elt->ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, FALSE);
&iter, FALSE, FALSE);
/* remove the node */
tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
@ -1328,9 +1348,14 @@ gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter,
}
gtk_tree_model_filter_increment_stamp (filter);
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
/* Only if the node is in the root level (parent == NULL) or
* the parent is visible, a row-deleted signal is necessary.
*/
if (!parent || parent->ext_ref_count > 0)
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
}
else if ((length == 1 && parent && parent->ref_count > 1)
else if ((length == 1 && parent && parent->ext_ref_count > 0)
|| (length == 1 && level == filter->priv->root))
{
/* We emit row-deleted, but keep the node in the cache and
@ -1352,9 +1377,12 @@ gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter,
* in gtk_tree_model_filter_free_level), because the node is
* still present in the child model.
*/
while (elt->ext_ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, TRUE, FALSE);
while (elt->ref_count > 1)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, FALSE);
&iter, FALSE, FALSE);
/* Blow level away, including any child levels */
gtk_tree_model_filter_free_level (filter, level, TRUE);
@ -1535,6 +1563,8 @@ gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter,
signals_emitted = TRUE;
}
gtk_tree_model_filter_increment_stamp (filter);
/* We need to disallow to build new levels, because we are then pulling
* in a child in an invisible level. We only want to find path if it
* is in a visible level (and thus has a parent that is visible).
@ -1548,8 +1578,6 @@ gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter,
/* parent is probably being filtered out */
return;
gtk_tree_model_filter_increment_stamp (filter);
gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path);
level = FILTER_LEVEL (iter.user_data);
@ -2227,8 +2255,12 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
* model's references on the node in case of level->parent or use
* of a virtual root are automatically destroyed by the child model.
*/
while (elt->ext_ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
TRUE, FALSE);
while (elt->ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, FALSE);
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
FALSE, FALSE);
if (level->array->len == 1)
{
@ -3066,6 +3098,14 @@ gtk_tree_model_filter_iter_parent (GtkTreeModel *model,
static void
gtk_tree_model_filter_ref_node (GtkTreeModel *model,
GtkTreeIter *iter)
{
gtk_tree_model_filter_real_ref_node (model, iter, TRUE);
}
static void
gtk_tree_model_filter_real_ref_node (GtkTreeModel *model,
GtkTreeIter *iter,
gboolean external)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
GtkTreeIter child_iter;
@ -3085,22 +3125,29 @@ gtk_tree_model_filter_ref_node (GtkTreeModel *model,
elt->ref_count++;
level->ref_count++;
if (level->ref_count == 1)
if (external)
{
FilterLevel *parent_level = level->parent_level;
gint parent_elt_index = level->parent_elt_index;
elt->ext_ref_count++;
level->ext_ref_count++;
/* we were at zero -- time to decrease the zero_ref_count val */
while (parent_level)
if (level->ext_ref_count == 1)
{
g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count--;
FilterLevel *parent_level = level->parent_level;
gint parent_elt_index = level->parent_elt_index;
parent_elt_index = parent_level->parent_elt_index;
parent_level = parent_level->parent_level;
/* we were at zero -- time to decrease the zero_ref_count val */
while (parent_level)
{
g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count--;
parent_elt_index = parent_level->parent_elt_index;
parent_level = parent_level->parent_level;
}
if (filter->priv->root != level)
filter->priv->zero_ref_count--;
}
if (filter->priv->root != level)
filter->priv->zero_ref_count--;
}
}
@ -3108,12 +3155,13 @@ static void
gtk_tree_model_filter_unref_node (GtkTreeModel *model,
GtkTreeIter *iter)
{
gtk_tree_model_filter_real_unref_node (model, iter, TRUE);
gtk_tree_model_filter_real_unref_node (model, iter, TRUE, TRUE);
}
static void
gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
GtkTreeIter *iter,
gboolean external,
gboolean propagate_unref)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
@ -3138,22 +3186,29 @@ gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
elt->ref_count--;
level->ref_count--;
if (level->ref_count == 0)
if (external)
{
FilterLevel *parent_level = level->parent_level;
gint parent_elt_index = level->parent_elt_index;
elt->ext_ref_count--;
level->ext_ref_count--;
/* we are at zero -- time to increase the zero_ref_count val */
while (parent_level)
if (level->ext_ref_count == 0)
{
g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count++;
FilterLevel *parent_level = level->parent_level;
gint parent_elt_index = level->parent_elt_index;
parent_elt_index = parent_level->parent_elt_index;
parent_level = parent_level->parent_level;
/* we are at zero -- time to increase the zero_ref_count val */
while (parent_level)
{
g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count++;
parent_elt_index = parent_level->parent_elt_index;
parent_level = parent_level->parent_level;
}
if (filter->priv->root != level)
filter->priv->zero_ref_count++;
}
if (filter->priv->root != level)
filter->priv->zero_ref_count++;
}
}