[evolution-data-server] Bug 793031 - Decrease memory usage by disabling backend-per-process by default ][



commit 7ca206e0c0044bb6f85b8421562f2e42002f6283
Author: Milan Crha <mcrha redhat com>
Date:   Mon Mar 12 14:06:44 2018 +0100

    Bug 793031 - Decrease memory usage by disabling backend-per-process by default ][
    
    A follow-up change, with more aggressive fix, which doesn't start
    factory subprocesses but runs backends in the main factory process.

 .../evolution-data-server-docs.sgml.in             |    5 +
 src/addressbook/libedata-book/CMakeLists.txt       |    2 +
 .../libedata-book/e-data-book-factory.c            |  179 +++++++++-
 .../libedata-book/e-subprocess-book-factory.c      |  272 +++------------
 .../libedata-book/e-system-locale-watcher.c        |  365 ++++++++++++++++++++
 .../libedata-book/e-system-locale-watcher.h        |   75 ++++
 src/addressbook/libedata-book/libedata-book.h      |    1 +
 src/calendar/libedata-cal/e-data-cal-factory.c     |   92 +++++
 src/libebackend/e-data-factory.c                   |  284 ++++++++++++++--
 src/libebackend/e-data-factory.h                   |   25 ++-
 10 files changed, 1034 insertions(+), 266 deletions(-)
---
diff --git a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in 
b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
index 0fc86bb..999cd7d 100644
--- a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
+++ b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
@@ -193,6 +193,7 @@
       <xi:include href="xml/e-data-book-factory.xml"/>
       <xi:include href="xml/e-data-book-view.xml"/>
       <xi:include href="xml/e-subprocess-book-factory.xml"/>
+      <xi:include href="xml/e-system-locale-watcher.xml"/>
     </chapter>
 
     <chapter>
@@ -345,6 +346,10 @@
     <title>Index of deprecated symbols</title>
     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-3-30" role="3.30">
+    <title>Index of new symbols in 3.30</title>
+    <xi:include href="xml/api-index-3.30.xml"><xi:fallback /></xi:include>
+  </index>
   <index id="api-index-3-28" role="3.28">
     <title>Index of new symbols in 3.28</title>
     <xi:include href="xml/api-index-3.28.xml"><xi:fallback /></xi:include>
diff --git a/src/addressbook/libedata-book/CMakeLists.txt b/src/addressbook/libedata-book/CMakeLists.txt
index 2bac3a9..994d5f4 100644
--- a/src/addressbook/libedata-book/CMakeLists.txt
+++ b/src/addressbook/libedata-book/CMakeLists.txt
@@ -27,6 +27,7 @@ set(SOURCES
        e-data-book-factory.c
        e-data-book-view.c
        e-subprocess-book-factory.c
+       e-system-locale-watcher.c
        ximian-vcard.h
 )
 
@@ -49,6 +50,7 @@ set(HEADERS
        e-book-backend-cache.h
        e-book-backend-sqlitedb.h
        e-subprocess-book-factory.h
+       e-system-locale-watcher.h
 )
 
 if(WITH_LIBDB)
diff --git a/src/addressbook/libedata-book/e-data-book-factory.c 
b/src/addressbook/libedata-book/e-data-book-factory.c
index d34b65f..5527d58 100644
--- a/src/addressbook/libedata-book/e-data-book-factory.c
+++ b/src/addressbook/libedata-book/e-data-book-factory.c
@@ -30,10 +30,6 @@
  **/
 #include "evolution-data-server-config.h"
 
-#include <locale.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 #include <glib/gi18n.h>
 
 /* Private D-Bus classes. */
@@ -43,6 +39,7 @@
 #include "e-book-backend-factory.h"
 #include "e-data-book.h"
 #include "e-data-book-factory.h"
+#include "e-system-locale-watcher.h"
 
 #define d(x)
 
@@ -52,6 +49,9 @@
 
 struct _EDataBookFactoryPrivate {
        EDBusAddressBookFactory *dbus_factory;
+
+       ESystemLocaleWatcher *locale_watcher;
+       gulong notify_locale_id;
 };
 
 /* Forward Declarations */
@@ -117,15 +117,177 @@ data_book_factory_handle_open_address_book_cb (EDBusAddressBookFactory *iface,
 }
 
 static void
+data_book_factory_backend_closed_cb (EBackend *backend,
+                                    const gchar *sender,
+                                    EDataFactory *data_factory)
+{
+       e_data_factory_backend_closed (data_factory, backend);
+}
+
+static EBackend *
+data_book_factory_create_backend (EDataFactory *data_factory,
+                                 EBackendFactory *backend_factory,
+                                 ESource *source)
+{
+       EBookBackendFactoryClass *backend_factory_class;
+       EBackend *backend;
+
+       g_return_val_if_fail (E_IS_DATA_BOOK_FACTORY (data_factory), NULL);
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_FACTORY (backend_factory), NULL);
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+       backend_factory_class = E_BOOK_BACKEND_FACTORY_GET_CLASS (backend_factory);
+       g_return_val_if_fail (backend_factory_class != NULL, NULL);
+
+       if (g_type_is_a (backend_factory_class->backend_type, G_TYPE_INITABLE)) {
+               GError *local_error = NULL;
+
+               backend = g_initable_new (backend_factory_class->backend_type, NULL, &local_error,
+                       "registry", e_data_factory_get_registry (data_factory),
+                       "source", source,
+                       NULL);
+
+               if (!backend)
+                       g_warning ("%s: Failed to create backend: %s\n", G_STRFUNC, local_error ? 
local_error->message : "Unknown error");
+
+               g_clear_error (&local_error);
+       } else {
+               backend = g_object_new (backend_factory_class->backend_type,
+                       "registry", e_data_factory_get_registry (data_factory),
+                       "source", source,
+                       NULL);
+       }
+
+       if (backend) {
+               g_signal_connect (backend, "closed",
+                       G_CALLBACK (data_book_factory_backend_closed_cb), data_factory);
+       }
+
+       return backend;
+}
+
+static gchar *
+data_book_factory_open_backend (EDataFactory *data_factory,
+                               EBackend *backend,
+                               GDBusConnection *connection,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       EDataBook *data_book;
+       gchar *object_path;
+
+       g_return_val_if_fail (E_IS_DATA_BOOK_FACTORY (data_factory), NULL);
+       g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
+       g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+
+       /* If the backend already has an EDataBook installed, return its
+        * object path.  Otherwise we need to install a new EDataBook. */
+       data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend));
+
+       if (data_book != NULL) {
+               object_path = g_strdup (e_data_book_get_object_path (data_book));
+       } else {
+               object_path = e_subprocess_factory_construct_path ();
+
+               /* The EDataBook will attach itself to EBookBackend,
+                * so no need to call e_book_backend_set_data_book(). */
+               data_book = e_data_book_new (E_BOOK_BACKEND (backend), connection, object_path, error);
+
+               if (data_book) {
+                       EDataBookFactory *data_book_factory = E_DATA_BOOK_FACTORY (data_factory);
+                       gchar *locale;
+
+                       locale = e_system_locale_watcher_dup_locale (data_book_factory->priv->locale_watcher);
+
+                       /* Don't set the locale on a new book if we have not
+                        * yet received a notification of a locale change
+                        */
+                       if (locale)
+                               e_data_book_set_locale (data_book, locale, NULL, NULL);
+
+                       g_free (locale);
+               } else {
+                       g_free (object_path);
+                       object_path = NULL;
+               }
+       }
+
+       g_clear_object (&data_book);
+
+       return object_path;
+}
+
+static void
+data_book_factory_notify_locale_cb (GObject *object,
+                                   GParamSpec *pspec,
+                                   gpointer user_data)
+{
+       ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+       EDataBookFactory *data_book_factory = E_DATA_BOOK_FACTORY (user_data);
+       gchar *locale;
+
+       locale = e_system_locale_watcher_dup_locale (watcher);
+
+       if (locale) {
+               GSList *backends, *link;
+               GError *local_error = NULL;
+
+               backends = e_data_factory_list_opened_backends (E_DATA_FACTORY (data_book_factory));
+
+               for (link = backends; link; link = g_slist_next (link)) {
+                       EBackend *backend = link->data;
+                       EDataBook *data_book;
+
+                       data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend));
+
+                       if (!e_data_book_set_locale (data_book, locale, NULL, &local_error)) {
+                               g_warning ("Failed to set locale on addressbook: %s", local_error ? 
local_error->message : "Unknown error");
+                               g_clear_error (&local_error);
+                       }
+
+                       g_object_unref (data_book);
+               }
+
+               g_slist_free_full (backends, g_object_unref);
+               g_free (locale);
+       }
+}
+
+static void
+data_book_factory_constructed (GObject *object)
+{
+       EDataBookFactory *data_book_factory;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_data_book_factory_parent_class)->constructed (object);
+
+       data_book_factory = E_DATA_BOOK_FACTORY (object);
+
+       /* Listen to locale changes only when the backends run in the factory process,
+          aka when the backend-per-process is disabled */
+       if (!e_data_factory_use_backend_per_process (E_DATA_FACTORY (data_book_factory))) {
+               data_book_factory->priv->locale_watcher = e_system_locale_watcher_new ();
+
+               data_book_factory->priv->notify_locale_id = g_signal_connect (
+                       data_book_factory->priv->locale_watcher, "notify::locale",
+                       G_CALLBACK (data_book_factory_notify_locale_cb), data_book_factory);
+       }
+}
+
+static void
 data_book_factory_dispose (GObject *object)
 {
        EDataBookFactory *factory;
-       EDataBookFactoryPrivate *priv;
 
        factory = E_DATA_BOOK_FACTORY (object);
-       priv = factory->priv;
 
-       g_clear_object (&priv->dbus_factory);
+       if (factory->priv->locale_watcher && factory->priv->notify_locale_id) {
+               g_signal_handler_disconnect (factory->priv->locale_watcher, factory->priv->notify_locale_id);
+               factory->priv->notify_locale_id = 0;
+       }
+
+       g_clear_object (&factory->priv->dbus_factory);
+       g_clear_object (&factory->priv->locale_watcher);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_data_book_factory_parent_class)->dispose (object);
@@ -154,6 +316,7 @@ e_data_book_factory_class_init (EDataBookFactoryClass *class)
        g_type_class_add_private (class, sizeof (EDataBookFactoryPrivate));
 
        object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = data_book_factory_constructed;
        object_class->dispose = data_book_factory_dispose;
 
        dbus_server_class = E_DBUS_SERVER_CLASS (class);
@@ -168,6 +331,8 @@ e_data_book_factory_class_init (EDataBookFactoryClass *class)
        data_factory_class->get_dbus_interface_skeleton = data_book_factory_get_dbus_interface_skeleton;
        data_factory_class->get_factory_name = data_book_get_factory_name;
        data_factory_class->complete_open = data_book_complete_open;
+       data_factory_class->create_backend = data_book_factory_create_backend;
+       data_factory_class->open_backend = data_book_factory_open_backend;
 }
 
 static void
diff --git a/src/addressbook/libedata-book/e-subprocess-book-factory.c 
b/src/addressbook/libedata-book/e-subprocess-book-factory.c
index 406285f..80b5ffe 100644
--- a/src/addressbook/libedata-book/e-subprocess-book-factory.c
+++ b/src/addressbook/libedata-book/e-subprocess-book-factory.c
@@ -25,16 +25,12 @@
 
 #include "evolution-data-server-config.h"
 
-#include <locale.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 #include <glib/gi18n-lib.h>
 
 #include "e-book-backend.h"
 #include "e-book-backend-factory.h"
 #include "e-data-book.h"
-#include "e-dbus-localed.h"
+#include "e-system-locale-watcher.h"
 #include "e-subprocess-book-factory.h"
 
 #include <e-dbus-subprocess-backend.h>
@@ -44,16 +40,10 @@
        ((obj), E_TYPE_SUBPROCESS_BOOK_FACTORY, ESubprocessBookFactoryPrivate))
 
 struct _ESubprocessBookFactoryPrivate {
-       /* Watching "org.freedesktop.locale1" for locale changes */
-       guint localed_watch_id;
-       guint subprocess_watch_id;
-       EDBusLocale1 *localed_proxy;
-       GCancellable *localed_cancel;
-       gchar *locale;
+       ESystemLocaleWatcher *locale_watcher;
+       gulong notify_locale_id;
 };
 
-static GInitableIface *initable_parent_interface;
-
 /* Forward Declarations */
 static void    e_subprocess_book_factory_initable_init
                                                (GInitableIface *iface);
@@ -94,17 +84,19 @@ subprocess_book_factory_open (ESubprocessFactory *subprocess_factory,
                        connection, object_path, error);
 
                if (data_book != NULL) {
-                       e_subprocess_factory_set_backend_callbacks (
-                               subprocess_factory, backend, data);
+                       gchar *locale;
+
+                       e_subprocess_factory_set_backend_callbacks (subprocess_factory, backend, data);
+
+                       locale = e_system_locale_watcher_dup_locale 
(subprocess_book_factory->priv->locale_watcher);
 
                        /* Don't set the locale on a new book if we have not
                         * yet received a notification of a locale change
                         */
-                       if (subprocess_book_factory->priv->locale)
-                               e_data_book_set_locale (
-                                       data_book,
-                                       subprocess_book_factory->priv->locale,
-                                       NULL, NULL);
+                       if (locale)
+                               e_data_book_set_locale (data_book, locale, NULL, NULL);
+
+                       g_free (locale);
                } else {
                        g_free (object_path);
                        object_path = NULL;
@@ -139,211 +131,71 @@ subprocess_book_factory_ref_backend (ESourceRegistry *registry,
 }
 
 static void
-subprocess_book_factory_dispose (GObject *object)
-{
-       ESubprocessBookFactory *subprocess_factory;
-       ESubprocessBookFactoryPrivate *priv;
-
-       subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object);
-       priv = subprocess_factory->priv;
-
-       if (priv->localed_cancel)
-               g_cancellable_cancel (priv->localed_cancel);
-
-       g_clear_object (&priv->localed_cancel);
-       g_clear_object (&priv->localed_proxy);
-
-       if (priv->localed_watch_id > 0)
-               g_bus_unwatch_name (priv->localed_watch_id);
-
-       if (priv->subprocess_watch_id > 0)
-               g_bus_unwatch_name (priv->subprocess_watch_id);
-
-       /* Chain up to parent's dispose() method. */
-       G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->dispose (object);
-}
-
-static void
-subprocess_book_factory_finalize (GObject *object)
-{
-       ESubprocessBookFactory *subprocess_factory;
-
-       subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object);
-
-       g_free (subprocess_factory->priv->locale);
-
-       /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->finalize (object);
-}
-
-static gchar *
-subprocess_book_factory_interpret_locale_value (const gchar *value)
+subprocess_book_factory_notify_locale_cb (GObject *object,
+                                         GParamSpec *pspec,
+                                         gpointer user_data)
 {
-       gchar *interpreted_value = NULL;
-       gchar **split;
-
-       split = g_strsplit (value, "=", 2);
-
-       if (split && split[0] && split[1])
-               interpreted_value = g_strdup (split[1]);
-
-       g_strfreev (split);
+       ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+       ESubprocessBookFactory *subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (user_data);
+       gchar *locale;
 
-       if (!interpreted_value)
-               g_warning ("Failed to interpret locale value: %s", value);
+       locale = e_system_locale_watcher_dup_locale (watcher);
 
-       return interpreted_value;
-}
-
-static gchar *
-subprocess_book_factory_interpret_locale (const gchar * const * locale)
-{
-       gint i;
-       gchar *interpreted_locale = NULL;
-
-       /* Prioritize LC_COLLATE and then LANG values
-        * in the 'locale' specified by localed.
-        *
-        * If localed explicitly specifies no locale, then
-        * default to checking system locale.
-        */
        if (locale) {
-               for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
-                       if (strncmp (locale[i], "LC_COLLATE", 10) == 0)
-                               interpreted_locale =
-                                       subprocess_book_factory_interpret_locale_value (locale[i]);
-               }
-
-               for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
-                       if (strncmp (locale[i], "LANG", 4) == 0)
-                               interpreted_locale =
-                                       subprocess_book_factory_interpret_locale_value (locale[i]);
-               }
-       }
-
-       if (!interpreted_locale) {
-               const gchar *system_locale = setlocale (LC_COLLATE, NULL);
-
-               interpreted_locale = g_strdup (system_locale);
-       }
-
-       return interpreted_locale;
-}
-
-static void
-subprocess_book_factory_set_locale (ESubprocessBookFactory *subprocess_factory,
-                                   const gchar *locale)
-{
-       ESubprocessBookFactoryPrivate *priv = subprocess_factory->priv;
-       GError *error = NULL;
-
-       if (g_strcmp0 (priv->locale, locale) != 0) {
-               GList *backends, *l;
-
-               g_free (priv->locale);
-               priv->locale = g_strdup (locale);
+               GList *backends, *link;
+               GError *local_error = NULL;
 
                backends = e_subprocess_factory_get_backends_list (E_SUBPROCESS_FACTORY (subprocess_factory));
 
-               for (l = backends; l != NULL; l = g_list_next (l)) {
-                       EBackend *backend = l->data;
+               for (link = backends; link; link = g_list_next (link)) {
+                       EBackend *backend = link->data;
                        EDataBook *data_book;
 
                        data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend));
 
-                       if (!e_data_book_set_locale (data_book, locale, NULL, &error)) {
-                               g_warning (
-                                       "Failed to set locale on addressbook: %s",
-                                       error->message);
-                               g_clear_error (&error);
+                       if (!e_data_book_set_locale (data_book, locale, NULL, &local_error)) {
+                               g_warning ("Failed to set locale on addressbook: %s", local_error ? 
local_error->message : "Unknown error");
+                               g_clear_error (&local_error);
                        }
 
                        g_object_unref (data_book);
                }
 
                g_list_free_full (backends, g_object_unref);
+               g_free (locale);
        }
 }
 
 static void
-subprocess_book_factory_locale_changed (GObject *object,
-                                       GParamSpec *pspec,
-                                       gpointer user_data)
+subprocess_book_factory_constructed (GObject *object)
 {
-       EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object);
-       ESubprocessBookFactory *factory = (ESubprocessBookFactory *) user_data;
-       const gchar * const *locale;
-       gchar *interpreted_locale;
+       ESubprocessBookFactory *subprocess_factory;
 
-       locale = e_dbus_locale1_get_locale (locale_proxy);
-       interpreted_locale = subprocess_book_factory_interpret_locale (locale);
+       /* Chain up to parent's method */
+       G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->constructed (object);
 
-       subprocess_book_factory_set_locale (factory, interpreted_locale);
+       subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object);
+       subprocess_factory->priv->locale_watcher = e_system_locale_watcher_new ();
 
-       g_free (interpreted_locale);
+       subprocess_factory->priv->notify_locale_id = g_signal_connect (
+               subprocess_factory->priv->locale_watcher, "notify::locale",
+               G_CALLBACK (subprocess_book_factory_notify_locale_cb), subprocess_factory);
 }
 
 static void
-subprocess_book_factory_localed_ready (GObject *source_object,
-                                      GAsyncResult *res,
-                                      gpointer user_data)
+subprocess_book_factory_dispose (GObject *object)
 {
-       ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data;
-       GError *error = NULL;
-
-       subprocess_factory->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error);
+       ESubprocessBookFactory *subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object);
 
-       if (subprocess_factory->priv->localed_proxy == NULL) {
-               g_warning ("Error fetching localed proxy: %s", error->message);
-               g_error_free (error);
+       if (subprocess_factory->priv->locale_watcher && subprocess_factory->priv->notify_locale_id) {
+               g_signal_handler_disconnect (subprocess_factory->priv->locale_watcher, 
subprocess_factory->priv->notify_locale_id);
+               subprocess_factory->priv->notify_locale_id = 0;
        }
 
-       g_clear_object (&subprocess_factory->priv->localed_cancel);
-
-       if (subprocess_factory->priv->localed_proxy) {
-               g_signal_connect (
-                       subprocess_factory->priv->localed_proxy, "notify::locale",
-                       G_CALLBACK (subprocess_book_factory_locale_changed), subprocess_factory);
+       g_clear_object (&subprocess_factory->priv->locale_watcher);
 
-               /* Initial refresh of the locale */
-               subprocess_book_factory_locale_changed (
-                       G_OBJECT (subprocess_factory->priv->localed_proxy), NULL, subprocess_factory);
-       }
-}
-
-static void
-subprocess_book_factory_localed_appeared (GDBusConnection *connection,
-                                         const gchar *name,
-                                         const gchar *name_owner,
-                                         gpointer user_data)
-{
-       ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data;
-
-       subprocess_factory->priv->localed_cancel = g_cancellable_new ();
-
-       e_dbus_locale1_proxy_new (
-               connection,
-               G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
-               "org.freedesktop.locale1",
-               "/org/freedesktop/locale1",
-               subprocess_factory->priv->localed_cancel,
-               subprocess_book_factory_localed_ready,
-               subprocess_factory);
-}
-
-static void
-subprocess_book_factory_localed_vanished (GDBusConnection *connection,
-                                         const gchar *name,
-                                         gpointer user_data)
-{
-       ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data;
-
-       if (subprocess_factory->priv->localed_cancel) {
-               g_cancellable_cancel (subprocess_factory->priv->localed_cancel);
-               g_clear_object (&subprocess_factory->priv->localed_cancel);
-       }
-
-       g_clear_object (&subprocess_factory->priv->localed_proxy);
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->dispose (object);
 }
 
 static void
@@ -355,51 +207,17 @@ e_subprocess_book_factory_class_init (ESubprocessBookFactoryClass *class)
        g_type_class_add_private (class, sizeof (ESubprocessBookFactoryPrivate));
 
        object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = subprocess_book_factory_constructed;
        object_class->dispose = subprocess_book_factory_dispose;
-       object_class->finalize = subprocess_book_factory_finalize;
 
        subprocess_factory_class = E_SUBPROCESS_FACTORY_CLASS (class);
        subprocess_factory_class->ref_backend = subprocess_book_factory_ref_backend;
        subprocess_factory_class->open_data = subprocess_book_factory_open;
 }
 
-static gboolean
-subprocess_book_factory_initable_init (GInitable *initable,
-                                      GCancellable *cancellable,
-                                      GError **error)
-{
-       ESubprocessBookFactory *subprocess_factory;
-       GBusType bus_type = G_BUS_TYPE_SYSTEM;
-
-       subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (initable);
-
-       /* When running tests, we pretend to be the "org.freedesktop.locale1" service
-        * on the session bus instead of the real location on the system bus.
-        */
-       if (g_getenv ("EDS_TESTING") != NULL)
-               bus_type = G_BUS_TYPE_SESSION;
-
-       /* Watch system bus for locale change notifications */
-       subprocess_factory->priv->localed_watch_id =
-               g_bus_watch_name (
-                       bus_type,
-                       "org.freedesktop.locale1",
-                       G_BUS_NAME_WATCHER_FLAGS_NONE,
-                       subprocess_book_factory_localed_appeared,
-                       subprocess_book_factory_localed_vanished,
-                       initable,
-                       NULL);
-
-       /* Chain up to parent interface's init() method. */
-       return initable_parent_interface->init (initable, cancellable, error);
-}
-
 static void
 e_subprocess_book_factory_initable_init (GInitableIface *iface)
 {
-       initable_parent_interface = g_type_interface_peek_parent (iface);
-
-       iface->init = subprocess_book_factory_initable_init;
 }
 
 static void
diff --git a/src/addressbook/libedata-book/e-system-locale-watcher.c 
b/src/addressbook/libedata-book/e-system-locale-watcher.c
new file mode 100644
index 0000000..c8f9b1f
--- /dev/null
+++ b/src/addressbook/libedata-book/e-system-locale-watcher.c
@@ -0,0 +1,365 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2018 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: e-source-locale-watcher
+ * @include: libedata-book/libedata-book.h
+ * @short_description: Watch changes of system locale
+ *
+ * #ESystemLocaleWatcher watches for changes of system locale.
+ **/
+
+#include "evolution-data-server-config.h"
+
+#include <locale.h>
+#include <string.h>
+
+#include "e-dbus-localed.h"
+
+#include "e-system-locale-watcher.h"
+
+struct _ESystemLocaleWatcherPrivate {
+       GMutex lock;
+
+       /* Watching "org.freedesktop.locale1" for locale changes when not using backend-per-process */
+       guint localed_watch_id;
+       EDBusLocale1 *localed_proxy;
+       GCancellable *localed_cancel;
+       gchar *locale;
+};
+
+G_DEFINE_TYPE (ESystemLocaleWatcher, e_system_locale_watcher, G_TYPE_OBJECT)
+
+enum {
+       PROP_0,
+       PROP_LOCALE
+};
+
+static gchar *
+system_locale_watcher_interpret_locale_value (const gchar *value)
+{
+       gchar *interpreted_value = NULL;
+       gchar **split;
+
+       split = g_strsplit (value, "=", 2);
+
+       if (split && split[0] && split[1])
+               interpreted_value = g_strdup (split[1]);
+
+       g_strfreev (split);
+
+       if (!interpreted_value)
+               g_warning ("Failed to interpret locale value: %s", value);
+
+       return interpreted_value;
+}
+
+static gchar *
+system_locale_watcher_interpret_locale (const gchar * const * locale)
+{
+       gint i;
+       gchar *interpreted_locale = NULL;
+
+       /* Prioritize LC_COLLATE and then LANG values
+        * in the 'locale' specified by localed.
+        *
+        * If localed explicitly specifies no locale, then
+        * default to checking system locale.
+        */
+       if (locale) {
+               for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
+                       if (strncmp (locale[i], "LC_COLLATE", 10) == 0)
+                               interpreted_locale = system_locale_watcher_interpret_locale_value (locale[i]);
+               }
+
+               for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
+                       if (strncmp (locale[i], "LANG", 4) == 0)
+                               interpreted_locale = system_locale_watcher_interpret_locale_value (locale[i]);
+               }
+       }
+
+       if (!interpreted_locale) {
+               const gchar *system_locale = setlocale (LC_COLLATE, NULL);
+
+               interpreted_locale = g_strdup (system_locale);
+       }
+
+       return interpreted_locale;
+}
+
+static void
+system_locale_watcher_set_locale (ESystemLocaleWatcher *watcher,
+                                 const gchar *locale)
+{
+       g_mutex_lock (&watcher->priv->lock);
+
+       if (g_strcmp0 (watcher->priv->locale, locale) != 0) {
+
+               g_free (watcher->priv->locale);
+               watcher->priv->locale = g_strdup (locale);
+
+               g_mutex_unlock (&watcher->priv->lock);
+
+               g_object_notify (G_OBJECT (watcher), "locale");
+       } else {
+               g_mutex_unlock (&watcher->priv->lock);
+       }
+}
+
+static void
+system_locale_watcher_locale_changed (GObject *object,
+                                     GParamSpec *pspec,
+                                     gpointer user_data)
+{
+       EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object);
+       ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data;
+       const gchar * const *locale;
+       gchar *interpreted_locale;
+
+       locale = e_dbus_locale1_get_locale (locale_proxy);
+       interpreted_locale = system_locale_watcher_interpret_locale (locale);
+
+       system_locale_watcher_set_locale (watcher, interpreted_locale);
+
+       g_free (interpreted_locale);
+}
+
+static void
+system_locale_watcher_localed_ready (GObject *source_object,
+                                    GAsyncResult *res,
+                                    gpointer user_data)
+{
+       ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data;
+       GError *error = NULL;
+
+       watcher->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error);
+
+       if (!watcher->priv->localed_proxy) {
+               g_warning ("Error fetching localed proxy: %s", error ? error->message : "Unknown error");
+               g_clear_error (&error);
+       }
+
+       g_clear_object (&watcher->priv->localed_cancel);
+
+       if (watcher->priv->localed_proxy) {
+               g_signal_connect (
+                       watcher->priv->localed_proxy, "notify::locale",
+                       G_CALLBACK (system_locale_watcher_locale_changed), watcher);
+
+               /* Initial refresh of the locale */
+               system_locale_watcher_locale_changed (G_OBJECT (watcher->priv->localed_proxy), NULL, watcher);
+       }
+}
+
+static void
+system_locale_watcher_localed_appeared (GDBusConnection *connection,
+                                       const gchar *name,
+                                       const gchar *name_owner,
+                                       gpointer user_data)
+{
+       ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data;
+
+       watcher->priv->localed_cancel = g_cancellable_new ();
+
+       e_dbus_locale1_proxy_new (
+               connection,
+               G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+               "org.freedesktop.locale1",
+               "/org/freedesktop/locale1",
+               watcher->priv->localed_cancel,
+               system_locale_watcher_localed_ready,
+               watcher);
+}
+
+static void
+system_locale_watcher_localed_vanished (GDBusConnection *connection,
+                                       const gchar *name,
+                                       gpointer user_data)
+{
+       ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data;
+
+       if (watcher->priv->localed_cancel) {
+               g_cancellable_cancel (watcher->priv->localed_cancel);
+               g_clear_object (&watcher->priv->localed_cancel);
+       }
+
+       g_clear_object (&watcher->priv->localed_proxy);
+}
+
+static void
+system_locale_watcher_get_property (GObject *object,
+                                   guint property_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_LOCALE:
+                       g_value_take_string (
+                               value,
+                               e_system_locale_watcher_dup_locale (
+                               E_SYSTEM_LOCALE_WATCHER (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+system_locale_watcher_constructed (GObject *object)
+{
+       ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+       GBusType bus_type = G_BUS_TYPE_SYSTEM;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->constructed (object);
+
+       /* When running tests, we pretend to be the "org.freedesktop.locale1" service
+        * on the session bus instead of the real location on the system bus.
+        */
+       if (g_getenv ("EDS_TESTING") != NULL)
+               bus_type = G_BUS_TYPE_SESSION;
+
+       /* Watch system bus for locale change notifications */
+       watcher->priv->localed_watch_id =
+               g_bus_watch_name (
+                       bus_type,
+                       "org.freedesktop.locale1",
+                       G_BUS_NAME_WATCHER_FLAGS_NONE,
+                       system_locale_watcher_localed_appeared,
+                       system_locale_watcher_localed_vanished,
+                       watcher,
+                       NULL);
+}
+
+static void
+system_locale_watcher_dispose (GObject *object)
+{
+       ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+
+       if (watcher->priv->localed_cancel)
+               g_cancellable_cancel (watcher->priv->localed_cancel);
+
+       g_clear_object (&watcher->priv->localed_cancel);
+       g_clear_object (&watcher->priv->localed_proxy);
+
+       if (watcher->priv->localed_watch_id > 0)
+               g_bus_unwatch_name (watcher->priv->localed_watch_id);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->dispose (object);
+}
+
+static void
+system_locale_watcher_finalize (GObject *object)
+{
+       ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+
+       g_free (watcher->priv->locale);
+       g_mutex_clear (&watcher->priv->lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->finalize (object);
+}
+
+static void
+e_system_locale_watcher_class_init (ESystemLocaleWatcherClass *klass)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ESystemLocaleWatcherPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->get_property = system_locale_watcher_get_property;
+       object_class->constructed = system_locale_watcher_constructed;
+       object_class->dispose = system_locale_watcher_dispose;
+       object_class->finalize = system_locale_watcher_finalize;
+
+       /**
+        * ESystemLocaleWatcher:locale:
+        *
+        * Current locale, as detected. It can be %NULL, when the locale
+        * was not detected yet.
+        *
+        * Since: 3.30
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_LOCALE,
+               g_param_spec_string (
+                       "locale",
+                       "Locale",
+                       NULL,
+                       NULL,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_system_locale_watcher_init (ESystemLocaleWatcher *watcher)
+{
+       watcher->priv = G_TYPE_INSTANCE_GET_PRIVATE (watcher, E_TYPE_SYSTEM_LOCALE_WATCHER, 
ESystemLocaleWatcherPrivate);
+
+       g_mutex_init (&watcher->priv->lock);
+
+       watcher->priv->locale = NULL;
+}
+
+/**
+ * e_system_locale_watcher_new:
+ *
+ * Creates a new #ESystemLocaleWatcher instance, which listens for D-Bus
+ * notification on locale changes. It uses system bus, unless an environment
+ * variable "EDS_TESTING" is defined, in which case it uses the session bus
+ * instead.
+ *
+ * Returns: (transfer full): a new #ESystemLocaleWatcher
+ *
+ * Since: 3.30
+ **/
+ESystemLocaleWatcher *
+e_system_locale_watcher_new (void)
+{
+       return g_object_new (E_TYPE_SYSTEM_LOCALE_WATCHER, NULL);
+}
+
+/**
+ * e_system_locale_watcher_dup_locale:
+ * @watcher: an #ESystemLocaleWatcher
+ *
+ * Returns the current locale, as detected by the @watcher. The string
+ * is duplicated for thread safety. It can be %NULL, when the locale
+ * was not detected yet.
+ *
+ * Free it with g_free(), when no longer needed.
+ *
+ * Returns: (transfer full) (nullable): the system locale, as detected by the @watcher
+ *
+ * Since: 3.30
+ **/
+gchar *
+e_system_locale_watcher_dup_locale (ESystemLocaleWatcher *watcher)
+{
+       gchar *locale;
+
+       g_return_val_if_fail (E_IS_SYSTEM_LOCALE_WATCHER (watcher), NULL);
+
+       g_mutex_lock (&watcher->priv->lock);
+       locale = g_strdup (watcher->priv->locale);
+       g_mutex_unlock (&watcher->priv->lock);
+
+       return locale;
+}
diff --git a/src/addressbook/libedata-book/e-system-locale-watcher.h 
b/src/addressbook/libedata-book/e-system-locale-watcher.h
new file mode 100644
index 0000000..e1824b4
--- /dev/null
+++ b/src/addressbook/libedata-book/e-system-locale-watcher.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2018 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 (__LIBEDATA_BOOK_H_INSIDE__) && !defined (LIBEDATA_BOOK_COMPILATION)
+#error "Only <libedata-book/libedata-book.h> should be included directly."
+#endif
+
+#ifndef E_SYSTEM_LOCALE_WATCHER_H
+#define E_SYSTEM_LOCALE_WATCHER_H
+
+#include <glib-object.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SYSTEM_LOCALE_WATCHER \
+       (e_system_locale_watcher_get_type ())
+#define E_SYSTEM_LOCALE_WATCHER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcher))
+#define E_SYSTEM_LOCALE_WATCHER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcherClass))
+#define E_IS_SYSTEM_LOCALE_WATCHER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SYSTEM_LOCALE_WATCHER))
+#define E_IS_SYSTEM_LOCALE_WATCHER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SYSTEM_LOCALE_WATCHER))
+#define E_SYSTEM_LOCALE_WATCHER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcherClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESystemLocaleWatcher ESystemLocaleWatcher;
+typedef struct _ESystemLocaleWatcherClass ESystemLocaleWatcherClass;
+typedef struct _ESystemLocaleWatcherPrivate ESystemLocaleWatcherPrivate;
+
+/**
+ * ESystemLocaleWatcher:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _ESystemLocaleWatcher {
+       /*< private >*/
+       GObject parent;
+       ESystemLocaleWatcherPrivate *priv;
+};
+
+struct _ESystemLocaleWatcherClass {
+       GObjectClass parent_class;
+};
+
+GType          e_system_locale_watcher_get_type        (void) G_GNUC_CONST;
+ESystemLocaleWatcher *
+               e_system_locale_watcher_new             (void);
+gchar *                e_system_locale_watcher_dup_locale      (ESystemLocaleWatcher *watcher);
+
+G_END_DECLS
+
+#endif /* E_SYSTEM_LOCALE_WATCHER_H */
diff --git a/src/addressbook/libedata-book/libedata-book.h b/src/addressbook/libedata-book/libedata-book.h
index 50ee041..b5db503 100644
--- a/src/addressbook/libedata-book/libedata-book.h
+++ b/src/addressbook/libedata-book/libedata-book.h
@@ -40,6 +40,7 @@
 #include <libedata-book/e-data-book-view.h>
 #include <libedata-book/e-data-book.h>
 #include <libedata-book/e-subprocess-book-factory.h>
+#include <libedata-book/e-system-locale-watcher.h>
 
 #undef __LIBEDATA_BOOK_H_INSIDE__
 
diff --git a/src/calendar/libedata-cal/e-data-cal-factory.c b/src/calendar/libedata-cal/e-data-cal-factory.c
index baaeb5f..808abf7 100644
--- a/src/calendar/libedata-cal/e-data-cal-factory.c
+++ b/src/calendar/libedata-cal/e-data-cal-factory.c
@@ -144,6 +144,96 @@ data_cal_complete_open (EDataFactory *data_factory,
                data_cal_complete_memo_list_open (data_factory, invocation, object_path, bus_name);
 }
 
+static void
+data_cal_factory_backend_closed_cb (EBackend *backend,
+                                   const gchar *sender,
+                                   EDataFactory *data_factory)
+{
+       e_data_factory_backend_closed (data_factory, backend);
+}
+
+static EBackend *
+data_cal_factory_create_backend (EDataFactory *data_factory,
+                                EBackendFactory *backend_factory,
+                                ESource *source)
+{
+       ECalBackendFactoryClass *backend_factory_class;
+       EBackend *backend;
+
+       g_return_val_if_fail (E_IS_DATA_CAL_FACTORY (data_factory), NULL);
+       g_return_val_if_fail (E_IS_CAL_BACKEND_FACTORY (backend_factory), NULL);
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+       backend_factory_class = E_CAL_BACKEND_FACTORY_GET_CLASS (backend_factory);
+       g_return_val_if_fail (backend_factory_class != NULL, NULL);
+
+       if (g_type_is_a (backend_factory_class->backend_type, G_TYPE_INITABLE)) {
+               GError *local_error = NULL;
+
+               backend = g_initable_new (backend_factory_class->backend_type, NULL, &local_error,
+                       "kind", backend_factory_class->component_kind,
+                       "registry", e_data_factory_get_registry (data_factory),
+                       "source", source,
+                       NULL);
+
+               if (!backend)
+                       g_warning ("%s: Failed to create backend: %s\n", G_STRFUNC, local_error ? 
local_error->message : "Unknown error");
+
+               g_clear_error (&local_error);
+       } else {
+               backend = g_object_new (backend_factory_class->backend_type,
+                       "kind", backend_factory_class->component_kind,
+                       "registry", e_data_factory_get_registry (data_factory),
+                       "source", source,
+                       NULL);
+       }
+
+       if (backend) {
+               g_signal_connect (backend, "closed",
+                       G_CALLBACK (data_cal_factory_backend_closed_cb), data_factory);
+       }
+
+       return backend;
+}
+
+static gchar *
+data_cal_factory_open_backend (EDataFactory *data_factory,
+                              EBackend *backend,
+                              GDBusConnection *connection,
+                              GCancellable *cancellable,
+                              GError **error)
+{
+       EDataCal *data_cal;
+       gchar *object_path;
+
+       g_return_val_if_fail (E_IS_DATA_CAL_FACTORY (data_factory), NULL);
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+
+       /* If the backend already has an EDataCal installed, return its
+        * object path.  Otherwise we need to install a new EDataCal. */
+       data_cal = e_cal_backend_ref_data_cal (E_CAL_BACKEND (backend));
+
+       if (data_cal) {
+               object_path = g_strdup (e_data_cal_get_object_path (data_cal));
+       } else {
+               object_path = e_subprocess_factory_construct_path ();
+
+               /* The EDataCal will attach itself to ECalBackend,
+                * so no need to call e_cal_backend_set_data_cal(). */
+               data_cal = e_data_cal_new (E_CAL_BACKEND (backend), connection, object_path, error);
+
+               if (!data_cal) {
+                       g_free (object_path);
+                       object_path = NULL;
+               }
+       }
+
+       g_clear_object (&data_cal);
+
+       return object_path;
+}
+
 static gboolean
 data_cal_factory_initable_init (GInitable *initable,
                                GCancellable *cancellable,
@@ -196,6 +286,8 @@ e_data_cal_factory_class_init (EDataCalFactoryClass *class)
        data_factory_class->get_dbus_interface_skeleton = data_cal_factory_get_dbus_interface_skeleton;
        data_factory_class->get_factory_name = data_cal_get_factory_name;
        data_factory_class->complete_open = data_cal_complete_open;
+       data_factory_class->create_backend = data_cal_factory_create_backend;
+       data_factory_class->open_backend = data_cal_factory_open_backend;
 }
 
 static gboolean
diff --git a/src/libebackend/e-data-factory.c b/src/libebackend/e-data-factory.c
index 0da6628..9495da1 100644
--- a/src/libebackend/e-data-factory.c
+++ b/src/libebackend/e-data-factory.c
@@ -85,6 +85,9 @@ struct _EDataFactoryPrivate {
 
        gboolean reload_supported;
        gint backend_per_process;
+
+       /* Used only when backend-per-process is disabled, guarded by 'mutex' */
+       GHashTable *opened_backends; /* backend-key ~> OpenedBackendData * */
 };
 
 enum {
@@ -183,6 +186,36 @@ data_factory_subprocess_helper_free (DataFactorySubprocessHelper *helper)
        }
 }
 
+typedef struct _OpenedBackendData {
+       EBackend *backend;
+       gchar *object_path;
+} OpenedBackendData;
+
+static OpenedBackendData *
+opened_backend_data_new (EBackend *backend, /* assumes ownership of 'backend' */
+                        const gchar *object_path)
+{
+       OpenedBackendData *obd;
+
+       obd = g_new0 (OpenedBackendData, 1);
+       obd->backend = backend;
+       obd->object_path = g_strdup (object_path);
+
+       return obd;
+}
+
+static void
+opened_backend_data_free (gpointer ptr)
+{
+       OpenedBackendData *obd = ptr;
+
+       if (obd) {
+               g_clear_object (&obd->backend);
+               g_free (obd->object_path);
+               g_free (obd);
+       }
+}
+
 static gchar *
 data_factory_construct_subprocess_path (EDataFactory *data_factory)
 {
@@ -286,34 +319,16 @@ data_factory_subprocess_data_free (DataFactorySubprocessData *sd)
        }
 }
 
-static gboolean
-data_factory_use_backend_per_process (gint override_backend_per_process)
-{
-       gboolean backend_per_process;
-
-       if (override_backend_per_process == 0 || override_backend_per_process == 1) {
-               backend_per_process = override_backend_per_process == 1;
-       } else {
-               #ifdef ENABLE_BACKEND_PER_PROCESS
-               backend_per_process = TRUE;
-               #else
-               backend_per_process = FALSE;
-               #endif
-       }
-
-       return backend_per_process;
-}
-
 static gchar *
-data_factory_dup_subprocess_helper_hash_key (gint override_backend_per_process,
-                                            const gchar *factory_name,
+data_factory_dup_subprocess_helper_hash_key (const gchar *factory_name,
                                             const gchar *extension_name,
                                             const gchar *uid,
+                                            gboolean backend_per_process,
                                             gboolean backend_factory_share_subprocess)
 {
        gchar *helper_hash_key;
 
-       if (data_factory_use_backend_per_process (override_backend_per_process)) {
+       if (backend_per_process) {
                helper_hash_key = backend_factory_share_subprocess ?
                        g_strdup (factory_name) : g_strdup_printf ("%s:%s:%s", factory_name, extension_name, 
uid);
        } else {
@@ -962,6 +977,8 @@ data_factory_finalize (GObject *object)
        g_hash_table_destroy (priv->watched_names);
        g_mutex_clear (&priv->watched_names_lock);
 
+       g_hash_table_destroy (priv->opened_backends);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_data_factory_parent_class)->finalize (object);
 }
@@ -1127,6 +1144,8 @@ e_data_factory_init (EDataFactory *data_factory)
                (GDestroyNotify) g_free,
                (GDestroyNotify) watched_names_value_free);
 
+       data_factory->priv->opened_backends = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
opened_backend_data_free);
+
        data_factory->priv->spawn_subprocess_state = DATA_FACTORY_SPAWN_SUBPROCESS_NONE;
        data_factory->priv->reload_supported = FALSE;
        data_factory->priv->backend_per_process = -1;
@@ -1229,6 +1248,50 @@ e_data_factory_construct_path (EDataFactory *data_factory)
 }
 
 static void
+data_factory_backend_toggle_notify_cb (gpointer user_data,
+                                      GObject *backend,
+                                      gboolean is_last_ref)
+{
+       if (is_last_ref) {
+               EDataFactory *data_factory = E_DATA_FACTORY (user_data);
+               gboolean found = FALSE;
+
+               g_object_ref (backend);
+
+               g_object_remove_toggle_ref (
+                       backend, data_factory_backend_toggle_notify_cb, user_data);
+
+               g_signal_emit_by_name (backend, "shutdown");
+
+               g_mutex_lock (&data_factory->priv->mutex);
+               if (data_factory->priv->opened_backends) {
+                       GHashTableIter iter;
+                       gpointer key, value;
+
+                       g_hash_table_iter_init (&iter, data_factory->priv->opened_backends);
+                       while (g_hash_table_iter_next (&iter, &key, &value)) {
+                               OpenedBackendData *obd = value;
+
+                               if (obd && obd->backend == (EBackend *) backend) {
+                                       g_hash_table_remove (data_factory->priv->opened_backends, key);
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+               }
+               g_mutex_unlock (&data_factory->priv->mutex);
+
+               if (!found) {
+                       g_warn_if_reached ();
+                       g_object_unref (backend);
+               }
+
+               /* Also drop the "reference" on the data_factory */
+               e_dbus_server_release (E_DBUS_SERVER (data_factory));
+       }
+}
+
+static void
 data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
                                       GDBusMethodInvocation *invocation,
                                       const gchar *uid,
@@ -1246,6 +1309,7 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
        guint watched_id = 0;
        gchar *backend_name = NULL;
        gchar *subprocess_helpers_hash_key;
+       gboolean backend_per_process;
        const gchar *factory_name;
        const gchar *filename;
        const gchar *type_name;
@@ -1265,26 +1329,83 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
                extension = e_source_get_extension (source, extension_name);
                backend_name = e_source_backend_dup_backend_name (extension);
        }
-       g_clear_object (&source);
 
        if (backend_name && *backend_name) {
                backend_factory = e_data_factory_ref_backend_factory (
                        data_factory, backend_name, extension_name);
        }
 
-       g_free (backend_name);
+       class = E_DATA_FACTORY_GET_CLASS (data_factory);
+       backend_per_process = e_data_factory_use_backend_per_process (data_factory);
+
+       if (!backend_per_process && backend_factory) {
+               gchar *object_path = NULL, *backend_key;
+               OpenedBackendData *obd;
+
+               backend_key = g_strconcat (backend_name, "\n", uid, "\n", extension_name, NULL);
+
+               g_mutex_lock (&data_factory->priv->mutex);
+               obd = g_hash_table_lookup (data_factory->priv->opened_backends, backend_key);
+               if (obd) {
+                       object_path = g_strdup (obd->object_path);
+                       g_object_ref (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));
+               }
+               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) {
+                               g_object_add_toggle_ref (
+                                       G_OBJECT (backend),
+                                       data_factory_backend_toggle_notify_cb,
+                                       data_factory);
+
+                               g_mutex_lock (&data_factory->priv->mutex);
+                               g_hash_table_insert (data_factory->priv->opened_backends, backend_key,
+                                       opened_backend_data_new (backend, object_path));
+                               g_mutex_unlock (&data_factory->priv->mutex);
+
+                               backend_key = NULL;
+
+                               /* Do not call e_dbus_server_release() here, otherwise the factory will close
+                                  shortly afterwards. The backend holds the "reference" to the factory. */
+                               /* e_dbus_server_release (E_DBUS_SERVER (data_factory)); */
+                       } else {
+                               g_clear_object (&backend);
+                       }
+               }
 
-       if (backend_factory) {
-               gboolean backend_per_process;
+               if (object_path) {
+                       EDBusServerClass *server_class;
 
+                       g_return_if_fail (class->complete_open != NULL);
+
+                       server_class = E_DBUS_SERVER_GET_CLASS (data_factory);
+
+                       class->complete_open (data_factory, invocation, object_path, server_class->bus_name, 
extension_name);
+
+                       g_mutex_lock (&priv->spawn_subprocess_lock);
+                       priv->spawn_subprocess_state = DATA_FACTORY_SPAWN_SUBPROCESS_NONE;
+                       g_cond_signal (&priv->spawn_subprocess_cond);
+                       g_mutex_unlock (&priv->spawn_subprocess_lock);
+               }
+
+               g_free (object_path);
+               g_free (backend_key);
+       } else if (backend_factory) {
                type_name = G_OBJECT_TYPE_NAME (backend_factory);
 
-               class = E_DATA_FACTORY_GET_CLASS (data_factory);
                factory_name = class->get_factory_name (backend_factory);
 
                subprocess_helpers_hash_key = data_factory_dup_subprocess_helper_hash_key (
-                       e_data_factory_get_backend_per_process (data_factory),
-                       factory_name, extension_name, uid, e_backend_factory_share_subprocess 
(backend_factory));
+                       factory_name, extension_name, uid, backend_per_process,
+                       e_backend_factory_share_subprocess (backend_factory));
 
                g_mutex_lock (&priv->mutex);
                helper = g_hash_table_lookup (
@@ -1306,7 +1427,9 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
                                filename);
 
                        g_object_unref (backend_factory);
+                       g_clear_object (&source);
                        g_free (subprocess_helpers_hash_key);
+                       g_free (backend_name);
 
                        return;
                }
@@ -1342,13 +1465,11 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
                g_hash_table_insert (priv->subprocess_watched_ids, g_strdup (sd->bus_name), GUINT_TO_POINTER 
(watched_id));
                g_mutex_unlock (&priv->subprocess_watched_ids_lock);
 
-               backend_per_process = data_factory_use_backend_per_process 
(e_data_factory_get_backend_per_process (data_factory));
-
                subprocess = g_subprocess_new (
                        G_SUBPROCESS_FLAGS_NONE,
                        &error,
                        subprocess_path,
-                       "--factory", backend_per_process ? sd->factory_name : "all",
+                       "--factory", sd->factory_name,
                        "--bus-name", sd->bus_name,
                        "--own-path", sd->path,
                        NULL);
@@ -1360,6 +1481,9 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
                        uid, extension_name);
        }
 
+       g_clear_object (&source);
+       g_free (backend_name);
+
        if (error != NULL) {
                e_dbus_server_release (E_DBUS_SERVER (data_factory));
 
@@ -1465,3 +1589,101 @@ e_data_factory_get_backend_per_process (EDataFactory *data_factory)
 
        return data_factory->priv->backend_per_process;
 }
+
+gboolean
+e_data_factory_use_backend_per_process (EDataFactory *data_factory)
+{
+       gboolean backend_per_process;
+       gint override_backend_per_process;
+
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), FALSE);
+
+       override_backend_per_process = e_data_factory_get_backend_per_process (data_factory);
+
+       if (override_backend_per_process == 0 || override_backend_per_process == 1) {
+               backend_per_process = override_backend_per_process == 1;
+       } else {
+               #ifdef ENABLE_BACKEND_PER_PROCESS
+               backend_per_process = TRUE;
+               #else
+               backend_per_process = FALSE;
+               #endif
+       }
+
+       return backend_per_process;
+}
+
+/* Used only when backend-per-process is off. Free the returned pointer
+   with g_object_unref(), if not NULL and no longer needed */
+EBackend *
+e_data_factory_create_backend (EDataFactory *data_factory,
+                              EBackendFactory *backend_factory,
+                              ESource *source)
+{
+       EDataFactoryClass *klass;
+
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+       g_return_val_if_fail (E_IS_BACKEND_FACTORY (backend_factory), NULL);
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+       klass = E_DATA_FACTORY_GET_CLASS (data_factory);
+       g_return_val_if_fail (klass->create_backend != NULL, NULL);
+
+       return klass->create_backend (data_factory, backend_factory, source);
+}
+
+/* Used only when backend-per-process is off. Free the returned string
+   with g_free(), when no longer needed */
+gchar *
+e_data_factory_open_backend (EDataFactory *data_factory,
+                            EBackend *backend,
+                            GDBusConnection *connection,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       EDataFactoryClass *klass;
+
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+       g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
+       g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+
+       klass = E_DATA_FACTORY_GET_CLASS (data_factory);
+       g_return_val_if_fail (klass->open_backend != NULL, NULL);
+
+       return klass->open_backend (data_factory, backend, connection, cancellable, error);
+}
+
+/* Whenever the backend is closed by any of its clients it should call
+   this function, thus the data_factory knows about it */
+void
+e_data_factory_backend_closed (EDataFactory *data_factory,
+                              EBackend *backend)
+{
+       g_return_if_fail (E_IS_DATA_FACTORY (data_factory));
+       g_return_if_fail (E_IS_BACKEND (backend));
+
+       g_object_unref (backend);
+}
+
+/* free with g_list_free_full (list, g_object_unref);, element is 'EBackend *' */
+GSList *
+e_data_factory_list_opened_backends (EDataFactory *data_factory)
+{
+       GSList *backends = NULL;
+       GHashTableIter iter;
+       gpointer value;
+
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+
+       g_mutex_lock (&data_factory->priv->mutex);
+       g_hash_table_iter_init (&iter, data_factory->priv->opened_backends);
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               OpenedBackendData *obd = value;
+
+               if (obd && obd->backend)
+                       backends = g_slist_prepend (backends, g_object_ref (obd->backend));
+       }
+       g_mutex_unlock (&data_factory->priv->mutex);
+
+       return backends;
+}
diff --git a/src/libebackend/e-data-factory.h b/src/libebackend/e-data-factory.h
index 8d4e5a0..7255f56 100644
--- a/src/libebackend/e-data-factory.h
+++ b/src/libebackend/e-data-factory.h
@@ -85,7 +85,16 @@ struct _EDataFactoryClass {
                                                 const gchar *bus_name,
                                                 const gchar *extension_name);
 
-       gpointer reserved[15];
+       EBackend *      (* create_backend)      (EDataFactory *data_factory,
+                                                EBackendFactory *backend_factory,
+                                                ESource *source);
+       gchar *         (* open_backend)        (EDataFactory *data_factory,
+                                                EBackend *backend,
+                                                GDBusConnection *connection,
+                                                GCancellable *cancellable,
+                                                GError **error);
+
+       gpointer reserved[13];
 };
 
 GType          e_data_factory_get_type         (void) G_GNUC_CONST;
@@ -107,6 +116,20 @@ gboolean   e_data_factory_get_reload_supported
                                                (EDataFactory *data_factory);
 gint           e_data_factory_get_backend_per_process
                                                (EDataFactory *data_factory);
+gboolean       e_data_factory_use_backend_per_process
+                                               (EDataFactory *data_factory);
+EBackend *     e_data_factory_create_backend   (EDataFactory *data_factory,
+                                                EBackendFactory *backend_factory,
+                                                ESource *source);
+gchar *                e_data_factory_open_backend     (EDataFactory *data_factory,
+                                                EBackend *backend,
+                                                GDBusConnection *connection,
+                                                GCancellable *cancellable,
+                                                GError **error);
+void           e_data_factory_backend_closed   (EDataFactory *data_factory,
+                                                EBackend *backend);
+GSList *       e_data_factory_list_opened_backends /* EBackend * */
+                                               (EDataFactory *data_factory);
 
 G_END_DECLS
 


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