1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "modest-marshal.h"
32 #include "modest-mail-operation-queue.h"
33 #include "modest-runtime.h"
34 #include "modest-debug.h"
36 /* 'private'/'protected' functions */
37 static void modest_mail_operation_queue_class_init (ModestMailOperationQueueClass *klass);
38 static void modest_mail_operation_queue_init (ModestMailOperationQueue *obj);
39 static void modest_mail_operation_queue_finalize (GObject *obj);
42 on_operation_finished (ModestMailOperation *mail_op,
52 typedef struct _ModestMailOperationQueuePrivate ModestMailOperationQueuePrivate;
53 struct _ModestMailOperationQueuePrivate {
57 guint queue_empty_handler;
58 gboolean running_final_sync;
60 #define MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
61 MODEST_TYPE_MAIL_OPERATION_QUEUE, \
62 ModestMailOperationQueuePrivate))
64 static GObjectClass *parent_class = NULL;
66 static guint signals[NUM_SIGNALS] = {0};
69 modest_mail_operation_queue_get_type (void)
71 static GType my_type = 0;
73 static const GTypeInfo my_info = {
74 sizeof(ModestMailOperationQueueClass),
76 NULL, /* base finalize */
77 (GClassInitFunc) modest_mail_operation_queue_class_init,
78 NULL, /* class finalize */
79 NULL, /* class data */
80 sizeof(ModestMailOperationQueue),
82 (GInstanceInitFunc) modest_mail_operation_queue_init,
86 my_type = g_type_register_static (G_TYPE_OBJECT,
87 "ModestMailOperationQueue",
94 modest_mail_operation_queue_class_init (ModestMailOperationQueueClass *klass)
96 GObjectClass *gobject_class;
98 gobject_class = (GObjectClass*) klass;
99 parent_class = g_type_class_peek_parent (klass);
101 gobject_class->finalize = modest_mail_operation_queue_finalize;
103 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationQueuePrivate));
106 * ModestMailOperationQueue::queue-changed
107 * @self: the #ModestMailOperationQueue that emits the signal
108 * @mail_op: the #ModestMailOperation affected
109 * @type: the type of change in the queue
110 * @user_data: user data set when the signal handler was connected
112 * Emitted whenever the contents of the queue change
114 signals[QUEUE_CHANGED_SIGNAL] =
115 g_signal_new ("queue-changed",
116 G_TYPE_FROM_CLASS (gobject_class),
118 G_STRUCT_OFFSET (ModestMailOperationQueueClass, queue_changed),
120 modest_marshal_VOID__POINTER_INT,
121 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_INT);
124 * ModestMailOperationQueue::queue-empty
125 * @self: the #ModestMailOperationQueue that emits the signal
126 * @user_data: user data set when the signal handler was connected
128 * Issued whenever the queue is empty
130 signals[QUEUE_EMPTY_SIGNAL] =
131 g_signal_new ("queue-empty",
132 G_TYPE_FROM_CLASS (gobject_class),
134 G_STRUCT_OFFSET (ModestMailOperationQueueClass, queue_empty),
136 g_cclosure_marshal_VOID__VOID,
142 modest_mail_operation_queue_init (ModestMailOperationQueue *obj)
144 ModestMailOperationQueuePrivate *priv;
146 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(obj);
148 priv->op_queue = g_queue_new ();
149 priv->queue_lock = g_mutex_new ();
151 priv->queue_empty_handler = 0;
152 priv->running_final_sync = FALSE;
156 print_queue_item (ModestMailOperation *op, const gchar* prefix)
158 gchar *op_str = modest_mail_operation_to_string (op);
160 prefix ? prefix : "",
166 on_finalize_foreach(gpointer op,
169 ModestMailOperationQueue *queue;
170 ModestMailOperationQueuePrivate *priv;
171 ModestMailOperation *mail_op;
173 queue = MODEST_MAIL_OPERATION_QUEUE (user_data);
174 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE (queue);
175 mail_op = MODEST_MAIL_OPERATION (op);
177 /* Simply remove from queue, but without emitting a
178 * QUEUE_CHANGED_SIGNAL because we are in finalize anyway and have
179 * the lock acquired. */
180 g_signal_handlers_disconnect_by_func (mail_op, G_CALLBACK (on_operation_finished), user_data);
182 MODEST_DEBUG_BLOCK (print_queue_item (mail_op, "cancel/remove"););
184 modest_mail_operation_cancel (mail_op);
185 g_queue_remove (priv->op_queue, mail_op);
186 g_object_unref (G_OBJECT (mail_op));
190 modest_mail_operation_queue_finalize (GObject *obj)
192 ModestMailOperationQueuePrivate *priv;
194 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(obj);
196 g_mutex_lock (priv->queue_lock);
199 g_debug ("%s; items in queue: %d",
200 __FUNCTION__, g_queue_get_length (priv->op_queue));
201 g_queue_foreach (priv->op_queue, (GFunc)print_queue_item, "in queue");
205 if (priv->op_queue) {
207 if (!g_queue_is_empty (priv->op_queue)) {
208 g_queue_foreach (priv->op_queue,
209 (GFunc)on_finalize_foreach,
210 MODEST_MAIL_OPERATION_QUEUE (obj));
213 g_queue_free (priv->op_queue);
216 g_mutex_unlock (priv->queue_lock);
217 g_mutex_free (priv->queue_lock);
219 G_OBJECT_CLASS(parent_class)->finalize (obj);
222 ModestMailOperationQueue *
223 modest_mail_operation_queue_new (void)
225 ModestMailOperationQueue *self = g_object_new (MODEST_TYPE_MAIL_OPERATION_QUEUE, NULL);
227 return MODEST_MAIL_OPERATION_QUEUE (self);
231 on_operation_started (ModestMailOperation *mail_op,
234 ModestMailOperationQueue *self = MODEST_MAIL_OPERATION_QUEUE (user_data);
235 ModestMailOperationTypeOperation op_type;
236 ModestMailOperationQueuePrivate *priv;
239 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
241 op_type = modest_mail_operation_get_type_operation (mail_op);
242 device = modest_runtime_get_device ();
244 if ((op_type != MODEST_MAIL_OPERATION_TYPE_SHUTDOWN) &&
245 priv->running_final_sync &&
246 tny_device_is_forced (device))
247 tny_device_reset (device);
249 priv->running_final_sync = (op_type == MODEST_MAIL_OPERATION_TYPE_SHUTDOWN);
253 on_operation_finished (ModestMailOperation *mail_op,
256 ModestMailOperationQueue *queue = MODEST_MAIL_OPERATION_QUEUE (user_data);
258 /* Remove operation from queue when finished */
259 modest_mail_operation_queue_remove (queue, mail_op);
263 modest_mail_operation_queue_add (ModestMailOperationQueue *self,
264 ModestMailOperation *mail_op)
266 ModestMailOperationQueuePrivate *priv;
268 g_return_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self));
269 g_return_if_fail (MODEST_IS_MAIL_OPERATION (mail_op));
271 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
273 g_mutex_lock (priv->queue_lock);
274 g_queue_push_tail (priv->op_queue, g_object_ref (mail_op));
275 g_mutex_unlock (priv->queue_lock);
277 MODEST_DEBUG_BLOCK (print_queue_item (mail_op, "add"););
279 g_signal_connect (G_OBJECT (mail_op),
281 G_CALLBACK (on_operation_started),
284 /* Get notified when the operation ends to remove it from the
285 queue. We connect it using the *after* because we want to
286 let the other handlers for the finish function happen
288 g_signal_connect_after (G_OBJECT (mail_op),
289 "operation-finished",
290 G_CALLBACK (on_operation_finished),
293 /* Notify observers */
294 g_signal_emit (self, signals[QUEUE_CHANGED_SIGNAL], 0,
295 mail_op, MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED);
299 notify_queue_empty (gpointer user_data)
301 ModestMailOperationQueue *self = (ModestMailOperationQueue *) user_data;
302 ModestMailOperationQueuePrivate *priv;
305 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
307 g_mutex_lock (priv->queue_lock);
308 num_elements = priv->op_queue->length;
309 g_mutex_unlock (priv->queue_lock);
311 /* We re-check again that the queue is empty. It could happen
312 that we had issued a tny_send_queue_flush before the send
313 queue could add a mail operation to the queue as a response
314 to the "start-queue" signal, because that signal is issued
315 by tinymail in the main loop. Therefor it could happen that
316 we emit the queue-empty signal while a send-queue is still
317 waiting for the "start-queue" signal from tinymail, so the
318 send queue will never try to send the items because modest
319 is finalized before */
320 if (num_elements == 0) {
321 gdk_threads_enter ();
322 g_signal_emit (self, signals[QUEUE_EMPTY_SIGNAL], 0);
323 gdk_threads_leave ();
331 modest_mail_operation_queue_remove (ModestMailOperationQueue *self,
332 ModestMailOperation *mail_op)
334 ModestMailOperationQueuePrivate *priv;
335 ModestMailOperationStatus status;
338 g_return_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self));
339 g_return_if_fail (MODEST_IS_MAIL_OPERATION (mail_op));
341 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
343 g_mutex_lock (priv->queue_lock);
344 g_queue_remove (priv->op_queue, mail_op);
345 num_elements = priv->op_queue->length;
346 g_mutex_unlock (priv->queue_lock);
348 MODEST_DEBUG_BLOCK (print_queue_item (mail_op, "remove"););
350 g_signal_handlers_disconnect_by_func (G_OBJECT (mail_op),
351 G_CALLBACK (on_operation_finished),
354 /* Notify observers */
355 g_signal_emit (self, signals[QUEUE_CHANGED_SIGNAL], 0,
356 mail_op, MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED);
359 status = modest_mail_operation_get_status (mail_op);
360 if (status != MODEST_MAIL_OPERATION_STATUS_SUCCESS &&
361 status != MODEST_MAIL_OPERATION_STATUS_INVALID) {
362 /* This is a sanity check. Shouldn't be needed, but
363 prevent possible application crashes. It's useful
364 also for detecting mail operations with invalid
365 status and error handling */
366 if (modest_mail_operation_get_error (mail_op) != NULL) {
367 modest_mail_operation_execute_error_handler (mail_op);
369 if (status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
370 g_debug ("%s: operation canceled \n", __FUNCTION__);
372 g_warning ("%s: possible error in a mail operation " \
373 "implementation. The status is not successful " \
374 "but the mail operation does not have any " \
375 "error set\n", __FUNCTION__);
380 g_object_unref (G_OBJECT (mail_op));
382 /* Emit the queue empty-signal. See the function to know why
383 we emit it in an idle */
384 if (num_elements == 0) {
385 if (priv->queue_empty_handler) {
386 g_source_remove (priv->queue_empty_handler);
387 priv->queue_empty_handler = 0;
389 priv->queue_empty_handler = g_idle_add_full (G_PRIORITY_LOW,
396 modest_mail_operation_queue_num_elements (ModestMailOperationQueue *self)
398 ModestMailOperationQueuePrivate *priv;
401 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self), 0);
403 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
405 g_mutex_lock (priv->queue_lock);
406 length = g_queue_get_length (priv->op_queue);
407 g_mutex_unlock (priv->queue_lock);
413 modest_mail_operation_queue_cancel (ModestMailOperationQueue *self,
414 ModestMailOperation *mail_op)
416 ModestMailOperationQueuePrivate *priv;
418 g_return_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self));
419 g_return_if_fail (MODEST_IS_MAIL_OPERATION (mail_op));
421 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
423 MODEST_DEBUG_BLOCK (print_queue_item (mail_op, "cancel"););
425 /* This triggers a progess_changed signal in which we remove
426 * the operation from the queue. */
427 modest_mail_operation_cancel (mail_op);
431 on_cancel_all_foreach (gpointer op, gpointer list)
435 new_list = (GSList**) list;
436 *new_list = g_slist_prepend (*new_list, MODEST_MAIL_OPERATION (op));
440 modest_mail_operation_queue_cancel_all (ModestMailOperationQueue *self)
442 ModestMailOperationQueuePrivate *priv;
443 GSList* operations_to_cancel = NULL;
446 g_return_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self));
448 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
450 /* Remember which operations to cancel. This is the only thing that
451 * is done while holding the lock, so we do not need to call
452 * functions from other files while holding the lock, which could
453 * lead to a deadlock if such a call re-enters into this queue and
454 * tries to acquire another lock. */
455 g_mutex_lock (priv->queue_lock);
456 g_queue_foreach (priv->op_queue, (GFunc) on_cancel_all_foreach, &operations_to_cancel);
457 g_mutex_unlock (priv->queue_lock);
459 operations_to_cancel = g_slist_reverse (operations_to_cancel);
461 for(cur = operations_to_cancel; cur != NULL; cur = cur->next) {
462 if (!MODEST_IS_MAIL_OPERATION(cur->data))
463 g_printerr ("modest: cur->data is not a valid mail operation\n");
465 modest_mail_operation_cancel (MODEST_MAIL_OPERATION (cur->data));
468 g_slist_free(operations_to_cancel);
478 on_find_by_source_foreach (gpointer op, gpointer data)
480 FindBySourceInfo *info = (FindBySourceInfo*) data;
481 GObject *source = NULL;
483 source = modest_mail_operation_get_source (MODEST_MAIL_OPERATION (op));
484 if (info->source == source) {
485 g_object_ref (G_OBJECT (op));
486 *(info->new_list) = g_slist_prepend (*(info->new_list), MODEST_MAIL_OPERATION (op));
489 g_object_unref (source);
493 modest_mail_operation_queue_get_by_source (ModestMailOperationQueue *self,
496 ModestMailOperationQueuePrivate *priv;
497 GSList* found_operations= NULL;
498 FindBySourceInfo *info;
500 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self), NULL);
501 g_return_val_if_fail (source != NULL, NULL);
503 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
505 info = g_new0 (FindBySourceInfo, 1);
506 info->new_list = &found_operations;
507 info->source = source;
509 g_mutex_lock (priv->queue_lock);
510 g_queue_foreach (priv->op_queue, (GFunc) on_find_by_source_foreach, info);
511 g_mutex_unlock (priv->queue_lock);
515 return found_operations;
519 accumulate_mail_op_strings (ModestMailOperation *op, gchar **str)
521 gchar *mail_op_to_str = modest_mail_operation_to_string (op);
522 *str = g_strdup_printf ("%s\n%s", *str, mail_op_to_str);
523 g_free (mail_op_to_str);
528 modest_mail_operation_queue_to_string (ModestMailOperationQueue *self)
532 ModestMailOperationQueuePrivate *priv;
534 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self), NULL);
536 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
538 len = g_queue_get_length (priv->op_queue);
539 str = g_strdup_printf ("mail operation queue (%02d)\n-------------------------", len);
543 str = g_strdup_printf ("%s\n%s", copy, "<empty>");
546 g_mutex_lock (priv->queue_lock);
547 g_queue_foreach (priv->op_queue, (GFunc)accumulate_mail_op_strings, &str);
548 g_mutex_unlock (priv->queue_lock);
555 modest_mail_operation_queue_running_shutdown (ModestMailOperationQueue *self)
557 ModestMailOperationQueuePrivate *priv;
559 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self), FALSE);
561 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
562 return priv->running_final_sync;
566 modest_mail_operation_queue_set_running_shutdown (ModestMailOperationQueue *self)
568 ModestMailOperationQueuePrivate *priv;
570 g_return_if_fail (MODEST_IS_MAIL_OPERATION_QUEUE (self));
572 priv = MODEST_MAIL_OPERATION_QUEUE_GET_PRIVATE(self);
573 priv->running_final_sync = TRUE;