From d09df426ba3788174b5bcc974831651208a13ea2 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 1 May 2011 10:55:24 -0400 Subject: [PATCH 1/1] 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); -- 2.7.4