[evolution-data-server] Camel string pool improvements.



commit c57820537cb97f36c28aa880cd41d29a1b87b0c2
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sun Jun 24 17:28:06 2012 -0400

    Camel string pool improvements.
    
    Keep the pointer to the allocated string and the allocated string's
    reference count together in a new StringPoolNode struct.  This way a
    StringPoolNode can be used as both the key and value in a GHashTable,
    allowing the table to save some memory.  Also use the slab allocator
    for the fixed-size StringPoolNode instances.
    
    Didn't do any real formal measurements for this so the actual reduction
    in memory usage may be insignificant.  If nothing else the code is more
    readable now, and manipulating reference counts is less awkward.

 camel/camel-string-utils.c |  269 +++++++++++++++++++++++++++-----------------
 camel/camel-string-utils.h |    8 +-
 2 files changed, 169 insertions(+), 108 deletions(-)
---
diff --git a/camel/camel-string-utils.c b/camel/camel-string-utils.c
index 417a9d6..0089fe5 100644
--- a/camel/camel-string-utils.c
+++ b/camel/camel-string-utils.c
@@ -141,172 +141,216 @@ gchar camel_toupper (gchar c)
 }
 
 /* working stuff for pstrings */
-static GStaticMutex pstring_lock = G_STATIC_MUTEX_INIT;
-static GHashTable *pstring_table = NULL;
+static GStaticMutex string_pool_lock = G_STATIC_MUTEX_INIT;
+static GHashTable *string_pool = NULL;
+
+typedef struct _StringPoolNode StringPoolNode;
+
+struct _StringPoolNode {
+	gchar *string;
+	gulong ref_count;
+};
+
+static StringPoolNode *
+string_pool_node_new (gchar *string)
+{
+	StringPoolNode *node;
+
+	node = g_slice_new (StringPoolNode);
+	node->string = string;  /* takes ownership */
+	node->ref_count = 1;
+
+	return node;
+}
+
+static void
+string_pool_node_free (StringPoolNode *node)
+{
+	g_free (node->string);
+
+	g_slice_free (StringPoolNode, node);
+}
+
+static guint
+string_pool_node_hash (const StringPoolNode *node)
+{
+	return g_str_hash (node->string);
+}
+
+static gboolean
+string_pool_node_equal (const StringPoolNode *node_a,
+                        const StringPoolNode *node_b)
+{
+	return g_str_equal (node_a->string, node_b->string);
+}
+
+static void
+string_pool_init (void)
+{
+	if (G_UNLIKELY (string_pool == NULL))
+		string_pool = g_hash_table_new_full (
+			(GHashFunc) string_pool_node_hash,
+			(GEqualFunc) string_pool_node_equal,
+			(GDestroyNotify) string_pool_node_free,
+			(GDestroyNotify) NULL);
+}
 
 /**
  * camel_pstring_add:
- * @str: string to add to the string pool
- * @own: whether the string pool will own the memory pointed to by @str, if @str is not yet in the pool
+ * @string: string to add to the string pool
+ * @own: whether the string pool will own the memory pointed to by
+ *       @string, if @string is not yet in the pool
+ *
+ * Add @string to the pool.
  *
- * Add the string to the pool.
+ * The %NULL and empty strings are special cased to constant values.
  *
- * The NULL and empty strings are special cased to constant values.
+ * Unreference the returned string with camel_pstring_free().
  *
- * Returns: A pointer to an equivalent string of @s.  Use
- * camel_pstring_free() when it is no longer needed.
+ * Returns: a canonicalized copy of @string
  **/
 const gchar *
-camel_pstring_add (gchar *str,
+camel_pstring_add (gchar *string,
                    gboolean own)
 {
-	gpointer pcount;
-	gpointer pstr;
+	StringPoolNode static_node = { string, };
+	StringPoolNode *node;
+	const gchar *interned;
 
-	if (str == NULL)
+	if (string == NULL)
 		return NULL;
 
-	if (str[0] == '\0') {
+	if (*string == '\0') {
 		if (own)
-			g_free (str);
+			g_free (string);
 		return "";
 	}
 
-	g_static_mutex_lock (&pstring_lock);
-	if (pstring_table == NULL)
-		pstring_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+	g_static_mutex_lock (&string_pool_lock);
+
+	string_pool_init ();
 
-	if (g_hash_table_lookup_extended (pstring_table, str, &pstr, &pcount)) {
-		gint *count = pcount;
-		*count = (*count) + 1;
+	node = g_hash_table_lookup (string_pool, &static_node);
+
+	if (node != NULL) {
+		node->ref_count++;
 		if (own)
-			g_free (str);
+			g_free (string);
 	} else {
-		gint *count = g_new0 (gint, 1);
-		*count = 1;
-		pstr = own ? str : g_strdup (str);
-		g_hash_table_insert (pstring_table, pstr, count);
+		if (!own)
+			string = g_strdup (string);
+		node = string_pool_node_new (string);
+		g_hash_table_add (string_pool, node);
 	}
 
-	g_static_mutex_unlock (&pstring_lock);
+	interned = node->string;
+
+	g_static_mutex_unlock (&string_pool_lock);
 
-	return pstr;
+	return interned;
 }
 
 /**
  * camel_pstring_peek:
- * @str: string to fetch to the string pool
+ * @string: string to fetch from the string pool
  *
- * Add return the string from the pool.
+ * Returns the canonicalized copy of @string without increasing its
+ * reference count in the string pool.  If necessary, @string is first
+ * added to the string pool.
  *
- * The NULL and empty strings are special cased to constant values.
+ * The %NULL and empty strings are special cased to constant values.
  *
- * Returns: A pointer to an equivalent string of @s.  Use
- * camel_pstring_free() when it is no longer needed.
+ * Returns: a canonicalized copy of @string
  *
  * Since: 2.24
  **/
 const gchar *
-camel_pstring_peek (const gchar *str)
+camel_pstring_peek (const gchar *string)
 {
-	gpointer pcount;
-	gpointer pstr;
+	StringPoolNode static_node = { (gchar *) string, };
+	StringPoolNode *node;
+	const gchar *interned;
 
-	if (str == NULL)
+	if (string == NULL)
 		return NULL;
 
-	if (str[0] == '\0') {
+	if (*string == '\0')
 		return "";
-	}
 
-	g_static_mutex_lock (&pstring_lock);
-	if (pstring_table == NULL)
-		pstring_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+	g_static_mutex_lock (&string_pool_lock);
 
-	if (!g_hash_table_lookup_extended (pstring_table, str, &pstr, &pcount)) {
-		gint *count = g_new0 (gint, 1);
-		*count = 1;
-		pstr = g_strdup (str);
-		g_hash_table_insert (pstring_table, pstr, count);
+	string_pool_init ();
+
+	node = g_hash_table_lookup (string_pool, &static_node);
+
+	if (node == NULL) {
+		node = string_pool_node_new (g_strdup (string));
+		g_hash_table_add (string_pool, node);
 	}
 
-	g_static_mutex_unlock (&pstring_lock);
+	interned = node->string;
+
+	g_static_mutex_unlock (&string_pool_lock);
 
-	return pstr;
+	return interned;
 }
+
 /**
  * camel_pstring_strdup:
- * @s: String to copy.
+ * @string: string to copy
+ *
+ * Create a new pooled string entry for @strings.  A pooled string
+ * is a table where common strings are canonicalized.  They are also
+ * reference counted and freed when no longer referenced.
  *
- * Create a new pooled string entry for the string @s.  A pooled
- * string is a table where common strings are uniquified to the same
- * pointer value.  They are also refcounted, so freed when no longer
- * in use.  In a thread-safe manner.
+ * The %NULL and empty strings are special cased to constant values.
  *
- * The NULL and empty strings are special cased to constant values.
+ * Unreference the returned string with camel_pstring_free().
  *
- * Returns: A pointer to an equivalent string of @s.  Use
- * camel_pstring_free() when it is no longer needed.
+ * Returns: a canonicalized copy of @string
  **/
 const gchar *
-camel_pstring_strdup (const gchar *s)
+camel_pstring_strdup (const gchar *string)
 {
-	return camel_pstring_add ((gchar *) s, FALSE);
+	return camel_pstring_add ((gchar *) string, FALSE);
 }
 
 /**
  * camel_pstring_free:
- * @s: String to free.
- *
- * De-ref a pooled string. If no more refs exist to this string, it will be deallocated.
+ * @string: string to free
  *
- * NULL and the empty string are special cased.
+ * Unreferences a pooled string.  If the string's reference count drops to
+ * zero it will be deallocated.  %NULL and the empty string are special cased.
  **/
 void
-camel_pstring_free (const gchar *s)
+camel_pstring_free (const gchar *string)
 {
-	gpointer pstr;
-	gpointer pcount;
+	StringPoolNode static_node = { (gchar *) string, };
+	StringPoolNode *node;
 
-	if (pstring_table == NULL)
+	if (string_pool == NULL)
 		return;
-	if (s == NULL || s[0] == 0)
+
+	if (string == NULL || *string == '\0')
 		return;
 
-	g_static_mutex_lock (&pstring_lock);
-	if (g_hash_table_lookup_extended (pstring_table, s, &pstr, &pcount)) {
-		gint *count = pcount;
-		*count = (*count) - 1;
-		if ((*count) == 0) {
-			g_hash_table_remove (pstring_table, pstr);
-			g_free (pstr);
-			if (g_getenv("CDS_DEBUG")) {
-				if (pstr != s) /* Only for debugging purposes */
-					g_assert (0);
-			}
-		}
-	} else {
-		if (g_getenv("CDS_DEBUG")) {
-			g_warning("Trying to free string not allocated from the pool '%s'", s);
-			/*Only for debugging purposes */
-			g_assert (0);
-		}
-	}
-	g_static_mutex_unlock (&pstring_lock);
-}
+	g_static_mutex_lock (&string_pool_lock);
 
-static void
-count_pstring_memory_cb (gpointer key,
-                         gpointer value,
-                         gpointer user_data)
-{
-	const gchar *str = key;
-	guint64 *pbytes = user_data;
+	node = g_hash_table_lookup (string_pool, &static_node);
 
-	g_return_if_fail (str != NULL);
-	g_return_if_fail (pbytes != NULL);
+	if (node == NULL) {
+		g_warning ("%s: String not in pool: %s", G_STRFUNC, string);
+	} else if (node->string != string) {
+		g_warning ("%s: String is not ours: %s", G_STRFUNC, string);
+	} else if (node->ref_count == 0) {
+		g_warning ("%s: Orphaned pool node: %s", G_STRFUNC, string);
+	} else {
+		node->ref_count--;
+		if (node->ref_count == 0)
+			g_hash_table_remove (string_pool, node);
+	}
 
-	*pbytes += strlen (str);
+	g_static_mutex_unlock (&string_pool_lock);
 }
 
 /**
@@ -317,16 +361,33 @@ count_pstring_memory_cb (gpointer key,
 void
 camel_pstring_dump_stat (void)
 {
-	g_static_mutex_lock (&pstring_lock);
+	g_static_mutex_lock (&string_pool_lock);
+
+	g_print ("   String Pool Statistics: ");
 
-	if (!pstring_table) {
-		g_print ("   String Pool Statistics: Not used yet\n");
+	if (string_pool == NULL) {
+		g_print ("Not used yet\n");
 	} else {
+		GHashTableIter iter;
+		gchar *format_size;
 		guint64 bytes = 0;
+		gpointer key;
+
+		g_hash_table_iter_init (&iter, string_pool);
+
+		while (g_hash_table_iter_next (&iter, &key, NULL))
+			bytes += strlen (((StringPoolNode *) key)->string);
+
+		format_size = g_format_size_full (
+			bytes, G_FORMAT_SIZE_LONG_FORMAT);
+
+		g_print (
+			"Holds %d strings totaling %s\n",
+			g_hash_table_size (string_pool),
+			format_size);
 
-		g_hash_table_foreach (pstring_table, count_pstring_memory_cb, &bytes);
-		g_print ("   String Pool Statistics: Holds %d strings of total %" G_GUINT64_FORMAT " bytes\n", g_hash_table_size (pstring_table), bytes);
+		g_free (format_size);
 	}
 
-	g_static_mutex_unlock (&pstring_lock);
+	g_static_mutex_unlock (&string_pool_lock);
 }
diff --git a/camel/camel-string-utils.h b/camel/camel-string-utils.h
index d6b11e6..a9760ca 100644
--- a/camel/camel-string-utils.h
+++ b/camel/camel-string-utils.h
@@ -42,10 +42,10 @@ const gchar *camel_strdown (gchar *str);
 gchar camel_tolower (gchar c);
 gchar camel_toupper (gchar c);
 
-const gchar *camel_pstring_add (gchar *str, gboolean own);
-const gchar *camel_pstring_strdup (const gchar *s);
-void camel_pstring_free (const gchar *s);
-const gchar * camel_pstring_peek (const gchar *str);
+const gchar *camel_pstring_add (gchar *string, gboolean own);
+const gchar *camel_pstring_strdup (const gchar *string);
+void camel_pstring_free (const gchar *string);
+const gchar * camel_pstring_peek (const gchar *string);
 void camel_pstring_dump_stat (void);
 
 G_END_DECLS



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