[evolution-data-server] EDataFactory: Free backend when no client connects to it



commit b4fb065d191c5da3bd19cf7952ddef26b10a4949
Author: Milan Crha <mcrha redhat com>
Date:   Wed Aug 7 13:49:32 2019 +0200

    EDataFactory: Free backend when no client connects to it
    
    This did not work properly with backend-per-process disabled, thus backends
    could be left running even when no client had them opened, which could
    prevent auto-close of the factory process.
    
    The fix contains also a change no avoid circular reference between the backend
    and its view, where view added its own reference to the associated backend and
    the backend added a reference to the view when it had been added into
    the backend. This could prevent free of the backend when any view was not
    properly freed by the client.

 .../backends/ldap/e-book-backend-ldap.c            |  12 ++-
 src/addressbook/libedata-book/e-book-backend.c     |   8 +-
 .../libedata-book/e-data-book-factory.c            |   2 +-
 src/addressbook/libedata-book/e-data-book-view.c   |  86 +++++++++++++---
 src/addressbook/libedata-book/e-data-book-view.h   |   4 +
 src/calendar/libedata-cal/e-cal-backend.c          |   6 +-
 src/calendar/libedata-cal/e-data-cal-factory.c     |   2 +-
 src/calendar/libedata-cal/e-data-cal-view.c        | 111 +++++++++++++++-----
 src/calendar/libedata-cal/e-data-cal-view.h        |   4 +
 src/libebackend/e-data-factory.c                   | 114 ++++++++++++++++++++-
 src/libebackend/e-data-factory.h                   |   4 +
 11 files changed, 299 insertions(+), 54 deletions(-)
---
diff --git a/src/addressbook/backends/ldap/e-book-backend-ldap.c 
b/src/addressbook/backends/ldap/e-book-backend-ldap.c
index 9ea955b83..90500b633 100644
--- a/src/addressbook/backends/ldap/e-book-backend-ldap.c
+++ b/src/addressbook/backends/ldap/e-book-backend-ldap.c
@@ -4398,22 +4398,28 @@ ldap_search_handler (LDAPOp *op,
 static void
 ldap_search_dtor (LDAPOp *op)
 {
+       EBookBackend *backend;
        EBookBackendLDAP *bl;
        LDAPSearchOp *search_op = (LDAPSearchOp *) op;
 
        d (printf ("ldap_search_dtor (%p)\n", search_op->view));
 
-       bl = E_BOOK_BACKEND_LDAP (e_data_book_view_get_backend (op->view));
+       backend = e_data_book_view_ref_backend (op->view);
+       bl = backend ? E_BOOK_BACKEND_LDAP (backend) : NULL;
 
        /* unhook us from our EDataBookView */
-       g_mutex_lock (&bl->priv->view_mutex);
+       if (bl)
+               g_mutex_lock (&bl->priv->view_mutex);
        g_object_set_data (G_OBJECT (search_op->view), LDAP_SEARCH_OP_IDENT, NULL);
-       g_mutex_unlock (&bl->priv->view_mutex);
+       if (bl)
+               g_mutex_unlock (&bl->priv->view_mutex);
 
        g_object_unref (search_op->view);
 
        if (!search_op->aborted)
                g_free (search_op);
+
+       g_clear_object (&backend);
 }
 
 static void
diff --git a/src/addressbook/libedata-book/e-book-backend.c b/src/addressbook/libedata-book/e-book-backend.c
index a8639c86d..f2fb21ab1 100644
--- a/src/addressbook/libedata-book/e-book-backend.c
+++ b/src/addressbook/libedata-book/e-book-backend.c
@@ -515,10 +515,10 @@ book_backend_dispose (GObject *object)
        g_clear_object (&priv->proxy_resolver);
        g_clear_object (&priv->authentication_source);
 
-       if (priv->views != NULL) {
-               g_list_free (priv->views);
-               priv->views = NULL;
-       }
+       g_mutex_lock (&priv->views_mutex);
+       g_list_free_full (priv->views, g_object_unref);
+       priv->views = NULL;
+       g_mutex_unlock (&priv->views_mutex);
 
        g_hash_table_remove_all (priv->operation_ids);
 
diff --git a/src/addressbook/libedata-book/e-data-book-factory.c 
b/src/addressbook/libedata-book/e-data-book-factory.c
index 5527d58d8..35850f2bb 100644
--- a/src/addressbook/libedata-book/e-data-book-factory.c
+++ b/src/addressbook/libedata-book/e-data-book-factory.c
@@ -121,7 +121,7 @@ data_book_factory_backend_closed_cb (EBackend *backend,
                                     const gchar *sender,
                                     EDataFactory *data_factory)
 {
-       e_data_factory_backend_closed (data_factory, backend);
+       e_data_factory_backend_closed_by_sender (data_factory, backend, sender);
 }
 
 static EBackend *
diff --git a/src/addressbook/libedata-book/e-data-book-view.c 
b/src/addressbook/libedata-book/e-data-book-view.c
index ee259f865..fbf0f9c28 100644
--- a/src/addressbook/libedata-book/e-data-book-view.c
+++ b/src/addressbook/libedata-book/e-data-book-view.c
@@ -57,7 +57,7 @@ struct _EDataBookViewPrivate {
        EDBusAddressBookView *dbus_object;
        gchar *object_path;
 
-       EBookBackend *backend;
+       GWeakRef backend_weakref; /* EBookBackend * */
 
        EBookBackendSExp *sexp;
        EBookClientViewFlags flags;
@@ -224,13 +224,19 @@ bookview_start_thread (gpointer data)
        EDataBookView *view = data;
 
        if (view->priv->running) {
-               /* To avoid race condition when one thread is starting the view, while
-                  another thread wants to notify about created/modified/removed objects. */
-               e_book_backend_sexp_lock (view->priv->sexp);
+               EBookBackend *backend = e_data_book_view_ref_backend (view);
+
+               if (backend) {
+                       /* To avoid race condition when one thread is starting the view, while
+                          another thread wants to notify about created/modified/removed objects. */
+                       e_book_backend_sexp_lock (view->priv->sexp);
+
+                       e_book_backend_start_view (backend, view);
 
-               e_book_backend_start_view (view->priv->backend, view);
+                       e_book_backend_sexp_unlock (view->priv->sexp);
 
-               e_book_backend_sexp_unlock (view->priv->sexp);
+                       g_object_unref (backend);
+               }
        }
 
        g_object_unref (view);
@@ -262,8 +268,14 @@ bookview_stop_thread (gpointer data)
 {
        EDataBookView *view = data;
 
-       if (!view->priv->running)
-               e_book_backend_stop_view (view->priv->backend, view);
+       if (!view->priv->running) {
+               EBookBackend *backend = e_data_book_view_ref_backend (view);
+
+               if (backend) {
+                       e_book_backend_stop_view (backend, view);
+                       g_object_unref (backend);
+               }
+       }
        g_object_unref (view);
 
        return NULL;
@@ -306,11 +318,21 @@ impl_DataBookView_dispose (EDBusAddressBookView *object,
                            GDBusMethodInvocation *invocation,
                            EDataBookView *view)
 {
+       EBookBackend *backend;
+
        e_dbus_address_book_view_complete_dispose (object, invocation);
 
-       e_book_backend_stop_view (view->priv->backend, view);
-       view->priv->running = FALSE;
-       e_book_backend_remove_view (view->priv->backend, view);
+       backend = e_data_book_view_ref_backend (view);
+
+       if (backend) {
+               e_book_backend_stop_view (backend, view);
+               view->priv->running = FALSE;
+               e_book_backend_remove_view (backend, view);
+
+               g_object_unref (backend);
+       } else {
+               view->priv->running = FALSE;
+       }
 
        return TRUE;
 }
@@ -366,9 +388,8 @@ data_book_view_set_backend (EDataBookView *view,
                             EBookBackend *backend)
 {
        g_return_if_fail (E_IS_BOOK_BACKEND (backend));
-       g_return_if_fail (view->priv->backend == NULL);
 
-       view->priv->backend = g_object_ref (backend);
+       g_weak_ref_set (&view->priv->backend_weakref, backend);
 }
 
 static void
@@ -444,9 +465,9 @@ data_book_view_get_property (GObject *object,
 {
        switch (property_id) {
                case PROP_BACKEND:
-                       g_value_set_object (
+                       g_value_take_object (
                                value,
-                               e_data_book_view_get_backend (
+                               e_data_book_view_ref_backend (
                                E_DATA_BOOK_VIEW (object)));
                        return;
 
@@ -493,9 +514,10 @@ data_book_view_dispose (GObject *object)
 
        g_clear_object (&priv->connection);
        g_clear_object (&priv->dbus_object);
-       g_clear_object (&priv->backend);
        g_clear_object (&priv->sexp);
 
+       g_weak_ref_set (&priv->backend_weakref, NULL);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_data_book_view_parent_class)->dispose (object);
 }
@@ -520,6 +542,7 @@ data_book_view_finalize (GObject *object)
                g_hash_table_destroy (priv->fields_of_interest);
 
        g_mutex_clear (&priv->pending_mutex);
+       g_weak_ref_clear (&priv->backend_weakref);
 
        g_hash_table_destroy (priv->ids);
 
@@ -618,6 +641,8 @@ e_data_book_view_init (EDataBookView *view)
 {
        view->priv = E_DATA_BOOK_VIEW_GET_PRIVATE (view);
 
+       g_weak_ref_init (&view->priv->backend_weakref, NULL);
+
        view->priv->flags = E_BOOK_CLIENT_VIEW_FLAGS_NOTIFY_INITIAL;
 
        view->priv->dbus_object = e_dbus_address_book_view_skeleton_new ();
@@ -693,6 +718,25 @@ e_data_book_view_new (EBookBackend *backend,
                "sexp", sexp, NULL);
 }
 
+/**
+ * e_data_book_view_ref_backend:
+ * @view: an #EDataBookView
+ *
+ * Refs the backend that @view is querying. Unref the returned backend,
+ * if not %NULL, with g_object_unref(), when no longer needed.
+ *
+ * Returns: (type EBookBackend) (transfer full) (nullable): The associated #EBookBackend.
+ *
+ * Since: 3.34
+ **/
+EBookBackend *
+e_data_book_view_ref_backend (EDataBookView *view)
+{
+       g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (view), NULL);
+
+       return g_weak_ref_get (&view->priv->backend_weakref);
+}
+
 /**
  * e_data_book_view_get_backend:
  * @view: an #EDataBookView
@@ -700,13 +744,21 @@ e_data_book_view_new (EBookBackend *backend,
  * Gets the backend that @view is querying.
  *
  * Returns: (type EBookBackend) (transfer none): The associated #EBookBackend.
+ *
+ * Deprecated: 3.34: Use e_data_book_view_ref_backend() instead.
  **/
 EBookBackend *
 e_data_book_view_get_backend (EDataBookView *view)
 {
+       EBookBackend *backend;
+
        g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (view), NULL);
 
-       return view->priv->backend;
+       backend = e_data_book_view_ref_backend (view);
+       if (backend)
+               g_object_unref (backend);
+
+       return backend;
 }
 
 /**
diff --git a/src/addressbook/libedata-book/e-data-book-view.h 
b/src/addressbook/libedata-book/e-data-book-view.h
index de24ca717..887b8dc48 100644
--- a/src/addressbook/libedata-book/e-data-book-view.h
+++ b/src/addressbook/libedata-book/e-data-book-view.h
@@ -73,8 +73,12 @@ EDataBookView *      e_data_book_view_new            (struct _EBookBackend *backend,
                                                 GDBusConnection *connection,
                                                 const gchar *object_path,
                                                 GError **error);
+struct _EBookBackend *
+               e_data_book_view_ref_backend    (EDataBookView *view);
+#ifndef EDS_DISABLE_DEPRECATED
 struct _EBookBackend *
                e_data_book_view_get_backend    (EDataBookView *view);
+#endif /* EDS_DISABLE_DEPRECATED */
 GDBusConnection *
                e_data_book_view_get_connection (EDataBookView *view);
 const gchar *  e_data_book_view_get_object_path
diff --git a/src/calendar/libedata-cal/e-cal-backend.c b/src/calendar/libedata-cal/e-cal-backend.c
index 77ca1eb36..3c94401c0 100644
--- a/src/calendar/libedata-cal/e-cal-backend.c
+++ b/src/calendar/libedata-cal/e-cal-backend.c
@@ -649,6 +649,11 @@ cal_backend_dispose (GObject *object)
        g_clear_object (&priv->proxy_resolver);
        g_clear_object (&priv->authentication_source);
 
+       g_mutex_lock (&priv->views_mutex);
+       g_list_free_full (priv->views, g_object_unref);
+       priv->views = NULL;
+       g_mutex_unlock (&priv->views_mutex);
+
        g_hash_table_remove_all (priv->operation_ids);
 
        while (!g_queue_is_empty (&priv->pending_operations))
@@ -667,7 +672,6 @@ cal_backend_finalize (GObject *object)
 
        priv = E_CAL_BACKEND_GET_PRIVATE (object);
 
-       g_list_free (priv->views);
        g_mutex_clear (&priv->views_mutex);
        g_mutex_clear (&priv->property_lock);
 
diff --git a/src/calendar/libedata-cal/e-data-cal-factory.c b/src/calendar/libedata-cal/e-data-cal-factory.c
index 5a4de310a..efb0d745d 100644
--- a/src/calendar/libedata-cal/e-data-cal-factory.c
+++ b/src/calendar/libedata-cal/e-data-cal-factory.c
@@ -149,7 +149,7 @@ data_cal_factory_backend_closed_cb (EBackend *backend,
                                    const gchar *sender,
                                    EDataFactory *data_factory)
 {
-       e_data_factory_backend_closed (data_factory, backend);
+       e_data_factory_backend_closed_by_sender (data_factory, backend, sender);
 }
 
 static EBackend *
diff --git a/src/calendar/libedata-cal/e-data-cal-view.c b/src/calendar/libedata-cal/e-data-cal-view.c
index 1c8edb77f..6452b5d31 100644
--- a/src/calendar/libedata-cal/e-data-cal-view.c
+++ b/src/calendar/libedata-cal/e-data-cal-view.c
@@ -57,7 +57,7 @@ struct _EDataCalViewPrivate {
        gchar *object_path;
 
        /* The backend we are monitoring */
-       ECalBackend *backend;
+       GWeakRef backend_weakref; /* ECalBackend * */
 
        gboolean started;
        gboolean stopped;
@@ -161,13 +161,19 @@ calview_start_thread (gpointer data)
        EDataCalView *view = data;
 
        if (view->priv->started && !view->priv->stopped) {
-               /* To avoid race condition when one thread is starting the view, while
-                  another thread wants to notify about created/modified/removed objects. */
-               e_cal_backend_sexp_lock (view->priv->sexp);
+               ECalBackend *backend = e_data_cal_view_ref_backend (view);
+
+               if (backend) {
+                       /* To avoid race condition when one thread is starting the view, while
+                          another thread wants to notify about created/modified/removed objects. */
+                       e_cal_backend_sexp_lock (view->priv->sexp);
+
+                       e_cal_backend_start_view (backend, view);
 
-               e_cal_backend_start_view (view->priv->backend, view);
+                       e_cal_backend_sexp_unlock (view->priv->sexp);
 
-               e_cal_backend_sexp_unlock (view->priv->sexp);
+                       g_object_unref (backend);
+               }
        }
 
        g_object_unref (view);
@@ -181,6 +187,7 @@ impl_DataCalView_start (EDBusCalendarView *object,
                         EDataCalView *view)
 {
        if (!view->priv->started) {
+               ECalBackend *backend = e_data_cal_view_ref_backend (view);
                GThread *thread;
 
                view->priv->started = TRUE;
@@ -188,7 +195,9 @@ impl_DataCalView_start (EDBusCalendarView *object,
                        FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES,
                        "---;%p;VIEW-START;%s;%s", view,
                        e_cal_backend_sexp_text (view->priv->sexp),
-                       G_OBJECT_TYPE_NAME (view->priv->backend));
+                       backend ? G_OBJECT_TYPE_NAME (backend) : "null backend");
+
+               g_clear_object (&backend);
 
                thread = g_thread_new (
                        NULL, calview_start_thread, g_object_ref (view));
@@ -205,8 +214,14 @@ calview_stop_thread (gpointer data)
 {
        EDataCalView *view = data;
 
-       if (view->priv->stopped)
-               e_cal_backend_stop_view (view->priv->backend, view);
+       if (view->priv->stopped) {
+               ECalBackend *backend = e_data_cal_view_ref_backend (view);
+
+               if (backend) {
+                       e_cal_backend_stop_view (backend, view);
+                       g_object_unref (backend);
+               }
+       }
        g_object_unref (view);
 
        return NULL;
@@ -247,11 +262,21 @@ impl_DataCalView_dispose (EDBusCalendarView *object,
                           GDBusMethodInvocation *invocation,
                           EDataCalView *view)
 {
+       ECalBackend *backend;
+
        e_dbus_calendar_view_complete_dispose (object, invocation);
 
-       e_cal_backend_stop_view (view->priv->backend, view);
-       view->priv->stopped = TRUE;
-       e_cal_backend_remove_view (view->priv->backend, view);
+       backend = e_data_cal_view_ref_backend (view);
+
+       if (backend) {
+               e_cal_backend_stop_view (backend, view);
+               view->priv->stopped = TRUE;
+               e_cal_backend_remove_view (backend, view);
+
+               g_object_unref (backend);
+       } else {
+               view->priv->stopped = TRUE;
+       }
 
        return TRUE;
 }
@@ -300,9 +325,8 @@ data_cal_view_set_backend (EDataCalView *view,
                            ECalBackend *backend)
 {
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (view->priv->backend == NULL);
 
-       view->priv->backend = g_object_ref (backend);
+       g_weak_ref_set (&view->priv->backend_weakref, backend);
 }
 
 static void
@@ -378,9 +402,9 @@ data_cal_view_get_property (GObject *object,
 {
        switch (property_id) {
                case PROP_BACKEND:
-                       g_value_set_object (
+                       g_value_take_object (
                                value,
-                               e_data_cal_view_get_backend (
+                               e_data_cal_view_ref_backend (
                                E_DATA_CAL_VIEW (object)));
                        return;
 
@@ -427,9 +451,10 @@ data_cal_view_dispose (GObject *object)
 
        g_clear_object (&priv->connection);
        g_clear_object (&priv->dbus_object);
-       g_clear_object (&priv->backend);
        g_clear_object (&priv->sexp);
 
+       g_weak_ref_set (&priv->backend_weakref, NULL);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_data_cal_view_parent_class)->dispose (object);
 }
@@ -457,6 +482,7 @@ data_cal_view_finalize (GObject *object)
                g_hash_table_destroy (priv->fields_of_interest);
 
        g_mutex_clear (&priv->pending_mutex);
+       g_weak_ref_clear (&priv->backend_weakref);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_data_cal_view_parent_class)->finalize (object);
@@ -572,7 +598,7 @@ e_data_cal_view_init (EDataCalView *view)
                view->priv->dbus_object, "handle-set-fields-of-interest",
                G_CALLBACK (impl_DataCalView_set_fields_of_interest), view);
 
-       view->priv->backend = NULL;
+       g_weak_ref_init (&view->priv->backend_weakref, NULL);
        view->priv->started = FALSE;
        view->priv->stopped = FALSE;
        view->priv->complete = FALSE;
@@ -811,6 +837,25 @@ notify_remove (EDataCalView *view,
        ensure_pending_flush_timeout (view);
 }
 
+/**
+ * e_data_cal_view_ref_backend:
+ * @view: an #EDataCalView
+ *
+ * Refs the backend that @view is querying. Unref the returned backend,
+ * if not %NULL, with g_object_unref(), when no longer needed.
+ *
+ * Returns: (type ECalBackend) (transfer full) (nullable): The associated #ECalBackend.
+ *
+ * Since: 3.34
+ **/
+ECalBackend *
+e_data_cal_view_ref_backend (EDataCalView *view)
+{
+       g_return_val_if_fail (E_IS_DATA_CAL_VIEW (view), NULL);
+
+       return g_weak_ref_get (&view->priv->backend_weakref);
+}
+
 /**
  * e_data_cal_view_get_backend:
  * @view: an #EDataCalView
@@ -820,13 +865,21 @@ notify_remove (EDataCalView *view,
  * Returns: (transfer none): The associated #ECalBackend.
  *
  * Since: 3.8
+ *
+ * Deprecated: 3.34: Use e_data_cal_view_ref_backend() instead.
  **/
 ECalBackend *
 e_data_cal_view_get_backend (EDataCalView *view)
 {
+       ECalBackend *backend;
+
        g_return_val_if_fail (E_IS_DATA_CAL_VIEW (view), NULL);
 
-       return view->priv->backend;
+       backend = e_data_cal_view_ref_backend (view);
+       if (backend)
+               g_object_unref (backend);
+
+       return backend;
 }
 
 /**
@@ -901,15 +954,19 @@ e_data_cal_view_object_matches (EDataCalView *view,
 {
        ECalBackend *backend;
        ECalBackendSExp *sexp;
+       gboolean res;
 
        g_return_val_if_fail (E_IS_DATA_CAL_VIEW (view), FALSE);
        g_return_val_if_fail (object != NULL, FALSE);
 
        sexp = e_data_cal_view_get_sexp (view);
-       backend = e_data_cal_view_get_backend (view);
+       backend = e_data_cal_view_ref_backend (view);
+
+       res = e_cal_backend_sexp_match_object (sexp, object, backend ? E_TIMEZONE_CACHE (backend) : NULL);
+
+       g_clear_object (&backend);
 
-       return e_cal_backend_sexp_match_object (
-               sexp, object, E_TIMEZONE_CACHE (backend));
+       return res;
 }
 
 /**
@@ -930,15 +987,19 @@ e_data_cal_view_component_matches (EDataCalView *view,
 {
        ECalBackend *backend;
        ECalBackendSExp *sexp;
+       gboolean res;
 
        g_return_val_if_fail (E_IS_DATA_CAL_VIEW (view), FALSE);
        g_return_val_if_fail (E_IS_CAL_COMPONENT (component), FALSE);
 
        sexp = e_data_cal_view_get_sexp (view);
-       backend = e_data_cal_view_get_backend (view);
+       backend = e_data_cal_view_ref_backend (view);
+
+       res = e_cal_backend_sexp_match_comp (sexp, component, backend ? E_TIMEZONE_CACHE (backend) : NULL);
 
-       return e_cal_backend_sexp_match_comp (
-               sexp, component, E_TIMEZONE_CACHE (backend));
+       g_clear_object (&backend);
+
+       return res;
 }
 
 /**
diff --git a/src/calendar/libedata-cal/e-data-cal-view.h b/src/calendar/libedata-cal/e-data-cal-view.h
index bf25ec37b..177427baa 100644
--- a/src/calendar/libedata-cal/e-data-cal-view.h
+++ b/src/calendar/libedata-cal/e-data-cal-view.h
@@ -69,8 +69,12 @@ EDataCalView *       e_data_cal_view_new             (struct _ECalBackend *backend,
                                                 GDBusConnection *connection,
                                                 const gchar *object_path,
                                                 GError **error);
+struct _ECalBackend *
+               e_data_cal_view_ref_backend     (EDataCalView *view);
+#ifndef EDS_DISABLE_DEPRECATED
 struct _ECalBackend *
                e_data_cal_view_get_backend     (EDataCalView *view);
+#endif /* EDS_DISABLE_DEPRECATED */
 GDBusConnection *
                e_data_cal_view_get_connection  (EDataCalView *view);
 const gchar *  e_data_cal_view_get_object_path (EDataCalView *view);
diff --git a/src/libebackend/e-data-factory.c b/src/libebackend/e-data-factory.c
index 865773430..68b2970b5 100644
--- a/src/libebackend/e-data-factory.c
+++ b/src/libebackend/e-data-factory.c
@@ -69,6 +69,11 @@ struct _EDataFactoryPrivate {
        GHashTable *connections;
        GRecMutex connections_lock;
 
+       /* Holds array of opened backends for each client.
+          Used only when not using backend-per-process.
+          Reuses the 'connections_lock'. */
+       GHashTable *backend_clients; /* gchar *sender ~> GPtrArray { GWeakRef { EBackend }} */
+
        /* This is a hash table of client bus names being watched.
         * The value is the watcher ID for g_bus_unwatch_name(). */
        GHashTable *watched_names;
@@ -340,6 +345,78 @@ data_factory_dup_subprocess_helper_hash_key (const gchar *factory_name,
        return helper_hash_key;
 }
 
+static void
+data_factory_add_backend_client (EDataFactory *data_factory,
+                                EBackend *backend,
+                                const gchar *sender)
+{
+       GPtrArray *array;
+
+       g_return_if_fail (E_IS_DATA_FACTORY (data_factory));
+       g_return_if_fail (E_IS_BACKEND (backend));
+       g_return_if_fail (sender != NULL);
+
+       g_rec_mutex_lock (&data_factory->priv->connections_lock);
+
+       array = g_hash_table_lookup (data_factory->priv->backend_clients, sender);
+       if (!array) {
+               array = g_ptr_array_new_with_free_func ((GDestroyNotify) e_weak_ref_free);
+               g_hash_table_insert (data_factory->priv->backend_clients, g_strdup (sender), array);
+       }
+
+       g_ptr_array_add (array, e_weak_ref_new (backend));
+
+       g_rec_mutex_unlock (&data_factory->priv->connections_lock);
+}
+
+static void
+data_factory_remove_backend_client (EDataFactory *data_factory,
+                                   EBackend *backend,
+                                   const gchar *sender)
+{
+       GPtrArray *array;
+       guint ii;
+
+       g_return_if_fail (E_IS_DATA_FACTORY (data_factory));
+       if (backend)
+               g_return_if_fail (E_IS_BACKEND (backend));
+       g_return_if_fail (sender != NULL);
+
+       g_rec_mutex_lock (&data_factory->priv->connections_lock);
+
+       array = g_hash_table_lookup (data_factory->priv->backend_clients, sender);
+       if (array) {
+               for (ii = 0; ii < array->len; ii++) {
+                       GWeakRef *weakref;
+                       EBackend *stored_backend;
+
+                       weakref = g_ptr_array_index (array, ii);
+                       stored_backend = g_weak_ref_get (weakref);
+
+                       if (backend) {
+                               if (stored_backend == backend) {
+                                       g_ptr_array_remove_index (array, ii);
+                                       /* One client can connect to one backend multiple times, thus
+                                          remove only one item from the array. */
+                                       g_clear_object (&stored_backend);
+                                       break;
+                               }
+                       } else if (stored_backend) {
+                               /* Do not provide 'sender', because it would call this function again,
+                                  causing unnecessary recursion. */
+                               e_data_factory_backend_closed_by_sender (data_factory, stored_backend, NULL);
+                       }
+
+                       g_clear_object (&stored_backend);
+               }
+
+               if (!array->len)
+                       g_hash_table_remove (data_factory->priv->backend_clients, sender);
+       }
+
+       g_rec_mutex_unlock (&data_factory->priv->connections_lock);
+}
+
 static gboolean
 data_factory_verify_subprocess_backend_proxy_is_used (EDataFactory *data_factory,
                                                      const gchar *except_bus_name,
@@ -470,6 +547,8 @@ data_factory_name_vanished_cb (GDBusConnection *connection,
        if (data_factory != NULL) {
                data_factory_connections_remove (data_factory, name, NULL);
 
+               data_factory_remove_backend_client (data_factory, NULL, name);
+
                /* Unwatching the bus name from here will corrupt the
                 * 'name' argument, and possibly also the 'user_data'.
                 *
@@ -955,6 +1034,7 @@ data_factory_dispose (GObject *object)
        g_clear_object (&priv->registry);
 
        g_hash_table_remove_all (priv->connections);
+       g_hash_table_remove_all (priv->backend_clients);
        g_hash_table_remove_all (priv->watched_names);
 
        /* Chain up to parent's dispose() method. */
@@ -981,6 +1061,8 @@ data_factory_finalize (GObject *object)
        g_hash_table_destroy (priv->connections);
        g_rec_mutex_clear (&priv->connections_lock);
 
+       g_hash_table_destroy (priv->backend_clients);
+
        g_hash_table_destroy (priv->watched_names);
        g_mutex_clear (&priv->watched_names_lock);
 
@@ -1147,6 +1229,8 @@ e_data_factory_init (EDataFactory *data_factory)
                (GDestroyNotify) g_free,
                (GDestroyNotify) g_ptr_array_unref);
 
+       data_factory->priv->backend_clients = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_ptr_array_unref);
+
        data_factory->priv->watched_names = g_hash_table_new_full (
                (GHashFunc) g_str_hash,
                (GEqualFunc) g_str_equal,
@@ -1366,6 +1450,7 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
        if (!backend_per_process && backend_factory) {
                gchar *object_path = NULL, *backend_key;
                OpenedBackendData *obd;
+               EBackend *backend = NULL;
 
                backend_key = g_strconcat (backend_name, "\n", uid, "\n", extension_name, NULL);
 
@@ -1374,6 +1459,7 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
                if (obd) {
                        object_path = g_strdup (obd->object_path);
                        g_object_ref (obd->backend);
+                       backend = obd->backend;
 
                        /* Also drop the "reference" on the data_factory, it's held by the obj->backend 
already */
                        e_dbus_server_release (E_DBUS_SERVER (data_factory));
@@ -1381,8 +1467,6 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
                g_mutex_unlock (&data_factory->priv->mutex);
 
                if (!object_path) {
-                       EBackend *backend;
-
                        backend = e_data_factory_create_backend (data_factory, backend_factory, source);
                        object_path = e_data_factory_open_backend (data_factory, backend, 
g_dbus_method_invocation_get_connection (invocation), NULL, &error);
                        if (object_path) {
@@ -1407,8 +1491,18 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
                }
 
                if (object_path) {
+                       GDBusConnection *connection;
+                       const gchar *sender;
+
                        class->complete_open (data_factory, invocation, object_path, server_class->bus_name, 
extension_name);
 
+                       connection = g_dbus_method_invocation_get_connection (invocation);
+                       sender = g_dbus_method_invocation_get_sender (invocation);
+
+                       data_factory_watched_names_add (data_factory, connection, sender);
+
+                       data_factory_add_backend_client (data_factory, backend, sender);
+
                        g_mutex_lock (&priv->spawn_subprocess_lock);
                        priv->spawn_subprocess_state = DATA_FACTORY_SPAWN_SUBPROCESS_NONE;
                        g_cond_signal (&priv->spawn_subprocess_cond);
@@ -1692,6 +1786,22 @@ e_data_factory_backend_closed (EDataFactory *data_factory,
        g_return_if_fail (E_IS_DATA_FACTORY (data_factory));
        g_return_if_fail (E_IS_BACKEND (backend));
 
+       e_data_factory_backend_closed_by_sender (data_factory, backend, NULL);
+}
+
+void
+e_data_factory_backend_closed_by_sender (EDataFactory *data_factory,
+                                        EBackend *backend,
+                                        const gchar *sender)
+{
+       g_return_if_fail (E_IS_DATA_FACTORY (data_factory));
+       g_return_if_fail (E_IS_BACKEND (backend));
+
+       /* Call only when 'sender' is not NULL, to avoid recursion when
+          data_factory_remove_backend_client() calls this function on its own. */
+       if (sender)
+               data_factory_remove_backend_client (data_factory, backend, sender);
+
        g_object_unref (backend);
 }
 
diff --git a/src/libebackend/e-data-factory.h b/src/libebackend/e-data-factory.h
index 7255f56a8..f80e5a15d 100644
--- a/src/libebackend/e-data-factory.h
+++ b/src/libebackend/e-data-factory.h
@@ -128,6 +128,10 @@ gchar *            e_data_factory_open_backend     (EDataFactory *data_factory,
                                                 GError **error);
 void           e_data_factory_backend_closed   (EDataFactory *data_factory,
                                                 EBackend *backend);
+void           e_data_factory_backend_closed_by_sender
+                                               (EDataFactory *data_factory,
+                                                EBackend *backend,
+                                                const gchar *sender);
 GSList *       e_data_factory_list_opened_backends /* EBackend * */
                                                (EDataFactory *data_factory);
 


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