[evolution-data-server] EBackend: Fix view leak when the client crashes



commit f99d14975e8561a9452bada6561f01a2151448bd
Author: Fabiano Fidêncio <fidencio redhat com>
Date:   Fri Apr 25 01:15:08 2014 +0200

    EBackend: Fix view leak when the client crashes
    
    Remove the views from the closed/crashed backend when it is not being
    used by any other connection.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=727431

 addressbook/libedata-book/e-book-backend.c |   20 ++++++
 calendar/libedata-cal/e-cal-backend.c      |   20 ++++++
 docs/reference/eds/eds-sections.txt        |    1 +
 libebackend/e-backend.c                    |   29 ++++++++
 libebackend/e-backend.h                    |    4 +-
 libebackend/e-data-factory.c               |   97 ++++++++++++++++++++++++----
 6 files changed, 158 insertions(+), 13 deletions(-)
---
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index a6556a0..a467c11 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -586,6 +586,25 @@ book_backend_authenticate_sync (EBackend *backend,
                registry, source, auth, cancellable, error);
 }
 
+static void
+book_backend_prepare_shutdown (EBackend *backend)
+{
+       GList *list, *l;
+
+       list = e_book_backend_list_views (E_BOOK_BACKEND (backend));
+
+       for (l = list; l != NULL; l = g_list_next (l)) {
+               EDataBookView *view = l->data;
+
+               e_book_backend_remove_view (E_BOOK_BACKEND (backend), view);
+       }
+
+       g_list_free_full (list, g_object_unref);
+
+       /* Chain up to parent's prepare_shutdown() method. */
+       E_BACKEND_CLASS (e_book_backend_parent_class)->prepare_shutdown (backend);
+}
+
 static gchar *
 book_backend_get_backend_property (EBookBackend *backend,
                                    const gchar *prop_name)
@@ -703,6 +722,7 @@ e_book_backend_class_init (EBookBackendClass *class)
 
        backend_class = E_BACKEND_CLASS (class);
        backend_class->authenticate_sync = book_backend_authenticate_sync;
+       backend_class->prepare_shutdown = book_backend_prepare_shutdown;
 
        class->get_backend_property = book_backend_get_backend_property;
        class->get_contact_list_uids_sync = book_backend_get_contact_list_uids_sync;
diff --git a/calendar/libedata-cal/e-cal-backend.c b/calendar/libedata-cal/e-cal-backend.c
index 4c1b7a2..c85e620 100644
--- a/calendar/libedata-cal/e-cal-backend.c
+++ b/calendar/libedata-cal/e-cal-backend.c
@@ -725,6 +725,25 @@ cal_backend_authenticate_sync (EBackend *backend,
 }
 
 static void
+cal_backend_prepare_shutdown (EBackend *backend)
+{
+       GList *list, *l;
+
+       list = e_cal_backend_list_views (E_CAL_BACKEND (backend));
+
+       for (l = list; l != NULL; l = g_list_next (l)) {
+               EDataCalView *view = l->data;
+
+               e_cal_backend_remove_view (E_CAL_BACKEND (backend), view);
+       }
+
+       g_list_free_full (list, g_object_unref);
+
+       /* Chain up to parent's prepare_shutdown() method. */
+       E_BACKEND_CLASS (e_cal_backend_parent_class)->prepare_shutdown (backend);
+}
+
+static void
 cal_backend_shutdown (ECalBackend *backend)
 {
        ESource *source;
@@ -911,6 +930,7 @@ e_cal_backend_class_init (ECalBackendClass *class)
 
        backend_class = E_BACKEND_CLASS (class);
        backend_class->authenticate_sync = cal_backend_authenticate_sync;
+       backend_class->prepare_shutdown = cal_backend_prepare_shutdown;
 
        class->get_backend_property = cal_backend_get_backend_property;
        class->shutdown = cal_backend_shutdown;
diff --git a/docs/reference/eds/eds-sections.txt b/docs/reference/eds/eds-sections.txt
index 80282ac..9b5b15f 100644
--- a/docs/reference/eds/eds-sections.txt
+++ b/docs/reference/eds/eds-sections.txt
@@ -101,6 +101,7 @@ e_backend_trust_prompt
 e_backend_trust_prompt_finish
 e_backend_get_destination_address
 e_backend_is_destination_reachable
+e_backend_prepare_shutdown
 <SUBSECTION Standard>
 EBackendPrivate
 E_BACKEND
diff --git a/libebackend/e-backend.c b/libebackend/e-backend.c
index 207c459..18ee2f2 100644
--- a/libebackend/e-backend.c
+++ b/libebackend/e-backend.c
@@ -469,6 +469,11 @@ backend_get_destination_address (EBackend *backend,
 }
 
 static void
+backend_prepare_shutdown (EBackend *backend)
+{
+}
+
+static void
 e_backend_class_init (EBackendClass *class)
 {
        GObjectClass *object_class;
@@ -486,6 +491,7 @@ e_backend_class_init (EBackendClass *class)
        class->authenticate = backend_authenticate;
        class->authenticate_finish = backend_authenticate_finish;
        class->get_destination_address = backend_get_destination_address;
+       class->prepare_shutdown = backend_prepare_shutdown;
 
        g_object_class_install_property (
                object_class,
@@ -1103,3 +1109,26 @@ e_backend_is_destination_reachable (EBackend *backend,
 
        return reachable;
 }
+
+/**
+ * e_backend_prepare_shutdown:
+ * @backend: an #EBackend instance
+ *
+ * Let's the @backend know that it'll be shut down shortly, no client connects
+ * to it anymore. The @backend can free any resources which reference it, for
+ * example the opened views.
+ *
+ * Since: 3.14
+ */
+void
+e_backend_prepare_shutdown (EBackend *backend)
+{
+       EBackendClass *class;
+
+       g_return_if_fail (E_IS_BACKEND (backend));
+
+       class = E_BACKEND_GET_CLASS (backend);
+       g_return_if_fail (class->prepare_shutdown != NULL);
+
+       class->prepare_shutdown (backend);
+}
diff --git a/libebackend/e-backend.h b/libebackend/e-backend.h
index 4f1d40f..4859fb1 100644
--- a/libebackend/e-backend.h
+++ b/libebackend/e-backend.h
@@ -100,9 +100,10 @@ struct _EBackendClass {
                                                (EBackend *backend,
                                                 gchar **host,
                                                 guint16 *port);
+       void            (*prepare_shutdown)     (EBackend *backend);
 
        /*< private >*/
-       gpointer reserved[12];
+       gpointer reserved[11];
 };
 
 GType          e_backend_get_type              (void) G_GNUC_CONST;
@@ -152,6 +153,7 @@ gboolean    e_backend_is_destination_reachable
                                                (EBackend *backend,
                                                 GCancellable *cancellable,
                                                 GError **error);
+void           e_backend_prepare_shutdown      (EBackend *backend);
 
 G_END_DECLS
 
diff --git a/libebackend/e-data-factory.c b/libebackend/e-data-factory.c
index ed3dd8f..e57985a 100644
--- a/libebackend/e-data-factory.c
+++ b/libebackend/e-data-factory.c
@@ -52,7 +52,7 @@ struct _EDataFactoryPrivate {
        /* This is a hash table of client bus names to an array of
         * EBackend references; one for every connection opened. */
        GHashTable *connections;
-       GMutex connections_lock;
+       GRecMutex connections_lock;
 
        /* This is a hash table of client bus names being watched.
         * The value is the watcher ID for g_bus_unwatch_name(). */
@@ -149,7 +149,7 @@ data_factory_connections_add (EDataFactory *data_factory,
        g_return_if_fail (name != NULL);
        g_return_if_fail (backend != NULL);
 
-       g_mutex_lock (&data_factory->priv->connections_lock);
+       g_rec_mutex_lock (&data_factory->priv->connections_lock);
 
        connections = data_factory->priv->connections;
 
@@ -167,7 +167,50 @@ data_factory_connections_add (EDataFactory *data_factory,
 
        g_ptr_array_add (array, g_object_ref (backend));
 
-       g_mutex_unlock (&data_factory->priv->connections_lock);
+       g_rec_mutex_unlock (&data_factory->priv->connections_lock);
+}
+
+static gboolean
+data_factory_verify_backend_is_used (EDataFactory *data_factory,
+                                    const gchar *except_bus_name,
+                                    EBackend *backend)
+{
+       GHashTable *connections;
+       GList *names, *l;
+       GPtrArray *array;
+       gboolean is_used = FALSE;
+
+       g_return_val_if_fail (except_bus_name != NULL, TRUE);
+
+       g_rec_mutex_lock (&data_factory->priv->connections_lock);
+
+       connections = data_factory->priv->connections;
+       names = g_hash_table_get_keys (connections);
+
+       for (l = names; l != NULL && !is_used; l = g_list_next (l)) {
+               const gchar *client_bus_name = l->data;
+               gint ii;
+
+               if (g_strcmp0 (client_bus_name, except_bus_name) == 0)
+                       continue;
+
+               array = g_hash_table_lookup (connections, client_bus_name);
+               for (ii = 0; ii < array->len; ii++) {
+                       EBackend *backend_in_use;
+
+                       backend_in_use = g_ptr_array_index (array, ii);
+
+                       if (backend_in_use == backend) {
+                               is_used = TRUE;
+                               break;
+                       }
+               }
+       }
+
+       g_list_free (names);
+       g_rec_mutex_unlock (&data_factory->priv->connections_lock);
+
+       return is_used;
 }
 
 static gboolean
@@ -182,15 +225,29 @@ data_factory_connections_remove (EDataFactory *data_factory,
        /* If backend is NULL, we remove all backends for name. */
        g_return_val_if_fail (name != NULL, FALSE);
 
-       g_mutex_lock (&data_factory->priv->connections_lock);
+       g_rec_mutex_lock (&data_factory->priv->connections_lock);
 
        connections = data_factory->priv->connections;
        array = g_hash_table_lookup (connections, name);
 
        if (array != NULL) {
                if (backend != NULL) {
+                       if (!data_factory_verify_backend_is_used (data_factory, name, backend))
+                               e_backend_prepare_shutdown (backend);
+
                        removed = g_ptr_array_remove_fast (array, backend);
                } else if (array->len > 0) {
+                       gint ii;
+
+                       for (ii = 0; ii < array->len; ii++) {
+                               EBackend *backend;
+
+                               backend = g_ptr_array_index (array, ii);
+
+                               if (!data_factory_verify_backend_is_used (data_factory, name, backend))
+                                       e_backend_prepare_shutdown (backend);
+                       }
+
                        g_ptr_array_set_size (array, 0);
                        removed = TRUE;
                }
@@ -202,7 +259,7 @@ data_factory_connections_remove (EDataFactory *data_factory,
                        e_dbus_server_release (E_DBUS_SERVER (data_factory));
        }
 
-       g_mutex_unlock (&data_factory->priv->connections_lock);
+       g_rec_mutex_unlock (&data_factory->priv->connections_lock);
 
        return removed;
 }
@@ -212,16 +269,26 @@ data_factory_connections_remove_all (EDataFactory *data_factory)
 {
        GHashTable *connections;
 
-       g_mutex_lock (&data_factory->priv->connections_lock);
+       g_rec_mutex_lock (&data_factory->priv->connections_lock);
 
        connections = data_factory->priv->connections;
 
        if (g_hash_table_size (connections) > 0) {
+               GSList *backends, *l;
+               backends = e_data_factory_list_backends (data_factory);
+
+               for (l = backends; l != NULL; l = g_slist_next (l)) {
+                       EBackend *backend = l->data;
+                       e_backend_prepare_shutdown (backend);
+               }
+
+               g_slist_free_full (backends, g_object_unref);
+
                g_hash_table_remove_all (connections);
                e_dbus_server_release (E_DBUS_SERVER (data_factory));
        }
 
-       g_mutex_unlock (&data_factory->priv->connections_lock);
+       g_rec_mutex_unlock (&data_factory->priv->connections_lock);
 }
 
 static void
@@ -391,7 +458,7 @@ data_factory_finalize (GObject *object)
        g_hash_table_destroy (priv->backend_factories);
 
        g_hash_table_destroy (priv->connections);
-       g_mutex_clear (&priv->connections_lock);
+       g_rec_mutex_clear (&priv->connections_lock);
 
        g_hash_table_destroy (priv->watched_names);
        g_mutex_clear (&priv->watched_names_lock);
@@ -520,7 +587,7 @@ e_data_factory_init (EDataFactory *data_factory)
        data_factory->priv = E_DATA_FACTORY_GET_PRIVATE (data_factory);
 
        g_mutex_init (&data_factory->priv->mutex);
-       g_mutex_init (&data_factory->priv->connections_lock);
+       g_rec_mutex_init (&data_factory->priv->connections_lock);
        g_mutex_init (&data_factory->priv->watched_names_lock);
 
        data_factory->priv->backends = g_hash_table_new_full (
@@ -810,14 +877,16 @@ e_data_factory_list_backends (EDataFactory *data_factory)
 {
        GSList *backends = NULL;
        GHashTable *connections;
+       GHashTable *backends_hash;
        GHashTableIter iter;
        gpointer key, value;
 
        g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
 
-       g_mutex_lock (&data_factory->priv->connections_lock);
+       g_rec_mutex_lock (&data_factory->priv->connections_lock);
 
        connections = data_factory->priv->connections;
+       backends_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
 
        g_hash_table_iter_init (&iter, connections);
        while (g_hash_table_iter_next (&iter, &key, &value)) {
@@ -826,12 +895,16 @@ e_data_factory_list_backends (EDataFactory *data_factory)
 
                for (ii = 0; ii < array->len; ii++) {
                        EBackend *backend = g_ptr_array_index (array, ii);
-                       backends = g_slist_prepend (backends, g_object_ref (backend));
+                       if (!g_hash_table_contains (backends_hash, backend)) {
+                               g_hash_table_insert (backends_hash, backend, GINT_TO_POINTER (1));
+                               backends = g_slist_prepend (backends, g_object_ref (backend));
+                       }
                }
        }
 
+       g_hash_table_destroy (backends_hash);
        backends = g_slist_reverse (backends);
-       g_mutex_unlock (&data_factory->priv->connections_lock);
+       g_rec_mutex_unlock (&data_factory->priv->connections_lock);
 
        return backends;
 }


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