2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
14 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
17 #include "evolution-config.h"
26 #include <libedataserver/libedataserver.h>
30 /*#define MALLOC_CHECK*/
33 static guint mail_msg_seq
; /* sequence number of each message */
35 /* Table of active messages. Must hold mail_msg_lock to access. */
36 static GHashTable
*mail_msg_active_table
;
37 static GMutex mail_msg_lock
;
38 static GCond mail_msg_cond
;
40 static MailMsgCreateActivityFunc create_activity
= NULL
;
41 static MailMsgSubmitActivityFunc submit_activity
= NULL
;
42 static MailMsgFreeActivityFunc free_activity
= NULL
;
43 static MailMsgCompleteActivityFunc complete_activity
= NULL
;
44 static MailMsgAlertErrorFunc alert_error
= NULL
;
45 static MailMsgCancelActivityFunc cancel_activity
= NULL
;
46 static MailMsgGetAlertSinkFunc get_alert_sink
= NULL
;
49 mail_msg_register_activities (MailMsgCreateActivityFunc acreate
,
50 MailMsgSubmitActivityFunc asubmit
,
51 MailMsgFreeActivityFunc freeact
,
52 MailMsgCompleteActivityFunc comp_act
,
53 MailMsgCancelActivityFunc cancel_act
,
54 MailMsgAlertErrorFunc ealert
,
55 MailMsgGetAlertSinkFunc ealertsink
)
57 /* XXX This is an utter hack to keep EActivity out
58 * of EDS and still let Evolution do EActivity. */
59 create_activity
= acreate
;
60 submit_activity
= asubmit
;
61 free_activity
= freeact
;
62 complete_activity
= comp_act
;
63 cancel_activity
= cancel_act
;
65 get_alert_sink
= ealertsink
;
69 mail_msg_get_alert_sink ()
72 return get_alert_sink ();
78 mail_msg_cancelled (CamelOperation
*operation
,
81 mail_msg_cancel (GPOINTER_TO_UINT (user_data
));
85 mail_msg_submit (CamelOperation
*cancellable
)
89 submit_activity ((GCancellable
*) cancellable
);
94 mail_msg_new_with_cancellable (MailMsgInfo
*info
,
95 GCancellable
*cancellable
)
99 g_mutex_lock (&mail_msg_lock
);
101 msg
= g_slice_alloc0 (info
->size
);
104 msg
->seq
= mail_msg_seq
++;
107 msg
->cancellable
= g_object_ref (cancellable
);
109 msg
->cancellable
= camel_operation_new ();
112 create_activity (msg
->cancellable
);
115 msg
->cancellable
, "cancelled",
116 G_CALLBACK (mail_msg_cancelled
),
117 GINT_TO_POINTER (msg
->seq
));
119 g_hash_table_insert (
120 mail_msg_active_table
, GINT_TO_POINTER (msg
->seq
), msg
);
122 d (printf ("New message %p\n", msg
));
124 g_mutex_unlock (&mail_msg_lock
);
130 mail_msg_new (MailMsgInfo
*info
)
132 return mail_msg_new_with_cancellable (info
, NULL
);
139 checkmem (gpointer p
)
142 gint status
= mprobe (p
);
146 printf ("Memory underrun at %p\n", p
);
149 printf ("Memory overrun at %p\n", p
);
152 printf ("Double free %p\n", p
);
160 mail_msg_free (MailMsg
*mail_msg
)
162 /* This is an idle callback. */
165 free_activity (mail_msg
->cancellable
);
167 if (mail_msg
->cancellable
!= NULL
)
168 g_object_unref (mail_msg
->cancellable
);
170 if (mail_msg
->error
!= NULL
)
171 g_error_free (mail_msg
->error
);
173 g_slice_free1 (mail_msg
->info
->size
, mail_msg
);
179 mail_msg_ref (gpointer msg
)
181 MailMsg
*mail_msg
= msg
;
183 g_return_val_if_fail (mail_msg
!= NULL
, msg
);
184 g_return_val_if_fail (mail_msg
->ref_count
> 0, msg
);
186 g_atomic_int_inc (&mail_msg
->ref_count
);
192 mail_msg_unref (gpointer msg
)
194 MailMsg
*mail_msg
= msg
;
196 g_return_if_fail (mail_msg
!= NULL
);
197 g_return_if_fail (mail_msg
->ref_count
> 0);
199 if (g_atomic_int_dec_and_test (&mail_msg
->ref_count
)) {
203 checkmem (mail_msg
->cancel
);
204 checkmem (mail_msg
->priv
);
206 d (printf ("Free message %p\n", msg
));
208 if (mail_msg
->info
->free
)
209 mail_msg
->info
->free (mail_msg
);
211 g_mutex_lock (&mail_msg_lock
);
213 g_hash_table_remove (
214 mail_msg_active_table
,
215 GINT_TO_POINTER (mail_msg
->seq
));
216 g_cond_broadcast (&mail_msg_cond
);
218 g_mutex_unlock (&mail_msg_lock
);
220 /* Destroy the message from an idle callback
221 * so we know we're in the main loop thread.
222 * Prioritize ahead of GTK+ redraws. */
224 G_PRIORITY_HIGH_IDLE
,
225 (GSourceFunc
) mail_msg_free
, mail_msg
, NULL
);
230 mail_msg_check_error (gpointer msg
)
236 checkmem (m
->cancel
);
240 if (m
->error
== NULL
)
243 if (complete_activity
)
244 complete_activity (m
->cancellable
);
246 if (g_error_matches (m
->error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
248 cancel_activity (m
->cancellable
);
252 /* XXX Hmm, no explanation of why this is needed. It looks like
253 * a lame hack and will be removed at some point, if only to
254 * reintroduce whatever issue made this necessary so we can
255 * document it in the source code this time. */
256 if (g_error_matches (
257 m
->error
, CAMEL_FOLDER_ERROR
,
258 CAMEL_FOLDER_ERROR_INVALID_UID
))
261 /* FIXME: Submit an error on the dbus */
265 if (m
->info
->desc
&& (what
= m
->info
->desc (m
))) {
266 alert_error (m
->cancellable
, what
, m
->error
->message
);
269 alert_error (m
->cancellable
, NULL
, m
->error
->message
);
274 mail_msg_cancel (guint msgid
)
277 GCancellable
*cancellable
= NULL
;
279 g_mutex_lock (&mail_msg_lock
);
281 msg
= g_hash_table_lookup (
282 mail_msg_active_table
, GINT_TO_POINTER (msgid
));
284 /* Hold a reference to the GCancellable so it doesn't finalize
285 * itself on us between unlocking the mutex and cancelling. */
287 cancellable
= msg
->cancellable
;
288 if (g_cancellable_is_cancelled (cancellable
))
291 g_object_ref (cancellable
);
294 g_mutex_unlock (&mail_msg_lock
);
296 if (cancellable
!= NULL
) {
297 g_cancellable_cancel (cancellable
);
298 g_object_unref (cancellable
);
303 mail_msg_active (void)
307 g_mutex_lock (&mail_msg_lock
);
308 active
= g_hash_table_size (mail_msg_active_table
) > 0;
309 g_mutex_unlock (&mail_msg_lock
);
314 /* **************************************** */
316 static guint idle_source_id
= 0;
317 G_LOCK_DEFINE_STATIC (idle_source_id
);
318 static GAsyncQueue
*main_loop_queue
= NULL
;
319 static GAsyncQueue
*msg_reply_queue
= NULL
;
320 static GThread
*main_thread
= NULL
;
323 mail_msg_idle_cb (void)
327 g_return_val_if_fail (main_loop_queue
!= NULL
, FALSE
);
328 g_return_val_if_fail (msg_reply_queue
!= NULL
, FALSE
);
330 G_LOCK (idle_source_id
);
332 G_UNLOCK (idle_source_id
);
333 /* check the main loop queue */
334 while ((msg
= g_async_queue_try_pop (main_loop_queue
)) != NULL
) {
335 GCancellable
*cancellable
;
337 cancellable
= msg
->cancellable
;
341 (GSourceFunc
) mail_msg_submit
,
342 g_object_ref (msg
->cancellable
),
343 (GDestroyNotify
) g_object_unref
);
344 if (msg
->info
->exec
!= NULL
)
345 msg
->info
->exec (msg
, cancellable
, &msg
->error
);
346 if (msg
->info
->done
!= NULL
)
347 msg
->info
->done (msg
);
348 mail_msg_unref (msg
);
351 /* check the reply queue */
352 while ((msg
= g_async_queue_try_pop (msg_reply_queue
)) != NULL
) {
353 if (msg
->info
->done
!= NULL
)
354 msg
->info
->done (msg
);
355 mail_msg_check_error (msg
);
356 mail_msg_unref (msg
);
362 mail_msg_proxy (MailMsg
*msg
)
364 GCancellable
*cancellable
;
366 cancellable
= msg
->cancellable
;
368 if (msg
->info
->desc
!= NULL
) {
369 gchar
*text
= msg
->info
->desc (msg
);
370 camel_operation_push_message (cancellable
, "%s", text
);
376 (GSourceFunc
) mail_msg_submit
,
377 g_object_ref (msg
->cancellable
),
378 (GDestroyNotify
) g_object_unref
);
380 if (msg
->info
->exec
!= NULL
)
381 msg
->info
->exec (msg
, cancellable
, &msg
->error
);
383 if (msg
->info
->desc
!= NULL
)
384 camel_operation_pop_message (cancellable
);
386 g_async_queue_push (msg_reply_queue
, msg
);
388 G_LOCK (idle_source_id
);
389 if (idle_source_id
== 0)
390 /* Prioritize ahead of GTK+ redraws. */
391 idle_source_id
= g_idle_add_full (
392 G_PRIORITY_HIGH_IDLE
,
393 (GSourceFunc
) mail_msg_idle_cb
, NULL
, NULL
);
394 G_UNLOCK (idle_source_id
);
400 g_mutex_init (&mail_msg_lock
);
401 g_cond_init (&mail_msg_cond
);
403 main_loop_queue
= g_async_queue_new ();
404 msg_reply_queue
= g_async_queue_new ();
406 mail_msg_active_table
= g_hash_table_new (NULL
, NULL
);
407 main_thread
= g_thread_self ();
411 mail_msg_compare (const MailMsg
*msg1
,
414 gint priority1
= msg1
->priority
;
415 gint priority2
= msg2
->priority
;
417 if (priority1
== priority2
)
420 return (priority1
< priority2
) ? 1 : -1;
424 create_thread_pool (gpointer data
)
426 GThreadPool
*thread_pool
;
427 gint max_threads
= GPOINTER_TO_INT (data
);
429 /* once created, run forever */
430 thread_pool
= g_thread_pool_new (
431 (GFunc
) mail_msg_proxy
, NULL
, max_threads
, FALSE
, NULL
);
432 g_thread_pool_set_sort_function (
433 thread_pool
, (GCompareDataFunc
) mail_msg_compare
, NULL
);
439 mail_msg_main_loop_push (gpointer msg
)
441 g_async_queue_push_sorted (
442 main_loop_queue
, msg
,
443 (GCompareDataFunc
) mail_msg_compare
, NULL
);
445 G_LOCK (idle_source_id
);
446 if (idle_source_id
== 0)
447 /* Prioritize ahead of GTK+ redraws. */
448 idle_source_id
= g_idle_add_full (
449 G_PRIORITY_HIGH_IDLE
,
450 (GSourceFunc
) mail_msg_idle_cb
, NULL
, NULL
);
451 G_UNLOCK (idle_source_id
);
455 mail_msg_unordered_push (gpointer msg
)
457 static GOnce once
= G_ONCE_INIT
;
459 g_once (&once
, (GThreadFunc
) create_thread_pool
, GINT_TO_POINTER (10));
461 g_thread_pool_push ((GThreadPool
*) once
.retval
, msg
, NULL
);
465 mail_msg_fast_ordered_push (gpointer msg
)
467 static GOnce once
= G_ONCE_INIT
;
469 g_once (&once
, (GThreadFunc
) create_thread_pool
, GINT_TO_POINTER (1));
471 g_thread_pool_push ((GThreadPool
*) once
.retval
, msg
, NULL
);
475 mail_msg_slow_ordered_push (gpointer msg
)
477 static GOnce once
= G_ONCE_INIT
;
479 g_once (&once
, (GThreadFunc
) create_thread_pool
, GINT_TO_POINTER (1));
481 g_thread_pool_push ((GThreadPool
*) once
.retval
, msg
, NULL
);
485 mail_in_main_thread (void)
487 return (g_thread_self () == main_thread
);
490 /* ********************************************************************** */
503 do_call (struct _call_msg
*m
,
504 GCancellable
*cancellable
,
507 gpointer p1
, *p2
, *p3
, *p4
, *p5
;
511 G_VA_COPY (ap
, m
->ap
);
515 p1
= va_arg (ap
, gpointer
);
516 m
->ret
= m
->func (p1
);
519 p1
= va_arg (ap
, gpointer
);
520 p2
= va_arg (ap
, gpointer
);
521 m
->ret
= m
->func (p1
, p2
);
523 case MAIL_CALL_p_ppp
:
524 p1
= va_arg (ap
, gpointer
);
525 p2
= va_arg (ap
, gpointer
);
526 p3
= va_arg (ap
, gpointer
);
527 m
->ret
= m
->func (p1
, p2
, p3
);
529 case MAIL_CALL_p_pppp
:
530 p1
= va_arg (ap
, gpointer
);
531 p2
= va_arg (ap
, gpointer
);
532 p3
= va_arg (ap
, gpointer
);
533 p4
= va_arg (ap
, gpointer
);
534 m
->ret
= m
->func (p1
, p2
, p3
, p4
);
536 case MAIL_CALL_p_ppppp
:
537 p1
= va_arg (ap
, gpointer
);
538 p2
= va_arg (ap
, gpointer
);
539 p3
= va_arg (ap
, gpointer
);
540 p4
= va_arg (ap
, gpointer
);
541 p5
= va_arg (ap
, gpointer
);
542 m
->ret
= m
->func (p1
, p2
, p3
, p4
, p5
);
544 case MAIL_CALL_p_ppippp
:
545 p1
= va_arg (ap
, gpointer
);
546 p2
= va_arg (ap
, gpointer
);
547 i1
= va_arg (ap
, gint
);
548 p3
= va_arg (ap
, gpointer
);
549 p4
= va_arg (ap
, gpointer
);
550 p5
= va_arg (ap
, gpointer
);
551 m
->ret
= m
->func (p1
, p2
, i1
, p3
, p4
, p5
);
557 if (g_cancellable_is_cancelled (cancellable
)) {
559 cancel_activity (cancellable
);
561 if (complete_activity
)
562 complete_activity (cancellable
);
566 e_flag_set (m
->done
);
570 do_free (struct _call_msg
*msg
)
575 static MailMsgInfo mail_call_info
= {
576 sizeof (struct _call_msg
),
577 (MailMsgDescFunc
) NULL
,
578 (MailMsgExecFunc
) do_call
,
579 (MailMsgDoneFunc
) NULL
,
580 (MailMsgFreeFunc
) do_free
584 mail_call_main (mail_call_t type
,
588 GCancellable
*cancellable
;
595 m
= mail_msg_new (&mail_call_info
);
598 G_VA_COPY (m
->ap
, ap
);
600 cancellable
= m
->base
.cancellable
;
602 if (mail_in_main_thread ())
603 do_call (m
, cancellable
, &m
->base
.error
);
606 m
->done
= e_flag_new ();
607 mail_msg_main_loop_push (m
);
608 e_flag_wait (m
->done
);
609 e_flag_free (m
->done
);
617 /* the m->ap is freed on the message end, at do_free() above */
618 /* coverity[missing_va_end] */