[evolution-data-server] Fix a performance issue caused by GWeakRef usage in CamelMessageInfo



commit 028394cef6c44ffe671ec45d32057090d7aedb60
Author: Milan Crha <mcrha redhat com>
Date:   Wed Feb 15 12:45:10 2017 +0100

    Fix a performance issue caused by GWeakRef usage in CamelMessageInfo
    
    Each single message has its own CamelMessageInfo, which means that
    a folder with 100K messages has 100K CamelMessageInfo-s where each
    weak-references the same CamelFolderSummary. The GWeakRef as such
    is not designed for such large sets, which causes a significant
    performance issue. The added CamelWeakRefGroup minimizes this
    performance issue by sharing the same GWeakRef for one object.

 src/camel/CMakeLists.txt         |    2 +
 src/camel/camel-message-info.c   |   17 ++--
 src/camel/camel-weak-ref-group.c |  234 ++++++++++++++++++++++++++++++++++++++
 src/camel/camel-weak-ref-group.h |   46 ++++++++
 src/camel/camel.h                |    1 +
 5 files changed, 292 insertions(+), 8 deletions(-)
---
diff --git a/src/camel/CMakeLists.txt b/src/camel/CMakeLists.txt
index b105f40..acc6b1a 100644
--- a/src/camel/CMakeLists.txt
+++ b/src/camel/CMakeLists.txt
@@ -122,6 +122,7 @@ set(SOURCES
        camel-vee-store.c
        camel-vee-summary.c
        camel-vtrash-folder.c
+       camel-weak-ref-group.c
        ${CMAKE_CURRENT_BINARY_DIR}/camel-enumtypes.c
        ${CMAKE_CURRENT_BINARY_DIR}/camel-mime-tables.c
 )
@@ -259,6 +260,7 @@ set(HEADERS
        camel-vee-store.h
        camel-vee-summary.h
        camel-vtrash-folder.h
+       camel-weak-ref-group.h
        ${CMAKE_CURRENT_BINARY_DIR}/camel-enumtypes.h
 )
 
diff --git a/src/camel/camel-message-info.c b/src/camel/camel-message-info.c
index 58c419d..d9fd7ea 100644
--- a/src/camel/camel-message-info.c
+++ b/src/camel/camel-message-info.c
@@ -25,15 +25,16 @@
 #include "camel-folder-summary.h"
 #include "camel-message-info-base.h"
 #include "camel-string-utils.h"
+#include "camel-weak-ref-group.h"
 
 #include "camel-message-info.h"
 
 struct _CamelMessageInfoPrivate {
        GRecMutex property_lock;
 
-       GWeakRef summary;       /* CamelFolderSummary * */
-       gboolean dirty;         /* whether requires save to local disk/summary */
-       const gchar *uid;       /* allocated in the string pool */
+       CamelWeakRefGroup *summary_wrg; /* CamelFolderSummary * */
+       gboolean dirty;                 /* whether requires save to local disk/summary */
+       const gchar *uid;               /* allocated in the string pool */
        gboolean abort_notifications;
        gboolean thaw_notify_folder;
        gboolean thaw_notify_folder_with_counts;
@@ -364,7 +365,7 @@ message_info_set_property (GObject *object,
 
        switch (property_id) {
        case PROP_SUMMARY:
-               g_weak_ref_set (&mi->priv->summary, g_value_get_object (value));
+               camel_weak_ref_group_set (mi->priv->summary_wrg, g_value_get_object (value));
                return;
 
        case PROP_DIRTY:
@@ -541,7 +542,7 @@ message_info_dispose (GObject *object)
 {
        CamelMessageInfo *mi = CAMEL_MESSAGE_INFO (object);
 
-       g_weak_ref_set (&mi->priv->summary, NULL);
+       camel_weak_ref_group_set (mi->priv->summary_wrg, NULL);
        camel_pstring_free (mi->priv->uid);
        mi->priv->uid = NULL;
 
@@ -554,7 +555,7 @@ message_info_finalize (GObject *object)
 {
        CamelMessageInfo *mi = CAMEL_MESSAGE_INFO (object);
 
-       g_weak_ref_clear (&mi->priv->summary);
+       camel_weak_ref_group_unref (mi->priv->summary_wrg);
        g_rec_mutex_clear (&mi->priv->property_lock);
 
        /* Chain up to parent's method. */
@@ -941,9 +942,9 @@ static void
 camel_message_info_init (CamelMessageInfo *mi)
 {
        mi->priv = G_TYPE_INSTANCE_GET_PRIVATE (mi, CAMEL_TYPE_MESSAGE_INFO, CamelMessageInfoPrivate);
+       mi->priv->summary_wrg = camel_weak_ref_group_new ();
 
        g_rec_mutex_init (&mi->priv->property_lock);
-       g_weak_ref_init (&mi->priv->summary, NULL);
 }
 
 /**
@@ -1107,7 +1108,7 @@ camel_message_info_ref_summary (const CamelMessageInfo *mi)
 {
        g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), NULL);
 
-       return g_weak_ref_get (&mi->priv->summary);
+       return camel_weak_ref_group_get (mi->priv->summary_wrg);
 }
 
 /**
diff --git a/src/camel/camel-weak-ref-group.c b/src/camel/camel-weak-ref-group.c
new file mode 100644
index 0000000..3cd8e19
--- /dev/null
+++ b/src/camel/camel-weak-ref-group.c
@@ -0,0 +1,234 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: camel-weak-ref-group
+ * @include: camel/camel.h
+ * @short_description: A weak ref group
+ *
+ * A #GWeakRef as such is not suitable for large sets, because
+ * it causes big performance impact on free. This #CamelWeakRefGroup
+ * groups together weak references for the same object to minimize
+ * the performance issue of the #GWeakRef.
+ **/
+
+#include "evolution-data-server-config.h"
+
+#include <glib.h>
+
+#include "camel-weak-ref-group.h"
+
+struct _CamelWeakRefGroup {
+       guint ref_count;
+       gpointer object;
+};
+
+G_DEFINE_BOXED_TYPE (CamelWeakRefGroup, camel_weak_ref_group, camel_weak_ref_group_ref, 
camel_weak_ref_group_unref)
+
+typedef struct _ObjectData {
+       guint64 use_count;
+       GWeakRef weakref;
+} ObjectData;
+
+static GHashTable *groups = NULL; /* gpointer ~> ObjectData */
+G_LOCK_DEFINE_STATIC (groups);
+
+static ObjectData *
+object_data_new (gpointer object)
+{
+       ObjectData *od;
+
+       od = g_new (ObjectData, 1);
+       od->use_count = 1;
+
+       g_weak_ref_init (&od->weakref, object);
+
+       return od;
+}
+
+static void
+object_data_free (gpointer ptr)
+{
+       ObjectData *od = ptr;
+
+       if (od) {
+               g_warn_if_fail (od->use_count == 0);
+               g_weak_ref_set (&od->weakref, NULL);
+               g_weak_ref_clear (&od->weakref);
+               g_free (od);
+       }
+}
+
+/**
+ * camel_weak_ref_group_new:
+ *
+ * Returns: (transfer full): A new #CamelWeakRefGroup instance, which should
+ *    be freed with camel_weak_ref_group_unref() when no longer needed.
+ *
+ * Since: 3.24
+ **/
+CamelWeakRefGroup *
+camel_weak_ref_group_new (void)
+{
+       CamelWeakRefGroup *wrg;
+
+       wrg = g_new (CamelWeakRefGroup, 1);
+       wrg->ref_count = 1;
+       wrg->object = NULL;
+
+       return wrg;
+}
+
+/**
+ * camel_weak_ref_group_ref:
+ * @group: a #CamelWeakRefGroup
+ *
+ * Increases a reference count of the @group.
+ *
+ * Returns: the @group
+ *
+ * Since: 3.24
+ **/
+CamelWeakRefGroup *
+camel_weak_ref_group_ref (CamelWeakRefGroup *group)
+{
+       g_return_val_if_fail (group != NULL, NULL);
+
+       G_LOCK (groups);
+
+       group->ref_count++;
+
+       G_UNLOCK (groups);
+
+       return group;
+}
+
+/**
+ * camel_weak_ref_group_unref:
+ * @group: a #CamelWeakRefGroup
+ *
+ * Decreases a reference count of the @group. The @group is
+ * freed when the reference count reaches zero.
+ *
+ * Since: 3.24
+ **/
+void
+camel_weak_ref_group_unref (CamelWeakRefGroup *group)
+{
+       g_return_if_fail (group != NULL);
+       g_return_if_fail (group->ref_count > 0);
+
+       G_LOCK (groups);
+
+       group->ref_count--;
+
+       G_UNLOCK (groups);
+
+       if (!group->ref_count) {
+               camel_weak_ref_group_set (group, NULL);
+               g_free (group);
+       }
+}
+
+/**
+ * camel_weak_ref_group_set:
+ * @group: a #CamelWeakRefGroup
+ * @object: (nullable): a #GObject descendant, or %NULL
+ *
+ * Sets the @object as the object help by this @group. If
+ * the @object is %NULL, then unsets any previously set.
+ *
+ * Since: 3.24
+ **/
+void
+camel_weak_ref_group_set (CamelWeakRefGroup *group,
+                         gpointer object)
+{
+       g_return_if_fail (group != NULL);
+       g_return_if_fail (!object || G_IS_OBJECT (object));
+
+       G_LOCK (groups);
+
+       if (object != group->object) {
+               ObjectData *od;
+
+               if (group->object) {
+                       od = g_hash_table_lookup (groups, group->object);
+
+                       g_warn_if_fail (od != NULL);
+
+                       if (od) {
+                               od->use_count--;
+                               if (!od->use_count)
+                                       g_hash_table_remove (groups, group->object);
+                       }
+               } else if (!groups) {
+                       groups = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
object_data_free);
+               }
+
+               group->object = object;
+
+               if (group->object) {
+                       od = g_hash_table_lookup (groups, group->object);
+                       if (od) {
+                               od->use_count++;
+                       } else {
+                               od = object_data_new (group->object);
+                               g_hash_table_insert (groups, group->object, od);
+                       }
+               }
+
+               if (groups && !g_hash_table_size (groups)) {
+                       g_hash_table_destroy (groups);
+                       groups = NULL;
+               }
+       }
+
+       G_UNLOCK (groups);
+}
+
+/**
+ * camel_weak_ref_group_get:
+ * @group: a #CamelWeakRefGroup
+ *
+ * Returns: (transfer full): A referenced object associated with @group,
+ *    or %NULL, when no object had been set to it. Use g_object_unref()
+ *    to free it, when no longer needed.
+ *
+ * Since: 3.24
+ **/
+gpointer
+camel_weak_ref_group_get (CamelWeakRefGroup *group)
+{
+       gpointer object = NULL;
+
+       g_return_val_if_fail (group != NULL, NULL);
+
+       G_LOCK (groups);
+
+       if (group->object) {
+               ObjectData *od = g_hash_table_lookup (groups, group->object);
+
+               g_warn_if_fail (od != NULL);
+
+               object = g_weak_ref_get (&od->weakref);
+       }
+
+       G_UNLOCK (groups);
+
+       return object;
+}
diff --git a/src/camel/camel-weak-ref-group.h b/src/camel/camel-weak-ref-group.h
new file mode 100644
index 0000000..811d20c
--- /dev/null
+++ b/src/camel/camel-weak-ref-group.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__CAMEL_H_INSIDE__) && !defined (CAMEL_COMPILATION)
+#error "Only <camel/camel.h> can be included directly."
+#endif
+
+#ifndef CAMEL_WEAK_REF_GROUP_H
+#define CAMEL_WEAK_REF_GROUP_H
+
+#include <glib-object.h>
+
+#define CAMEL_TYPE_WEAK_REF_GROUP (camel_weak_ref_group_get_type ())
+
+G_BEGIN_DECLS
+
+typedef struct _CamelWeakRefGroup CamelWeakRefGroup;
+
+GType          camel_weak_ref_group_get_type   (void) G_GNUC_CONST;
+CamelWeakRefGroup *
+               camel_weak_ref_group_new        (void);
+CamelWeakRefGroup *
+               camel_weak_ref_group_ref        (CamelWeakRefGroup *group);
+void           camel_weak_ref_group_unref      (CamelWeakRefGroup *group);
+
+void           camel_weak_ref_group_set        (CamelWeakRefGroup *group,
+                                                gpointer object);
+gpointer       camel_weak_ref_group_get        (CamelWeakRefGroup *group);
+
+G_END_DECLS
+
+#endif /* CAMEL_WEAK_REF_GROUP_H */
diff --git a/src/camel/camel.h b/src/camel/camel.h
index b405baf..daf9068 100644
--- a/src/camel/camel.h
+++ b/src/camel/camel.h
@@ -139,6 +139,7 @@
 #include <camel/camel-vee-store.h>
 #include <camel/camel-vee-summary.h>
 #include <camel/camel-vtrash-folder.h>
+#include <camel/camel-weak-ref-group.h>
 
 #undef __CAMEL_H_INSIDE__
 


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