[evolution-data-server] Bug 793031 - Decrease memory usage by disabling backend-per-process by default ][
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug 793031 - Decrease memory usage by disabling backend-per-process by default ][
- Date: Mon, 12 Mar 2018 13:06:55 +0000 (UTC)
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]