ECalClient: Create view objects in the D-Bus thread.
authorMatthew Barnes <mbarnes@redhat.com>
Wed, 27 Feb 2013 23:42:46 +0000 (18:42 -0500)
committerMatthew Barnes <mbarnes@redhat.com>
Thu, 28 Feb 2013 01:53:50 +0000 (20:53 -0500)
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.

calendar/libecal/e-cal-client-view.c
calendar/libecal/e-cal-client.c

index 00b9841..43a0f64 100644 (file)
@@ -39,6 +39,8 @@
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_CAL_CLIENT_VIEW, ECalClientViewPrivate))
 
+typedef struct _SignalClosure SignalClosure;
+
 struct _ECalClientViewPrivate {
        ECalClient *client;
        GDBusProxy *dbus_proxy;
@@ -53,6 +55,15 @@ struct _ECalClientViewPrivate {
        gulong complete_handler_id;
 };
 
+struct _SignalClosure {
+       ECalClientView *client_view;
+       GSList *component_list;
+       GSList *component_id_list;
+       gchar *message;
+       guint percent;
+       GError *error;
+};
+
 enum {
        PROP_0,
        PROP_CLIENT,
@@ -82,6 +93,27 @@ G_DEFINE_TYPE_WITH_CODE (
                G_TYPE_INITABLE,
                e_cal_client_view_initable_init))
 
+static void
+signal_closure_free (SignalClosure *signal_closure)
+{
+       g_object_unref (signal_closure->client_view);
+
+       g_slist_free_full (
+               signal_closure->component_list,
+               (GDestroyNotify) icalcomponent_free);
+
+       g_slist_free_full (
+               signal_closure->component_id_list,
+               (GDestroyNotify) e_cal_component_free_id);
+
+       g_free (signal_closure->message);
+
+       if (signal_closure->error != NULL)
+               g_error_free (signal_closure->error);
+
+       g_slice_free (SignalClosure, signal_closure);
+}
+
 static GSList *
 build_object_list (const gchar * const *seq)
 {
@@ -131,97 +163,232 @@ build_id_list (const gchar * const *seq)
        return g_slist_reverse (list);
 }
 
+static gboolean
+cal_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->component_list);
+
+       return FALSE;
+}
+
+static gboolean
+cal_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->component_list);
+
+       return FALSE;
+}
+
+static gboolean
+cal_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->component_id_list);
+
+       return FALSE;
+}
+
+static gboolean
+cal_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
+cal_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
 cal_client_view_objects_added_cb (EGdbusCalView *dbus_proxy,
                                   const gchar * const *objects,
-                                  ECalClientView *view)
+                                  ECalClientView *client_view)
 {
-       GSList *list;
+       ECalClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
 
-       if (!view->priv->running)
+       if (!client_view->priv->running)
                return;
 
-       g_object_ref (view);
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       signal_closure->component_list = build_object_list (objects);
 
-       list = build_object_list (objects);
+       client = e_cal_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
 
-       g_signal_emit (view, signals[OBJECTS_ADDED], 0, list);
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               cal_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_slist_free_full (list, (GDestroyNotify) icalcomponent_free);
-
-       g_object_unref (view);
+       g_main_context_unref (main_context);
 }
 
 static void
 cal_client_view_objects_modified_cb (EGdbusCalView *dbus_proxy,
                                      const gchar * const *objects,
-                                     ECalClientView *view)
+                                     ECalClientView *client_view)
 {
-       GSList *list;
+       ECalClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
 
-       if (!view->priv->running)
+       if (!client_view->priv->running)
                return;
 
-       g_object_ref (view);
-
-       list = build_object_list (objects);
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       signal_closure->component_list = build_object_list (objects);
 
-       g_signal_emit (view, signals[OBJECTS_MODIFIED], 0, list);
+       client = e_cal_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
 
-       g_slist_free_full (list, (GDestroyNotify) icalcomponent_free);
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               cal_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_object_unref (view);
+       g_main_context_unref (main_context);
 }
 
 static void
 cal_client_view_objects_removed_cb (EGdbusCalView *dbus_proxy,
                                     const gchar * const *uids,
-                                    ECalClientView *view)
+                                    ECalClientView *client_view)
 {
-       GSList *list;
+       ECalClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
 
-       if (!view->priv->running)
+       if (!client_view->priv->running)
                return;
 
-       g_object_ref (view);
-
-       list = build_id_list (uids);
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       signal_closure->component_id_list = build_id_list (uids);
 
-       g_signal_emit (view, signals[OBJECTS_REMOVED], 0, list);
+       client = e_cal_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
 
-       g_slist_free_full (list, (GDestroyNotify) e_cal_component_free_id);
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               cal_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);
 
-       g_object_unref (view);
+       g_main_context_unref (main_context);
 }
 
 static void
 cal_client_view_progress_cb (EGdbusCalView *dbus_proxy,
                              guint percent,
                              const gchar *message,
-                             ECalClientView *view)
+                             ECalClientView *client_view)
 {
-       if (!view->priv->running)
+       ECalClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
+
+       if (!client_view->priv->running)
                return;
 
-       g_signal_emit (G_OBJECT (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_cal_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,
+               cal_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
 cal_client_view_complete_cb (EGdbusCalView *dbus_proxy,
                              const gchar * const *arg_error,
-                             ECalClientView *view)
+                             ECalClientView *client_view)
 {
-       GError *error = NULL;
+       ECalClient *client;
+       GSource *idle_source;
+       GMainContext *main_context;
+       SignalClosure *signal_closure;
 
-       if (!view->priv->running)
+       if (!client_view->priv->running)
                return;
 
-       g_return_if_fail (e_gdbus_templates_decode_error (arg_error, &error));
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client_view = g_object_ref (client_view);
+       e_gdbus_templates_decode_error (arg_error, &signal_closure->error);
+
+       client = e_cal_client_view_get_client (client_view);
+       main_context = e_client_ref_main_context (E_CLIENT (client));
 
-       g_signal_emit (G_OBJECT (view), signals[COMPLETE], 0, error);
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               cal_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);
 }
 
 static void
index 020af32..1468b85 100644 (file)
@@ -5913,20 +5913,55 @@ e_cal_client_discard_alarm_sync (ECalClient *client,
 
 /* Helper for e_cal_client_get_view() */
 static void
-cal_client_get_view_thread (GSimpleAsyncResult *simple,
-                            GObject *source_object,
-                            GCancellable *cancellable)
+cal_client_get_view_in_dbus_thread (GSimpleAsyncResult *simple,
+                                    GObject *source_object,
+                                    GCancellable *cancellable)
 {
+       ECalClient *client = E_CAL_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_cal_client_get_view_sync (
-               E_CAL_CLIENT (source_object),
-               async_context->sexp,
-               &async_context->client_view,
-               cancellable, &error);
+       utf8_sexp = e_util_utf8_make_valid (async_context->sexp);
+
+       e_dbus_calendar_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;
+               ECalClientView *client_view;
+
+               connection = g_dbus_proxy_get_connection (
+                       G_DBUS_PROXY (client->priv->dbus_proxy));
+
+               client_view = g_initable_new (
+                       E_TYPE_CAL_CLIENT_VIEW,
+                       cancellable, &error,
+                       "client", client,
+                       "connection", connection,
+                       "object-path", object_path,
+                       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);
@@ -5971,8 +6006,8 @@ e_cal_client_get_view (ECalClient *client,
        g_simple_async_result_set_op_res_gpointer (
                simple, async_context, (GDestroyNotify) async_context_free);
 
-       g_simple_async_result_run_in_thread (
-               simple, cal_client_get_view_thread,
+       cal_client_run_in_dbus_thread (
+               simple, cal_client_get_view_in_dbus_thread,
                G_PRIORITY_DEFAULT, cancellable);
 
        g_object_unref (simple);
@@ -6044,52 +6079,26 @@ e_cal_client_get_view_sync (ECalClient *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_CAL_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_calendar_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;
-               ECalClientView *client_view;
+       closure = e_async_closure_new ();
 
-               connection = g_dbus_proxy_get_connection (
-                       G_DBUS_PROXY (client->priv->dbus_proxy));
+       e_cal_client_get_view (
+               client, sexp, cancellable,
+               e_async_closure_callback, closure);
 
-               client_view = g_initable_new (
-                       E_TYPE_CAL_CLIENT_VIEW,
-                       cancellable, error,
-                       "client", client,
-                       "connection", connection,
-                       "object-path", object_path,
-                       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_cal_client_get_view_finish (
+               client, result, out_view, error);
 
-               g_free (object_path);
-       }
+       e_async_closure_free (closure);
 
        return success;
 }