[evolution-data-server] [Camel] Introduce camel_operation_new_proxy()



commit 5297c58cdad4a041c4b0a443d901f0aeca3382c3
Author: Milan Crha <mcrha redhat com>
Date:   Tue Nov 29 10:07:41 2016 +0100

    [Camel] Introduce camel_operation_new_proxy()
    
    It creates a new CamelOperation, which listens for 'cancelled' signal
    of the original operation, but not the other way around. That allows
    to cancel the operation without influencing the passed-in cancellable.
    Other CamelOperation changes (push/pop message and progress) are also
    passed to the original cancellable, being it a CamelOperation as well.
    
    That's because there could happen that for example CamelService's
    connect and disconnect do cancel the passed-in cancellable on its own,
    but that's wrong, because it can cancel any larger operation to which
    it belongs, like when going online or offline from UI, on user's request.

 src/camel/camel-operation.c                        |   86 ++++++++++++++++++--
 src/camel/camel-operation.h                        |    1 +
 src/camel/camel-service.c                          |   10 +--
 .../providers/imapx/camel-imapx-conn-manager.c     |    6 +-
 4 files changed, 84 insertions(+), 19 deletions(-)
---
diff --git a/src/camel/camel-operation.c b/src/camel/camel-operation.c
index b160c90..7cd5d3b 100644
--- a/src/camel/camel-operation.c
+++ b/src/camel/camel-operation.c
@@ -46,6 +46,8 @@ struct _StatusNode {
 
 struct _CamelOperationPrivate {
        GQueue status_stack;
+       GCancellable *proxying;
+       gulong proxying_handler_id;
 };
 
 enum {
@@ -137,6 +139,15 @@ operation_emit_status_cb (gpointer user_data)
 }
 
 static void
+proxying_cancellable_cancelled_cb (GCancellable *cancellable,
+                                  GCancellable *operation)
+{
+       g_return_if_fail (CAMEL_IS_OPERATION (operation));
+
+       g_cancellable_cancel (operation);
+}
+
+static void
 operation_finalize (GObject *object)
 {
        CamelOperationPrivate *priv;
@@ -145,6 +156,13 @@ operation_finalize (GObject *object)
 
        LOCK ();
 
+       if (priv->proxying && priv->proxying_handler_id) {
+               g_cancellable_disconnect (priv->proxying, priv->proxying_handler_id);
+               priv->proxying_handler_id = 0;
+       }
+
+       g_clear_object (&priv->proxying);
+
        g_queue_remove (&operation_list, object);
 
        /* Because each StatusNode holds a reference to its
@@ -211,6 +229,8 @@ camel_operation_init (CamelOperation *operation)
        operation->priv = CAMEL_OPERATION_GET_PRIVATE (operation);
 
        g_queue_init (&operation->priv->status_stack);
+       operation->priv->proxying = NULL;
+       operation->priv->proxying_handler_id = 0;
 
        LOCK ();
        g_queue_push_tail (&operation_list, operation);
@@ -226,7 +246,7 @@ camel_operation_init (CamelOperation *operation)
  * operations and to obtain notification messages of the internal
  * status of messages.
  *
- * Returns: A new operation handle.
+ * Returns: (transfer full): A new operation handle.
  **/
 GCancellable *
 camel_operation_new (void)
@@ -235,6 +255,50 @@ camel_operation_new (void)
 }
 
 /**
+ * camel_operation_new_proxy:
+ * @cancellable: (nullable): a #GCancellable to proxy
+ *
+ * Proxies the @cancellable in a way that if it is cancelled,
+ * then the returned cancellable is also cancelled, but when
+ * the returned cancellable is cancelled, then it doesn't
+ * influence the original cancellable. Other CamelOperation
+ * actions being done on the returned cancellable are also
+ * propagated to the @cancellable.
+ *
+ * The passed-in @cancellable can be %NULL, in which case
+ * a plain CamelOperation is returned.
+ *
+ * This is useful when some operation can be cancelled from
+ * elsewhere (like by a user), but also by the code on its own,
+ * when it doesn't make sense to cancel also any larger operation
+ * to which the passed-in cancellable belongs.
+ *
+ * Returns: (transfer full): A new operation handle, proxying @cancellable.
+ *
+ * Since: 3.24
+ **/
+GCancellable *
+camel_operation_new_proxy (GCancellable *cancellable)
+{
+       GCancellable *operation;
+       CamelOperationPrivate *priv;
+
+       operation = camel_operation_new ();
+
+       if (!G_IS_CANCELLABLE (cancellable))
+               return operation;
+
+       priv = CAMEL_OPERATION_GET_PRIVATE (operation);
+       g_return_val_if_fail (priv != NULL, operation);
+
+       priv->proxying = g_object_ref (cancellable);
+       priv->proxying_handler_id = g_cancellable_connect (cancellable,
+               G_CALLBACK (proxying_cancellable_cancelled_cb), operation, NULL);
+
+       return operation;
+}
+
+/**
  * camel_operation_cancel_all:
  *
  * Cancel all outstanding operations.
@@ -294,11 +358,14 @@ camel_operation_push_message (GCancellable *cancellable,
        message = g_strdup_vprintf (format, ap);
        va_end (ap);
 
+       operation = CAMEL_OPERATION (cancellable);
+
        g_signal_emit (cancellable, signals[PUSH_MESSAGE], 0, message);
 
-       LOCK ();
+       if (operation->priv->proxying)
+               camel_operation_push_message (operation->priv->proxying, "%s", message);
 
-       operation = CAMEL_OPERATION (cancellable);
+       LOCK ();
 
        node = status_node_new ();
        node->message = message; /* takes ownership */
@@ -350,11 +417,15 @@ camel_operation_pop_message (GCancellable *cancellable)
 
        g_return_if_fail (CAMEL_IS_OPERATION (cancellable));
 
+       operation = CAMEL_OPERATION (cancellable);
+
        g_signal_emit (cancellable, signals[POP_MESSAGE], 0);
 
+       if (operation->priv->proxying)
+               camel_operation_pop_message (operation->priv->proxying);
+
        LOCK ();
 
-       operation = CAMEL_OPERATION (cancellable);
        node = g_queue_pop_head (&operation->priv->status_stack);
 
        if (node != NULL) {
@@ -411,11 +482,15 @@ camel_operation_progress (GCancellable *cancellable,
 
        g_return_if_fail (CAMEL_IS_OPERATION (cancellable));
 
+       operation = CAMEL_OPERATION (cancellable);
+
        g_signal_emit (cancellable, signals[PROGRESS], 0, percent);
 
+       if (operation->priv->proxying)
+               camel_operation_progress (operation->priv->proxying, percent);
+
        LOCK ();
 
-       operation = CAMEL_OPERATION (cancellable);
        node = g_queue_peek_head (&operation->priv->status_stack);
 
        if (node != NULL) {
@@ -436,4 +511,3 @@ camel_operation_progress (GCancellable *cancellable,
 
        UNLOCK ();
 }
-
diff --git a/src/camel/camel-operation.h b/src/camel/camel-operation.h
index 1452470..691edfd 100644
--- a/src/camel/camel-operation.h
+++ b/src/camel/camel-operation.h
@@ -68,6 +68,7 @@ struct _CamelOperationClass {
 
 GType          camel_operation_get_type        (void);
 GCancellable * camel_operation_new             (void);
+GCancellable * camel_operation_new_proxy       (GCancellable *cancellable);
 void           camel_operation_cancel_all      (void);
 
 /* Since Camel methods pass around GCancellable pointers instead of
diff --git a/src/camel/camel-service.c b/src/camel/camel-service.c
index e53f7d1..4e202dc 100644
--- a/src/camel/camel-service.c
+++ b/src/camel/camel-service.c
@@ -1810,10 +1810,7 @@ camel_service_connect (CamelService *service,
 
        g_return_if_fail (CAMEL_IS_SERVICE (service));
 
-       if (cancellable)
-               g_object_ref (cancellable);
-       else
-               cancellable = g_cancellable_new ();
+       cancellable = camel_operation_new_proxy (cancellable);
 
        task = g_task_new (service, cancellable, callback, user_data);
        g_task_set_source_tag (task, camel_service_connect);
@@ -1982,10 +1979,7 @@ camel_service_disconnect (CamelService *service,
 
        g_return_if_fail (CAMEL_IS_SERVICE (service));
 
-       if (cancellable)
-               g_object_ref (cancellable);
-       else
-               cancellable = g_cancellable_new ();
+       cancellable = camel_operation_new_proxy (cancellable);
 
        task = g_task_new (service, cancellable, callback, user_data);
        g_task_set_source_tag (task, camel_service_disconnect);
diff --git a/src/camel/providers/imapx/camel-imapx-conn-manager.c 
b/src/camel/providers/imapx/camel-imapx-conn-manager.c
index e82de56..8eea37b 100644
--- a/src/camel/providers/imapx/camel-imapx-conn-manager.c
+++ b/src/camel/providers/imapx/camel-imapx-conn-manager.c
@@ -857,11 +857,7 @@ camel_imapx_conn_manager_ref_connection (CamelIMAPXConnManager *conn_man,
            session && camel_session_get_online (session)) {
 
                g_mutex_lock (&conn_man->priv->pending_connections_lock);
-               if (cancellable) {
-                       g_object_ref (cancellable);
-               } else {
-                       cancellable = g_cancellable_new ();
-               }
+               cancellable = camel_operation_new_proxy (cancellable);
                conn_man->priv->pending_connections = g_slist_prepend (conn_man->priv->pending_connections, 
cancellable);
                g_mutex_unlock (&conn_man->priv->pending_connections_lock);
 


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