X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fmodest-mail-operation-queue.c;h=7bf680bc813f3c1e41382f292e73b66025cf39f3;hp=cf536b92ab2a8d7c81feed0113a2eeb996a4188a;hb=40a8f5cbe03bd040fe3403dbfa6806df4fb6bc8a;hpb=1386f3b566f5b5c9aa2ea4d0a5a6157502a45b4f diff --git a/src/modest-mail-operation-queue.c b/src/modest-mail-operation-queue.c index cf536b9..7bf680b 100644 --- a/src/modest-mail-operation-queue.c +++ b/src/modest-mail-operation-queue.c @@ -37,11 +37,10 @@ static void modest_mail_operation_queue_class_init (ModestMailOperationQueueClas static void modest_mail_operation_queue_init (ModestMailOperationQueue *obj); static void modest_mail_operation_queue_finalize (GObject *obj); -static void modest_mail_operation_queue_cancel_no_block_wrapper (ModestMailOperation *mail_op, - ModestMailOperationQueue *op_queue); - -static void modest_mail_operation_queue_cancel_no_block (ModestMailOperationQueue *op_queue, - ModestMailOperation *mail_op); +static void +on_progress_changed (ModestMailOperation *mail_op, + ModestMailOperationState *state, + gpointer user_data); /* list my signals */ enum { @@ -53,6 +52,7 @@ typedef struct _ModestMailOperationQueuePrivate ModestMailOperationQueuePrivate; struct _ModestMailOperationQueuePrivate { GQueue *op_queue; GMutex *queue_lock; + guint op_id; }; #define MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MODEST_TYPE_MAIL_OPERATION_QUEUE, \ @@ -127,6 +127,29 @@ modest_mail_operation_queue_init (ModestMailOperationQueue *obj) priv->op_queue = g_queue_new (); priv->queue_lock = g_mutex_new (); + priv->op_id = 0; +} + +static void +on_finalize_foreach(gpointer op, + gpointer user_data) +{ + ModestMailOperationQueue *queue; + ModestMailOperationQueuePrivate *priv; + ModestMailOperation *mail_op; + + queue = MODEST_MAIL_OPERATION_QUEUE (user_data); + priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE (queue); + mail_op = MODEST_MAIL_OPERATION (op); + + /* Simply remove from queue, but without emitting a + * QUEUE_CHANGED_SIGNAL because we are in finalize anyway and have + * the lock acquired. */ + g_signal_handlers_disconnect_by_func (mail_op, G_CALLBACK (on_progress_changed), user_data); + + modest_mail_operation_cancel (mail_op); + g_queue_remove (priv->op_queue, mail_op); + g_object_unref (G_OBJECT (mail_op)); } static void @@ -139,8 +162,13 @@ modest_mail_operation_queue_finalize (GObject *obj) g_mutex_lock (priv->queue_lock); if (priv->op_queue) { - if (!g_queue_is_empty (priv->op_queue)) - g_queue_foreach (priv->op_queue, (GFunc) g_object_unref, NULL); + /* Cancel all */ + if (!g_queue_is_empty (priv->op_queue)) { + g_queue_foreach (priv->op_queue, + (GFunc)on_finalize_foreach, + MODEST_MAIL_OPERATION_QUEUE (obj)); + } + g_queue_free (priv->op_queue); } @@ -158,6 +186,21 @@ modest_mail_operation_queue_new (void) return MODEST_MAIL_OPERATION_QUEUE (self); } +static void +on_progress_changed (ModestMailOperation *mail_op, + ModestMailOperationState *state, + gpointer user_data) +{ + ModestMailOperationQueue *queue; + + if(!state->finished) + return; + + /* Remove operation from queue when finished */ + queue = MODEST_MAIL_OPERATION_QUEUE (user_data); + modest_mail_operation_queue_remove (queue, mail_op); +} + void modest_mail_operation_queue_add (ModestMailOperationQueue *self, ModestMailOperation *mail_op) @@ -171,18 +214,24 @@ modest_mail_operation_queue_add (ModestMailOperationQueue *self, g_mutex_lock (priv->queue_lock); g_queue_push_tail (priv->op_queue, g_object_ref (mail_op)); + modest_mail_operation_set_id (mail_op, priv->op_id++); g_mutex_unlock (priv->queue_lock); + /* Get notified when the operation ends to remove it from the queue */ + g_signal_connect (G_OBJECT (mail_op), "progress_changed", + G_CALLBACK (on_progress_changed), self); + /* Notify observers */ g_signal_emit (self, signals[QUEUE_CHANGED_SIGNAL], 0, mail_op, MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED); } void -modest_mail_operation_queue_remove (ModestMailOperationQueue *self, +modest_mail_operation_queue_remove (ModestMailOperationQueue *self, ModestMailOperation *mail_op) { ModestMailOperationQueuePrivate *priv; + ModestMailOperationStatus status; g_return_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self)); g_return_if_fail (MODEST_IS_MAIL_OPERATION (mail_op)); @@ -193,47 +242,59 @@ modest_mail_operation_queue_remove (ModestMailOperationQueue *self, g_queue_remove (priv->op_queue, mail_op); g_mutex_unlock (priv->queue_lock); - /* HACK see the documentation of the function. Remove this - call when tinymail provides accurate progress values */ - _modest_mail_operation_notify_end (mail_op); + g_signal_handlers_disconnect_by_func (G_OBJECT (mail_op), + G_CALLBACK (on_progress_changed), + self); /* Notify observers */ g_signal_emit (self, signals[QUEUE_CHANGED_SIGNAL], 0, mail_op, MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED); - /* TODO: errors? */ - { - const GError *err = modest_mail_operation_get_error (mail_op); - if (err) - g_warning (err->message); + /* Check errors */ + status = modest_mail_operation_get_status (mail_op); + if (status != MODEST_MAIL_OPERATION_STATUS_SUCCESS) { + /* This is a sanity check. Shouldn't be needed, but + prevent possible application crashes. It's useful + also for detecting mail operations with invalid + status and error handling */ + if (modest_mail_operation_get_error (mail_op) != NULL) { + modest_mail_operation_execute_error_handler (mail_op); + } else { + if (status == MODEST_MAIL_OPERATION_STATUS_CANCELED) + g_warning ("%s: operation canceled \n", __FUNCTION__); + else + g_warning ("%s: possible error in a mail operation " \ + "implementation. The status is not successful " \ + "but the mail operation does not have any " \ + "error set\n", __FUNCTION__); + } } /* Free object */ + + /* We do not own the last reference when this operation is deleted + * as response to a progress changed signal from the mail operation + * itself, in which case the glib signal system owns a reference + * until the signal emission is complete. armin. */ + /* modest_runtime_verify_object_last_ref (mail_op, ""); */ g_object_unref (G_OBJECT (mail_op)); - modest_runtime_verify_object_death (mail_op, ""); } - -/* Utility function intended to be used with g_queue_foreach */ -static void -modest_mail_operation_queue_cancel_no_block_wrapper (ModestMailOperation *self, - ModestMailOperationQueue *op_queue) +guint +modest_mail_operation_queue_num_elements (ModestMailOperationQueue *self) { - modest_mail_operation_queue_cancel_no_block (op_queue, self); -} + ModestMailOperationQueuePrivate *priv; + guint length = 0; -static void -modest_mail_operation_queue_cancel_no_block (ModestMailOperationQueue *self, - ModestMailOperation *mail_op) -{ - if (modest_mail_operation_is_finished (mail_op)) - return; + g_return_val_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self), 0); + + priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self); - /* TODO: the implementation is still empty */ - modest_mail_operation_cancel (mail_op); + g_mutex_lock (priv->queue_lock); + length = g_queue_get_length (priv->op_queue); + g_mutex_unlock (priv->queue_lock); - /* Remove from the queue */ - modest_mail_operation_queue_remove (self, mail_op); + return length; } void @@ -247,23 +308,48 @@ modest_mail_operation_queue_cancel (ModestMailOperationQueue *self, priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self); - g_mutex_lock (priv->queue_lock); - modest_mail_operation_queue_cancel_no_block (self, mail_op); - g_mutex_unlock (priv->queue_lock); + /* This triggers a progess_changed signal in which we remove + * the operation from the queue. */ + modest_mail_operation_cancel (mail_op); +} + +static void +on_cancel_all_foreach (gpointer op, gpointer list) +{ + GSList **new_list; + + new_list = (GSList**) list; + *new_list = g_slist_prepend (*new_list, MODEST_MAIL_OPERATION (op)); } void modest_mail_operation_queue_cancel_all (ModestMailOperationQueue *self) { ModestMailOperationQueuePrivate *priv; + GSList* operations_to_cancel = NULL; + GSList* cur = NULL; g_return_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self)); priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self); + /* Remember which operations to cancel. This is the only thing that + * is done while holding the lock, so we do not need to call + * functions from other files while holding the lock, which could + * lead to a deadlock if such a call re-enters into this queue and + * tries to acquire another lock. */ g_mutex_lock (priv->queue_lock); - g_queue_foreach (priv->op_queue, - (GFunc) modest_mail_operation_queue_cancel_no_block_wrapper, - self); + g_queue_foreach (priv->op_queue, (GFunc) on_cancel_all_foreach, &operations_to_cancel); g_mutex_unlock (priv->queue_lock); + + /* TODO: Reverse the list, to remove operations in order? */ + + for(cur = operations_to_cancel; cur != NULL; cur = cur->next) { + if (!MODEST_IS_MAIL_OPERATION(cur->data)) + g_printerr ("modest: cur->data is not a valid mail operation\n"); + else + modest_mail_operation_cancel (MODEST_MAIL_OPERATION (cur->data)); + } + + g_slist_free(operations_to_cancel); }