[evolution-data-server] EBackend: Fix view leak when the client crashes
- From: Fabiano Fidêncio <ffidencio src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] EBackend: Fix view leak when the client crashes
- Date: Tue, 29 Apr 2014 08:56:43 +0000 (UTC)
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]