[glib] GHash: make sets with refcounted keys work correctly



commit d09df426ba3788174b5bcc974831651208a13ea2
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun May 1 10:55:24 2011 -0400

    GHash: make sets with refcounted keys work correctly
    
    When keys == values, we have to be careful about the order in
    which we replace their elements.

 glib/ghash.c      |    6 ++--
 glib/tests/hash.c |   93 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+), 3 deletions(-)
---
diff --git a/glib/ghash.c b/glib/ghash.c
index 006f2e5..8b31a59 100644
--- a/glib/ghash.c
+++ b/glib/ghash.c
@@ -982,6 +982,9 @@ g_hash_table_insert_internal (GHashTable *hash_table,
 
   if (HASH_IS_REAL (old_hash))
     {
+      if (hash_table->value_destroy_func)
+        hash_table->value_destroy_func (hash_table->values[node_index]);
+
       if (keep_new_key)
         {
           if (hash_table->key_destroy_func)
@@ -994,9 +997,6 @@ g_hash_table_insert_internal (GHashTable *hash_table,
             hash_table->key_destroy_func (key);
         }
 
-      if (hash_table->value_destroy_func)
-        hash_table->value_destroy_func (hash_table->values[node_index]);
-
       hash_table->values[node_index] = value;
     }
   else
diff --git a/glib/tests/hash.c b/glib/tests/hash.c
index 5d47894..2aedece 100644
--- a/glib/tests/hash.c
+++ b/glib/tests/hash.c
@@ -733,6 +733,98 @@ test_remove_all (void)
   g_hash_table_unref (h);
 }
 
+typedef struct {
+  gint ref_count;
+  const gchar *key;
+} RefCountedKey;
+
+static guint
+hash_func (gconstpointer key)
+{
+  const RefCountedKey *rkey = key;
+
+  return g_str_hash (rkey->key);
+}
+
+static gboolean
+eq_func (gconstpointer a, gconstpointer b)
+{
+  const RefCountedKey *aa = a;
+  const RefCountedKey *bb = b;
+
+  return g_strcmp0 (aa->key, bb->key) == 0;
+}
+
+static void
+key_unref (gpointer data)
+{
+  RefCountedKey *key = data;
+
+  g_assert (key->ref_count > 0);
+
+  key->ref_count -= 1;
+
+  if (key->ref_count == 0)
+    g_free (key);
+}
+
+static RefCountedKey *
+key_ref (RefCountedKey *key)
+{
+  key->ref_count += 1;
+
+  return key;
+}
+
+static RefCountedKey *
+key_new (const gchar *key)
+{
+  RefCountedKey *rkey;
+
+  rkey = g_new (RefCountedKey, 1);
+
+  rkey->ref_count = 1;
+  rkey->key = key;
+
+  return rkey;
+}
+
+static void
+set_ref_hash_test (void)
+{
+  GHashTable *h;
+  RefCountedKey *key1;
+  RefCountedKey *key2;
+
+  h = g_hash_table_new_full (hash_func, eq_func, key_unref, key_unref);
+
+  key1 = key_new ("a");
+  key2 = key_new ("a");
+
+  g_assert_cmpint (key1->ref_count, ==, 1);
+  g_assert_cmpint (key2->ref_count, ==, 1);
+
+  g_hash_table_insert (h, key_ref (key1), key_ref (key1));
+
+  g_assert_cmpint (key1->ref_count, ==, 3);
+  g_assert_cmpint (key2->ref_count, ==, 1);
+
+  g_hash_table_replace (h, key_ref (key2), key_ref (key2));
+
+  g_assert_cmpint (key1->ref_count, ==, 1);
+  g_assert_cmpint (key2->ref_count, ==, 3);
+
+  g_hash_table_remove (h, key1);
+
+  g_assert_cmpint (key1->ref_count, ==, 1);
+  g_assert_cmpint (key2->ref_count, ==, 1);
+
+  g_hash_table_unref (h);
+
+  key_unref (key1);
+  key_unref (key2);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -748,6 +840,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/hash/double", double_hash_test);
   g_test_add_func ("/hash/string", string_hash_test);
   g_test_add_func ("/hash/set", set_hash_test);
+  g_test_add_func ("/hash/set-ref", set_ref_hash_test);
   g_test_add_func ("/hash/ref", test_hash_ref);
   g_test_add_func ("/hash/remove-all", test_remove_all);
 



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