[evolution-data-server] Fix a performance issue caused by GWeakRef usage in CamelMessageInfo
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Fix a performance issue caused by GWeakRef usage in CamelMessageInfo
- Date: Wed, 15 Feb 2017 11:47:10 +0000 (UTC)
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]