[evolution-data-server] EBookClient: Create view objects in the D-Bus thread.



commit 2fa55517d744ff0640e6487cf662a757764749e1
Author: Matthew Barnes <mbarnes redhat com>
Date:   Wed Feb 27 20:42:32 2013 -0500

    EBookClient: Create view objects in the D-Bus thread.
    
    Flip-flop the sync/async implementations.  The view object should be
    instantiated from the dedicated D-Bus thread so its underlying proxy
    object shares the same main loop context as the client's proxy.
    
    Have the view object schedule idle callbacks on the client object's
    main loop context to emit change notification signals, so as not to
    expose the dedicated D-Bus thread to external signal handlers.

 addressbook/libebook/e-book-client-view.c |  349 ++++++++++++++++++++++++-----
 addressbook/libebook/e-book-client.c      |  103 +++++----
 2 files changed, 347 insertions(+), 105 deletions(-)
---
diff --git a/addressbook/libebook/e-book-client-view.c b/addressbook/libebook/e-book-client-view.c
index 5982295..d485525 100644
--- a/addressbook/libebook/e-book-client-view.c
+++ b/addressbook/libebook/e-book-client-view.c
@@ -38,6 +38,8 @@
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_BOOK_CLIENT_VIEW, EBookClientViewPrivate))
 
+typedef struct _SignalClosure SignalClosure;
+
 struct _EBookClientViewPrivate {
        EBookClient *client;
        GDBusProxy *dbus_proxy;
@@ -55,6 +57,15 @@ struct _EBookClientViewPrivate {
        gulong complete_handler_id;
 };
 
+struct _SignalClosure {
+       EBookClientView *client_view;
+       GSList *object_list;
+       GSList *string_list;
+       gchar *message;
+       guint percent;
+       GError *error;
+};
+
 enum {
        PROP_0,
        PROP_CLIENT,
@@ -91,6 +102,149 @@ typedef struct {
        guint signum;
 } NotificationData;
 
+static void
+signal_closure_free (SignalClosure *signal_closure)
+{
+       g_object_unref (signal_closure->client_view);
+
+       g_slist_free_full (
+               signal_closure->object_list,
+               (GDestroyNotify) g_object_unref);
+
+       g_slist_free_full (
+               signal_closure->string_list,
+               (GDestroyNotify) g_free);
+
+       g_free (signal_closure->message);
+
+       if (signal_closure->error != NULL)
+               g_error_free (signal_closure->error);
+
+       g_slice_free (SignalClosure, signal_closure);
+}
+
+static gboolean
+book_client_view_emit_objects_added_idle_cb (gpointer user_data)
+{
+       SignalClosure *signal_closure = user_data;
+
+       g_signal_emit (
+               signal_closure->client_view,
+               signals[OBJECTS_ADDED], 0,
+               signal_closure->object_list);
+
+       return FALSE;
+}
+
+static gboolean
+book_client_view_emit_objects_modified_idle_cb (gpointer user_data)
+{
+       SignalClosure *signal_closure = user_data;
+
+       g_signal_emit (
+               signal_closure->client_view,
+               signals[OBJECTS_MODIFIED], 0,
+               signal_closure->object_list);
+
+       return FALSE;
+}
+
+static gboolean
+book_client_view_emit_objects_removed_idle_cb (gpointer user_data)
+{
+       SignalClosure *signal_closure = user_data;
+
+       g_signal_emit (
+               signal_closure->client_view,
+               signals[OBJECTS_REMOVED], 0,
+               signal_closure->string_list);
+
+       return FALSE;
+}
+
+static gboolean
+book_client_view_emit_progress_idle_cb (gpointer user_data)
+{
+       SignalClosure *signal_closure = user_data;
+
+       g_signal_emit (
+               signal_closure->client_view,
+               signals[PROGRESS], 0,
+               signal_closure->percent,
+               signal_closure->message);
+
+       return FALSE;
+}
+
+static gboolean
+book_client_view_emit_complete_idle_cb (gpointer user_data)
+{
+       SignalClosure *signal_closure = user_data;
+
+       g_signal_emit (
+               signal_closure->client_view,
+               signals[COMPLETE], 0,
+               signal_closure->error);
+
+       return FALSE;
+}
+
+static void
+book_client_view_emit_objects_added (EBookClientView *client_view,
+                                     GSList *object_list)
+{
+       EBookClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
+
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       signal_closure->object_list = object_list;  /* takes ownership */
+
+       client = e_book_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
+
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               book_client_view_emit_objects_added_idle_cb,
+               signal_closure,
+               (GDestroyNotify) signal_closure_free);
+       g_source_attach (idle_source, main_context);
+       g_source_unref (idle_source);
+
+       g_main_context_unref (main_context);
+}
+
+static void
+book_client_view_emit_objects_modified (EBookClientView *client_view,
+                                        GSList *object_list)
+{
+       EBookClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
+
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       signal_closure->object_list = object_list;  /* takes ownership */
+
+       client = e_book_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
+
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               book_client_view_emit_objects_modified_idle_cb,
+               signal_closure,
+               (GDestroyNotify) signal_closure_free);
+       g_source_attach (idle_source, main_context);
+       g_source_unref (idle_source);
+
+       g_main_context_unref (main_context);
+}
+
 static gchar *
 direct_contacts_query (const gchar * const *uids)
 {
@@ -116,30 +270,44 @@ direct_contacts_query (const gchar * const *uids)
 
 static void
 direct_contacts_ready (GObject *source_object,
-                      GAsyncResult *res,
-                      gpointer user_data)
+                       GAsyncResult *result,
+                       gpointer user_data)
 {
        NotificationData *data = (NotificationData *)user_data;
        GSList *contacts = NULL;
        GError *error = NULL;
 
-       if (!e_data_book_get_contacts_finish (E_DATA_BOOK (source_object),
-                                             res, &contacts, &error)) {
-               g_warning ("Error fetching contacts directly: %s\n", error->message);
+       e_data_book_get_contacts_finish (
+               E_DATA_BOOK (source_object),
+               result, &contacts, &error);
+
+       if (error != NULL) {
+               g_warn_if_fail (contacts == NULL);
+               g_warning (
+                       "Error fetching contacts directly: %s\n",
+                       error->message);
                g_error_free (error);
+
+       } else if (data->signum == OBJECTS_ADDED) {
+               /* Takes ownership of the linked list. */
+               book_client_view_emit_objects_added (data->view, contacts);
+
+       } else if (data->signum == OBJECTS_MODIFIED) {
+               /* Takes ownership of the linked list. */
+               book_client_view_emit_objects_modified (data->view, contacts);
+
        } else {
-               g_signal_emit (data->view, data->signum, 0, contacts);
+               g_slist_free_full (contacts, (GDestroyNotify) g_object_unref);
        }
 
-       g_slist_free_full (contacts, (GDestroyNotify) g_object_unref);
        g_object_unref (data->view);
        g_slice_free (NotificationData, data);
 }
 
 static void
 direct_contacts_fetch (EBookClientView *view,
-                      const gchar * const *uids,
-                      guint signum)
+                       const gchar * const *uids,
+                       guint signum)
 {
        NotificationData *data;
        gchar *sexp = direct_contacts_query (uids);
@@ -151,13 +319,28 @@ direct_contacts_fetch (EBookClientView *view,
                GSList *contacts = NULL;
                GError *error = NULL;
 
-               if (!e_data_book_get_contacts_sync (view->priv->direct_book,
-                                                   sexp, &contacts, NULL, &error)) {
-                       g_warning ("Error fetching contacts directly: %s\n", error->message);
+               e_data_book_get_contacts_sync (
+                       view->priv->direct_book,
+                       sexp, &contacts, NULL, &error);
+
+               if (error != NULL) {
+                       g_warn_if_fail (contacts == NULL);
+                       g_warning (
+                               "Error fetching contacts directly: %s\n",
+                               error->message);
                        g_error_free (error);
+
+               } else if (signum == OBJECTS_ADDED) {
+                       /* Takes ownership of the linked list. */
+                       book_client_view_emit_objects_added (view, contacts);
+
+               } else if (signum == OBJECTS_MODIFIED) {
+                       /* Takes ownership of the linked list. */
+                       book_client_view_emit_objects_modified (view, contacts);
+
                } else {
-                       g_signal_emit (view, signum, 0, contacts);
-                       g_slist_free_full (contacts, (GDestroyNotify) g_object_unref);
+                       g_slist_free_full (
+                               contacts, (GDestroyNotify) g_object_unref);
                }
 
        } else {
@@ -168,8 +351,9 @@ direct_contacts_fetch (EBookClientView *view,
                data->view = g_object_ref (view);
                data->signum = signum;
 
-               e_data_book_get_contacts (view->priv->direct_book,
-                                         sexp, NULL, direct_contacts_ready, data);
+               e_data_book_get_contacts (
+                       view->priv->direct_book,
+                       sexp, NULL, direct_contacts_ready, data);
        }
 
        g_free (sexp);
@@ -178,25 +362,25 @@ direct_contacts_fetch (EBookClientView *view,
 static void
 book_client_view_objects_added_cb (EGdbusBookView *object,
                                    const gchar * const *vcards,
-                                   EBookClientView *view)
+                                   EBookClientView *client_view)
 {
-       const gchar * const *p;
        GSList *list = NULL;
+       gint ii;
 
-       if (!view->priv->running)
+       if (!client_view->priv->running)
                return;
 
        /* array contains UIDs only */
-       if (view->priv->direct_book) {
-               direct_contacts_fetch (view, vcards, signals[OBJECTS_ADDED]);
+       if (client_view->priv->direct_book != NULL) {
+               direct_contacts_fetch (client_view, vcards, OBJECTS_ADDED);
                return;
        }
 
        /* array contains both UID and vcard */
-       for (p = vcards; p[0] && p[1]; p += 2) {
+       for (ii = 0; vcards[ii] != NULL && vcards[ii + 1] != NULL; ii += 2) {
                EContact *contact;
-               const gchar *vcard = p[0];
-               const gchar *uid = p[1];
+               const gchar *vcard = vcards[ii];
+               const gchar *uid = vcards[ii + 1];
 
                contact = e_contact_new_from_vcard_with_uid (vcard, uid);
                list = g_slist_prepend (list, contact);
@@ -204,33 +388,32 @@ book_client_view_objects_added_cb (EGdbusBookView *object,
 
        list = g_slist_reverse (list);
 
-       g_signal_emit (view, signals[OBJECTS_ADDED], 0, list);
-
-       g_slist_free_full (list, (GDestroyNotify) g_object_unref);
+       /* Takes ownership of the linked list. */
+       book_client_view_emit_objects_added (client_view, list);
 }
 
 static void
 book_client_view_objects_modified_cb (EGdbusBookView *object,
                                       const gchar * const *vcards,
-                                      EBookClientView *view)
+                                      EBookClientView *client_view)
 {
-       const gchar * const *p;
        GSList *list = NULL;
+       gint ii;
 
-       if (!view->priv->running)
+       if (!client_view->priv->running)
                return;
 
        /* array contains UIDs only */
-       if (view->priv->direct_book) {
-               direct_contacts_fetch (view, vcards, signals[OBJECTS_MODIFIED]);
+       if (client_view->priv->direct_book != NULL) {
+               direct_contacts_fetch (client_view, vcards, OBJECTS_MODIFIED);
                return;
        }
 
        /* array contains both UID and vcard */
-       for (p = vcards; p[0] && p[1]; p += 2) {
+       for (ii = 0; vcards[ii] != NULL && vcards[ii + 1] != NULL; ii += 2) {
                EContact *contact;
-               const gchar *vcard = p[0];
-               const gchar *uid = p[1];
+               const gchar *vcard = vcards[ii];
+               const gchar *uid = vcards[ii + 1];
 
                contact = e_contact_new_from_vcard_with_uid (vcard, uid);
                list = g_slist_prepend (list, contact);
@@ -238,63 +421,113 @@ book_client_view_objects_modified_cb (EGdbusBookView *object,
 
        list = g_slist_reverse (list);
 
-       g_signal_emit (view, signals[OBJECTS_MODIFIED], 0, list);
-
-       g_slist_free_full (list, (GDestroyNotify) g_object_unref);
+       /* Takes ownership of the linked list. */
+       book_client_view_emit_objects_modified (client_view, list);
 }
 
 static void
 book_client_view_objects_removed_cb (EGdbusBookView *object,
                                      const gchar * const *ids,
-                                     EBookClientView *view)
+                                     EBookClientView *client_view)
 {
-       const gchar * const *p;
+       EBookClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
        GSList *list = NULL;
+       gint ii;
 
-       if (!view->priv->running)
+       if (!client_view->priv->running)
                return;
 
-       for (p = ids; *p; p++)
-               list = g_slist_prepend (list, (gchar *) *p);
+       for (ii = 0; ids[ii] != NULL; ii++)
+               list = g_slist_prepend (list, g_strdup (ids[ii]));
 
-       list = g_slist_reverse (list);
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       signal_closure->string_list = g_slist_reverse (list);
+
+       client = e_book_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
 
-       g_signal_emit (view, signals[OBJECTS_REMOVED], 0, list);
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               book_client_view_emit_objects_removed_idle_cb,
+               signal_closure,
+               (GDestroyNotify) signal_closure_free);
+       g_source_attach (idle_source, main_context);
+       g_source_unref (idle_source);
 
-       /* No need to free the values, our caller will */
-       g_slist_free (list);
+       g_main_context_unref (main_context);
 }
 
 static void
 book_client_view_progress_cb (EGdbusBookView *object,
                               guint percent,
                               const gchar *message,
-                              EBookClientView *view)
+                              EBookClientView *client_view)
 {
-       if (!view->priv->running)
+       EBookClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
+
+       if (!client_view->priv->running)
                return;
 
-       g_signal_emit (view, signals[PROGRESS], 0, percent, message);
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       signal_closure->message = g_strdup (message);
+       signal_closure->percent = percent;
+
+       client = e_book_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
+
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               book_client_view_emit_progress_idle_cb,
+               signal_closure,
+               (GDestroyNotify) signal_closure_free);
+       g_source_attach (idle_source, main_context);
+       g_source_unref (idle_source);
+
+       g_main_context_unref (main_context);
 }
 
 static void
 book_client_view_complete_cb (EGdbusBookView *object,
                               const gchar * const *in_error_strv,
-                              EBookClientView *view)
+                              EBookClientView *client_view)
 {
-       GError *error = NULL;
+       EBookClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
 
-       if (!view->priv->running)
+       if (!client_view->priv->running)
                return;
 
-       view->priv->complete = TRUE;
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       e_gdbus_templates_decode_error (in_error_strv, &signal_closure->error);
 
-       g_return_if_fail (e_gdbus_templates_decode_error (in_error_strv, &error));
+       client = e_book_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
 
-       g_signal_emit (view, signals[COMPLETE], 0, error);
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               book_client_view_emit_complete_idle_cb,
+               signal_closure,
+               (GDestroyNotify) signal_closure_free);
+       g_source_attach (idle_source, main_context);
+       g_source_unref (idle_source);
 
-       if (error != NULL)
-               g_error_free (error);
+       g_main_context_unref (main_context);
+
+       client_view->priv->complete = TRUE;
 }
 
 static void
diff --git a/addressbook/libebook/e-book-client.c b/addressbook/libebook/e-book-client.c
index 854bb90..69f6b40 100644
--- a/addressbook/libebook/e-book-client.c
+++ b/addressbook/libebook/e-book-client.c
@@ -3109,20 +3109,56 @@ e_book_client_get_contacts_uids_sync (EBookClient *client,
 
 /* Helper for e_book_client_get_view() */
 static void
-book_client_get_view_thread (GSimpleAsyncResult *simple,
-                             GObject *source_object,
-                             GCancellable *cancellable)
+book_client_get_view_in_dbus_thread (GSimpleAsyncResult *simple,
+                                     GObject *source_object,
+                                     GCancellable *cancellable)
 {
+       EBookClient *client = E_BOOK_CLIENT (source_object);
        AsyncContext *async_context;
+       gchar *utf8_sexp;
+       gchar *object_path = NULL;
        GError *error = NULL;
 
        async_context = g_simple_async_result_get_op_res_gpointer (simple);
 
-       e_book_client_get_view_sync (
-               E_BOOK_CLIENT (source_object),
-               async_context->sexp,
-               &async_context->client_view,
-               cancellable, &error);
+       utf8_sexp = e_util_utf8_make_valid (async_context->sexp);
+
+       e_dbus_address_book_call_get_view_sync (
+               client->priv->dbus_proxy, utf8_sexp,
+               &object_path, cancellable, &error);
+
+       g_free (utf8_sexp);
+
+       /* Sanity check. */
+       g_return_if_fail (
+               ((object_path != NULL) && (error == NULL)) ||
+               ((object_path == NULL) && (error != NULL)));
+
+       if (object_path != NULL) {
+               GDBusConnection *connection;
+               EBookClientView *client_view;
+
+               connection = g_dbus_proxy_get_connection (
+                       G_DBUS_PROXY (client->priv->dbus_proxy));
+
+               client_view = g_initable_new (
+                       E_TYPE_BOOK_CLIENT_VIEW,
+                       cancellable, &error,
+                       "client", client,
+                       "connection", connection,
+                       "object-path", object_path,
+                       "direct-book", client->priv->direct_book,
+                       NULL);
+
+               /* Sanity check. */
+               g_return_if_fail (
+                       ((client_view != NULL) && (error == NULL)) ||
+                       ((client_view == NULL) && (error != NULL)));
+
+               async_context->client_view = client_view;
+
+               g_free (object_path);
+       }
 
        if (error != NULL)
                g_simple_async_result_take_error (simple, error);
@@ -3170,8 +3206,8 @@ e_book_client_get_view (EBookClient *client,
        g_simple_async_result_set_op_res_gpointer (
                simple, async_context, (GDestroyNotify) async_context_free);
 
-       g_simple_async_result_run_in_thread (
-               simple, book_client_get_view_thread,
+       book_client_run_in_dbus_thread (
+               simple, book_client_get_view_in_dbus_thread,
                G_PRIORITY_DEFAULT, cancellable);
 
        g_object_unref (simple);
@@ -3246,53 +3282,26 @@ e_book_client_get_view_sync (EBookClient *client,
                              GCancellable *cancellable,
                              GError **error)
 {
-       gchar *utf8_sexp;
-       gchar *object_path = NULL;
+       EAsyncClosure *closure;
+       GAsyncResult *result;
        gboolean success;
 
        g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
        g_return_val_if_fail (sexp != NULL, FALSE);
        g_return_val_if_fail (out_view != NULL, FALSE);
 
-       utf8_sexp = e_util_utf8_make_valid (sexp);
-
-       success = e_dbus_address_book_call_get_view_sync (
-               client->priv->dbus_proxy, utf8_sexp,
-               &object_path, cancellable, error);
-
-       g_free (utf8_sexp);
-
-       /* Sanity check. */
-       g_return_val_if_fail (
-               (success && (object_path != NULL)) ||
-               (!success && (object_path == NULL)), FALSE);
-
-       if (object_path != NULL) {
-               GDBusConnection *connection;
-               EBookClientView *client_view;
+       closure = e_async_closure_new ();
 
-               connection = g_dbus_proxy_get_connection (
-                       G_DBUS_PROXY (client->priv->dbus_proxy));
+       e_book_client_get_view (
+               client, sexp, cancellable,
+               e_async_closure_callback, closure);
 
-               client_view = g_initable_new (
-                       E_TYPE_BOOK_CLIENT_VIEW,
-                       cancellable, error,
-                       "client", client,
-                       "connection", connection,
-                       "object-path", object_path,
-                       "direct-book", client->priv->direct_book,
-                       NULL);
+       result = e_async_closure_wait (closure);
 
-               /* XXX Would have been easier to return the
-                *     EBookClientView directly rather than
-                *     through an "out" parameter. */
-               if (client_view != NULL)
-                       *out_view = client_view;
-               else
-                       success = FALSE;
+       success = e_book_client_get_view_finish (
+               client, result, out_view, error);
 
-               g_free (object_path);
-       }
+       e_async_closure_free (closure);
 
        return success;
 }


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