aboutsummaryrefslogtreecommitdiffstats
path: root/mail/mail-threads.c
diff options
context:
space:
mode:
Diffstat (limited to 'mail/mail-threads.c')
-rw-r--r--mail/mail-threads.c1016
1 files changed, 683 insertions, 333 deletions
diff --git a/mail/mail-threads.c b/mail/mail-threads.c
index a5dbac2427..7f5e796a51 100644
--- a/mail/mail-threads.c
+++ b/mail/mail-threads.c
@@ -24,8 +24,6 @@
#include <config.h>
-#ifdef USE_BROKEN_THREADS
-
#include <string.h>
#include <glib.h>
#include "mail.h"
@@ -35,7 +33,7 @@
/* FIXME TODO: Do we need operations that don't get a progress window because
* they're quick, but we still want camel to be locked? We need some kind
- * of flag to mail_operation_try, but then we also need some kind of monitor
+ * of flag to mail_operation_queue, but then we also need some kind of monitor
* to open the window if it takes more than a second or something. That would
* probably entail another thread....
*/
@@ -44,37 +42,56 @@
* A function and its userdata
**/
-typedef struct closure_s {
- void (*callback)( gpointer );
- void (*cleanup)( gpointer );
- gpointer data;
-
- gchar *prettyname;
- /* gboolean gets_window; */
-} closure_t;
+typedef struct closure_s
+{
+ gpointer in_data;
+ gboolean free_in_data;
+ gpointer op_data;
+ const mail_operation_spec *spec;
+ CamelException *ex;
+ gchar *infinitive;
+ gchar *gerund;
+}
+closure_t;
/**
* A command issued through the compipe
**/
-typedef struct com_msg_s {
- enum com_msg_type_e { STARTING, PERCENTAGE, HIDE_PBAR, SHOW_PBAR, MESSAGE, PASSWORD, ERROR, FINISHED } type;
+typedef struct com_msg_s
+{
+ enum com_msg_type_e {
+ STARTING,
+ PERCENTAGE,
+ HIDE_PBAR,
+ SHOW_PBAR,
+ MESSAGE,
+ PASSWORD,
+ ERROR,
+ FINISHED
+ } type;
gfloat percentage;
gchar *message;
- void (*func)( gpointer );
- gpointer userdata;
+ closure_t *clur;
/* Password stuff */
gchar **reply;
gboolean secret;
gboolean *success;
-} com_msg_t;
+}
+com_msg_t;
+
+/**
+ * @dispatch_thread_started: gboolean that tells us whether
+ * the dispatch thread has been launched.
+ **/
+
+static gboolean dispatch_thread_started = FALSE;
/**
- * @mail_operation_in_progress: When true, there's
- * another thread executing a major ev-mail operation:
- * fetch_mail, etc.
+ * @queue_len : the number of operations pending
+ * and being executed.
*
* Because camel is not thread-safe we work
* with the restriction that more than one mailbox
@@ -82,7 +99,7 @@ typedef struct com_msg_s {
* concurrently check mail and move messages, etc.
**/
-static gboolean mail_operation_in_progress;
+static gint queue_len = 0;
/**
* @queue_window: The little window on the screen that
@@ -110,27 +127,22 @@ static GtkWidget *queue_window_progress = NULL;
static int progress_timeout_handle = -1;
/**
- * @op_queue: The list of operations the are scheduled
- * to proceed after the currently executing one. When
- * only one operation is going, this is NULL.
- **/
-
-static GSList *op_queue = NULL;
-
-/**
- * @compipe: The pipe through which the dispatcher communicates
+ * @main_compipe: The pipe through which the dispatcher communicates
* with the main thread for GTK+ calls
*
* @chan_reader: the GIOChannel that reads our pipe
*
- * @READER: the fd in our pipe that.... reads!
- * @WRITER: the fd in our pipe that.... writes!
+ * @MAIN_READER: the fd in our main pipe that.... reads!
+ * @MAIN_WRITER: the fd in our main pipe that.... writes!
*/
-#define READER compipe[0]
-#define WRITER compipe[1]
+#define MAIN_READER main_compipe[0]
+#define MAIN_WRITER main_compipe[1]
+#define DISPATCH_READER dispatch_compipe[0]
+#define DISPATCH_WRITER dispatch_compipe[1]
-static int compipe[2] = { -1, -1 };
+static int main_compipe[2] = { -1, -1 };
+static int dispatch_compipe[2] = { -1, -1 };
GIOChannel *chan_reader = NULL;
@@ -146,28 +158,50 @@ GIOChannel *chan_reader = NULL;
* the dispatch thread may proceed its operations.
*/
-G_LOCK_DEFINE_STATIC( modal_lock );
+G_LOCK_DEFINE_STATIC (modal_lock);
static GCond *modal_cond = NULL;
static gboolean modal_may_proceed = FALSE;
/**
+ * @ready_for_op: A lock that the main thread only releases
+ * when it is ready for the dispatch thread to do its thing
+ *
+ * @ready_cond: A condition for this ... condition
+ *
+ * @ready_may_proceed: a gboolean telling the dispatch thread
+ * when it may proceed.
+ **/
+
+G_LOCK_DEFINE_STATIC (ready_for_op);
+static GCond *ready_cond = NULL;
+static gboolean ready_may_proceed = FALSE;
+
+/**
* Static prototypes
**/
-static void create_queue_window( void );
-static void dispatch( closure_t *clur );
-static void *dispatch_func( void *data );
-static void check_compipe( void );
-static void check_cond( void );
-static gboolean read_msg( GIOChannel *source, GIOCondition condition, gpointer userdata );
-static void remove_next_pending( void );
-static void show_error( com_msg_t *msg );
-static void show_error_clicked( void );
-static void get_password( com_msg_t *msg );
-static void get_password_cb( gchar *string, gpointer data );
-static void get_password_clicked( GnomeDialog *dialog, gint button, gpointer user_data );
-static gboolean progress_timeout( gpointer data );
-static void timeout_toggle( gboolean active );
+static void create_queue_window (void);
+static void destroy_queue_window (void);
+static void *dispatch (void * data);
+static void check_dispatcher (void);
+static void check_compipes (void);
+static void check_cond (void);
+static gboolean read_msg (GIOChannel * source, GIOCondition condition,
+ gpointer userdata);
+static void remove_next_pending (void);
+static void show_error (com_msg_t * msg);
+static void show_error_clicked (GtkObject * obj);
+static void get_password (com_msg_t * msg);
+static void get_password_cb (gchar * string, gpointer data);
+static void get_password_clicked (GnomeDialog * dialog, gint button,
+
+ gpointer user_data);
+static gboolean progress_timeout (gpointer data);
+static void timeout_toggle (gboolean active);
+static gboolean display_timeout (gpointer data);
+static closure_t *new_closure (const mail_operation_spec * spec, gpointer input,
+ gboolean free_in_data);
+static void free_closure (closure_t *clur);
/* Pthread code */
/* FIXME: support other thread types!!!! */
@@ -190,17 +224,25 @@ static pthread_t dispatch_thread;
* enough.
*/
-#else /* defined USE_PTHREADS */
-choke on this: no thread type defined
+#elif defined( G_THREADS_IMPL_SOLARIS )
+
+#include <thread.h>
+
+static thread_t dispatch_thread;
+
+#else /* no supported thread impl */
+void
+f (void)
+{
+ Error_No_supported_thread_implementation_recognized ();
+ choke on this;
+}
#endif
/**
- * mail_operation_try:
- * @description: A user-friendly string describing the operation.
- * @callback: the function to call in another thread to start the operation
- * @cleanup: the function to call in the main thread when the callback is finished.
- * NULL is allowed.
- * @user_data: extra data passed to the callback
+ * mail_operation_queue:
+ * @spec: describes the operation to be performed
+ * @input: input data for the operation.
*
* Runs a mail operation asynchronously. If no other operation is running,
* we start another thread and call the callback in that thread. The function
@@ -215,66 +257,75 @@ choke on this: no thread type defined
**/
gboolean
-mail_operation_try( const gchar *description, void (*callback)( gpointer ),
- void (*cleanup)( gpointer ), gpointer user_data )
+mail_operation_queue (const mail_operation_spec * spec, gpointer input,
+ gboolean free_in_data)
{
closure_t *clur;
- g_assert( callback );
-
- clur = g_new( closure_t, 1 );
- clur->callback = callback;
- clur->cleanup = cleanup;
- clur->data = user_data;
- clur->prettyname = g_strdup( description );
-
- if( mail_operation_in_progress == FALSE ) {
- /* No operations are going on, none are pending. So
- * we check to see if we're initialized (create the
- * window and the pipes), and send off the operation
- * on its merry way.
- */
- mail_operation_in_progress = TRUE;
+ g_assert (spec);
+
+ clur = new_closure (spec, input, free_in_data);
+
+ if (spec->setup)
+ (spec->setup) (clur->in_data, clur->op_data, clur->ex);
+
+ if (camel_exception_is_set (clur->ex)) {
+ if (clur->ex->id != CAMEL_EXCEPTION_USER_CANCEL) {
+ GtkWidget *err_dialog;
+ gchar *msg;
+
+ msg =
+ g_strdup_printf
+ ("Error while preparing to %s:\n" "%s",
+ clur->infinitive,
+ camel_exception_get_description (clur->ex));
+ err_dialog = gnome_error_dialog (msg);
+ g_free (msg);
+ gnome_dialog_set_close (GNOME_DIALOG (err_dialog),
+ TRUE);
+ /*gnome_dialog_run_and_close (GNOME_DIALOG (err_dialog)); */
+ /*gtk_widget_destroy (err_dialog); */
+ gtk_widget_show (GTK_WIDGET (err_dialog));
+
+ g_warning ("Setup failed for `%s': %s",
+ clur->infinitive,
+ camel_exception_get_description (clur->
+ ex));
+ }
- check_compipe();
- create_queue_window();
- gtk_widget_show_all( queue_window );
- gnome_win_hints_set_layer( queue_window,
- WIN_LAYER_ONTOP );
- gnome_win_hints_set_state( queue_window,
- WIN_STATE_ARRANGE_IGNORE );
- gnome_win_hints_set_hints( queue_window,
- WIN_HINTS_SKIP_FOCUS |
- WIN_HINTS_SKIP_WINLIST |
- WIN_HINTS_SKIP_TASKBAR );
- gtk_widget_hide( queue_window_pending );
+ free_closure (clur);
+ return FALSE;
+ }
- dispatch( clur );
+ if (queue_len == 0) {
+ check_cond ();
+ check_compipes ();
+ check_dispatcher ();
+ create_queue_window ();
+ /*gtk_widget_show_all (queue_window); */
+ gtk_timeout_add (1000, display_timeout, NULL);
} else {
GtkWidget *label;
- /* Zut. We already have an operation running. Well,
- * queue ourselves up.
- *
- * Yes, g_slist_prepend is faster down here.. But we pop
- * operations off the beginning of the list later and
- * that's a lot faster.
+ /* We already have an operation running. Well,
+ * queue ourselves up. (visually)
*/
- op_queue = g_slist_append( op_queue, clur );
-
/* Show us in the pending window. */
- label = gtk_label_new( description );
- gtk_misc_set_alignment( GTK_MISC( label ), 1.0, 0.5 );
- gtk_box_pack_start( GTK_BOX( queue_window_pending ), label,
- FALSE, TRUE, 2 );
+ label = gtk_label_new (clur->infinitive);
+ gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (queue_window_pending), label,
+ FALSE, TRUE, 2);
+ gtk_widget_show (label);
/* If we want the next op to be on the bottom, uncomment this */
/* 1 = first on list always (0-based) */
/* gtk_box_reorder_child( GTK_BOX( queue_window_pending ), label, 1 ); */
- gtk_widget_show_all( queue_window_pending );
+ gtk_widget_show (queue_window_pending);
}
+ write (DISPATCH_WRITER, clur, sizeof (closure_t));
+ queue_len++;
return TRUE;
}
@@ -286,13 +337,14 @@ mail_operation_try( const gchar *description, void (*callback)( gpointer ),
* Threadsafe for, nay, intended to be called by, the dispatching thread.
**/
-void mail_op_set_percentage( gfloat percentage )
+void
+mail_op_set_percentage (gfloat percentage)
{
com_msg_t msg;
msg.type = PERCENTAGE;
msg.percentage = percentage;
- write( WRITER, &msg, sizeof( msg ) );
+ write (MAIN_WRITER, &msg, sizeof (msg));
}
/**
@@ -307,12 +359,13 @@ void mail_op_set_percentage( gfloat percentage )
* that, right?
*/
-void mail_op_hide_progressbar( void )
+void
+mail_op_hide_progressbar (void)
{
com_msg_t msg;
msg.type = HIDE_PBAR;
- write( WRITER, &msg, sizeof( msg ) );
+ write (MAIN_WRITER, &msg, sizeof (msg));
}
/**
@@ -322,12 +375,13 @@ void mail_op_hide_progressbar( void )
* Threadsafe for, nay, intended to be called by, the dispatching thread.
**/
-void mail_op_show_progressbar( void )
+void
+mail_op_show_progressbar (void)
{
com_msg_t msg;
msg.type = SHOW_PBAR;
- write( WRITER, &msg, sizeof( msg ) );
+ write (MAIN_WRITER, &msg, sizeof (msg));
}
/**
@@ -340,17 +394,18 @@ void mail_op_show_progressbar( void )
* Threadsafe for, nay, intended to be called by, the dispatching thread.
**/
-void mail_op_set_message( gchar *fmt, ... )
+void
+mail_op_set_message (gchar * fmt, ...)
{
com_msg_t msg;
va_list val;
- va_start( val, fmt );
+ va_start (val, fmt);
msg.type = MESSAGE;
- msg.message = g_strdup_vprintf( fmt, val );
- va_end( val );
+ msg.message = g_strdup_vprintf (fmt, val);
+ va_end (val);
- write( WRITER, &msg, sizeof( msg ) );
+ write (MAIN_WRITER, &msg, sizeof (msg));
}
/**
@@ -365,30 +420,31 @@ void mail_op_set_message( gchar *fmt, ... )
* message.
**/
-gboolean mail_op_get_password( gchar *prompt, gboolean secret, gchar **dest )
+gboolean
+mail_op_get_password (gchar * prompt, gboolean secret, gchar ** dest)
{
com_msg_t msg;
gboolean result;
- check_cond();
-
msg.type = PASSWORD;
msg.secret = secret;
msg.message = prompt;
msg.reply = dest;
msg.success = &result;
-
+
(*dest) = NULL;
- G_LOCK( modal_lock );
+ G_LOCK (modal_lock);
- write( WRITER, &msg, sizeof( msg ) );
+ write (MAIN_WRITER, &msg, sizeof (msg));
modal_may_proceed = FALSE;
- while( modal_may_proceed == FALSE )
- g_cond_wait( modal_cond, g_static_mutex_get_mutex( &G_LOCK_NAME( modal_lock ) ) );
+ while (modal_may_proceed == FALSE)
+ g_cond_wait (modal_cond,
+ g_static_mutex_get_mutex (&G_LOCK_NAME
+ (modal_lock)));
- G_UNLOCK( modal_lock );
+ G_UNLOCK (modal_lock);
return result;
}
@@ -402,27 +458,28 @@ gboolean mail_op_get_password( gchar *prompt, gboolean secret, gchar **dest )
* Threadsafe for, nay, intended to be called by, the dispatching thread.
**/
-void mail_op_error( gchar *fmt, ... )
+void
+mail_op_error (gchar * fmt, ...)
{
com_msg_t msg;
va_list val;
- check_cond();
-
- va_start( val, fmt );
+ va_start (val, fmt);
msg.type = ERROR;
- msg.message = g_strdup_vprintf( fmt, val );
- va_end( val );
+ msg.message = g_strdup_vprintf (fmt, val);
+ va_end (val);
- G_LOCK( modal_lock );
+ G_LOCK (modal_lock);
modal_may_proceed = FALSE;
- write( WRITER, &msg, sizeof( msg ) );
+ write (MAIN_WRITER, &msg, sizeof (msg));
- while( modal_may_proceed == FALSE )
- g_cond_wait( modal_cond, g_static_mutex_get_mutex( &G_LOCK_NAME( modal_lock ) ) );
+ while (modal_may_proceed == FALSE)
+ g_cond_wait (modal_cond,
+ g_static_mutex_get_mutex (&G_LOCK_NAME
+ (modal_lock)));
- G_UNLOCK( modal_lock );
+ G_UNLOCK (modal_lock);
}
/**
@@ -432,12 +489,13 @@ void mail_op_error( gchar *fmt, ... )
* to finish executing
*/
-void mail_operation_wait_for_finish( void )
+void
+mail_operation_wait_for_finish (void)
{
- while( mail_operation_in_progress ) {
- while( gtk_events_pending() )
- gtk_main_iteration();
- }
+ while (queue_len)
+ gtk_main_iteration ();
+ /* Sigh. Otherwise we deadlock upon exit. */
+ GDK_THREADS_LEAVE ();
}
/**
@@ -445,15 +503,82 @@ void mail_operation_wait_for_finish( void )
*
* Returns TRUE if operations are being executed asynchronously
* when called, FALSE if not.
- */
+ **/
+
+gboolean
+mail_operations_are_executing (void)
+{
+ return (queue_len > 0);
+}
+
+/**
+ * mail_operations_terminate:
+ *
+ * Let the operations finish then terminate the dispatch thread
+ **/
-gboolean mail_operations_are_executing( void )
+void
+mail_operations_terminate (void)
{
- return mail_operation_in_progress;
+ closure_t clur;
+
+ mail_operation_wait_for_finish();
+
+ memset (&clur, 0, sizeof (closure_t));
+ clur.spec = NULL;
+
+ write (DISPATCH_WRITER, &clur, sizeof (closure_t));
}
/* ** Static functions **************************************************** */
+static void check_dispatcher (void)
+{
+ int res;
+
+ if (dispatch_thread_started)
+ return;
+
+#if defined( G_THREADS_IMPL_POSIX )
+ res = pthread_create (&dispatch_thread, NULL,
+ (void *) &dispatch, NULL);
+#elif defined( G_THREADS_IMPL_SOLARIS )
+ res = thr_create (NULL, 0, (void *) &dispatch, NULL, 0, &dispatch_thread);
+#else /* no known impl */
+ Error_No_thread_create_implementation ();
+ choke on this;
+#endif
+ if (res != 0) {
+ g_warning ("Error launching dispatch thread!");
+ /* FIXME: more error handling */
+ } else
+ dispatch_thread_started = TRUE;
+}
+
+static void
+print_hide (GtkWidget * wid)
+{
+ g_message ("$$$ hide signal emitted");
+}
+
+static void
+print_unmap (GtkWidget * wid)
+{
+ g_message ("$$$ unmap signal emitted");
+}
+
+static void
+print_map (GtkWidget * wid)
+{
+ g_message ("$$$ map signal emitted");
+}
+
+static void
+print_show (GtkWidget * wid)
+{
+ g_message ("$$$ show signal emitted");
+}
+
/**
* create_queue_window:
*
@@ -462,72 +587,115 @@ gboolean mail_operations_are_executing( void )
*/
static void
-create_queue_window( void )
+queue_window_delete_event_cb (GtkWindow *window,
+ void *data)
+{
+ /* Do nothing. Just prevent GTK+ from destroying the window. */
+}
+
+static void
+create_queue_window (void)
{
GtkWidget *vbox;
GtkWidget *pending_vb, *pending_lb;
GtkWidget *progress_lb, *progress_bar;
/* Check to see if we've only hidden it */
- if( queue_window != NULL )
+ if (queue_window != NULL)
return;
- queue_window = gtk_window_new( GTK_WINDOW_DIALOG );
- gtk_container_set_border_width( GTK_CONTAINER( queue_window ), 8 );
+ queue_window = gtk_window_new (GTK_WINDOW_DIALOG);
+ gtk_container_set_border_width (GTK_CONTAINER (queue_window), 8);
- vbox = gtk_vbox_new( FALSE, 4 );
+ gtk_signal_connect (GTK_OBJECT (queue_window), "delete_event",
+ GTK_SIGNAL_FUNC (queue_window_delete_event_cb), NULL);
- pending_vb = gtk_vbox_new( FALSE, 2 );
+ vbox = gtk_vbox_new (FALSE, 4);
+
+ pending_vb = gtk_vbox_new (FALSE, 2);
queue_window_pending = pending_vb;
- pending_lb = gtk_label_new( _("Currently pending operations:") );
- gtk_misc_set_alignment( GTK_MISC( pending_lb ), 0.0, 0.0 );
- gtk_box_pack_start( GTK_BOX( pending_vb ), pending_lb,
- FALSE, TRUE, 0 );
+ pending_lb = gtk_label_new (_("Currently pending operations:"));
+ gtk_misc_set_alignment (GTK_MISC (pending_lb), 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (pending_vb), pending_lb, FALSE, TRUE, 0);
- gtk_box_pack_start( GTK_BOX( vbox ), pending_vb,
- TRUE, TRUE, 4 );
+ gtk_box_pack_start (GTK_BOX (vbox), pending_vb, TRUE, TRUE, 4);
/* FIXME: 'operation' is not the warmest cuddliest word. */
- progress_lb = gtk_label_new( "" );
+ progress_lb = gtk_label_new ("");
queue_window_message = progress_lb;
- gtk_box_pack_start( GTK_BOX( vbox ), progress_lb,
- FALSE, TRUE, 4 );
+ gtk_box_pack_start (GTK_BOX (vbox), progress_lb, FALSE, TRUE, 4);
- progress_bar = gtk_progress_bar_new();
+ progress_bar = gtk_progress_bar_new ();
queue_window_progress = progress_bar;
/* FIXME: is this fit for l10n? */
- gtk_progress_bar_set_orientation( GTK_PROGRESS_BAR( progress_bar ),
- GTK_PROGRESS_LEFT_TO_RIGHT );
- gtk_progress_bar_set_bar_style( GTK_PROGRESS_BAR( progress_bar ),
- GTK_PROGRESS_CONTINUOUS );
- gtk_box_pack_start( GTK_BOX( vbox ), progress_bar,
- FALSE, TRUE, 4 );
+ gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR (progress_bar),
+ GTK_PROGRESS_LEFT_TO_RIGHT);
+ gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (progress_bar),
+ GTK_PROGRESS_CONTINUOUS);
+ gtk_box_pack_start (GTK_BOX (vbox), progress_bar, FALSE, TRUE, 4);
+
+ gtk_container_add (GTK_CONTAINER (queue_window), vbox);
+
+ gtk_widget_show (GTK_WIDGET (progress_bar));
+ gtk_widget_show (GTK_WIDGET (progress_lb));
+ gtk_widget_show (GTK_WIDGET (pending_lb));
+ gtk_widget_show (GTK_WIDGET (pending_vb));
+ gtk_widget_show (GTK_WIDGET (vbox));
+
+ gtk_signal_connect (GTK_OBJECT (queue_window), "hide", print_hide,
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (queue_window), "unmap", print_unmap,
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (queue_window), "show", print_show,
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (queue_window), "map", print_map,
+ NULL);
+}
- gtk_container_add( GTK_CONTAINER( queue_window ), vbox );
+static void destroy_queue_window (void)
+{
+ g_return_if_fail (queue_window);
+
+ timeout_toggle (FALSE);
+ gtk_widget_destroy (queue_window);
+
+ queue_window = NULL;
+ queue_window_progress = NULL;
+ queue_window_pending = NULL;
+ queue_window_message = NULL;
}
/**
- * check_compipe:
+ * check_compipes:
*
* Check and see if our pipe has been opened and open
* it if necessary.
**/
-static void check_compipe( void )
+static void
+check_compipes (void)
{
- if( READER > 0 )
- return;
+ if (MAIN_READER < 0) {
+ if (pipe (main_compipe) < 0) {
+ g_warning ("Call to pipe(2) failed!");
- if( pipe( compipe ) < 0 ) {
- g_warning( "Call to pipe(2) failed!" );
+ /* FIXME: better error handling. How do we react? */
+ return;
+ }
- /* FIXME: better error handling. How do we react? */
- return;
+ chan_reader = g_io_channel_unix_new (MAIN_READER);
+ g_io_add_watch (chan_reader, G_IO_IN, read_msg, NULL);
}
- chan_reader = g_io_channel_unix_new( READER );
- g_io_add_watch( chan_reader, G_IO_IN, read_msg, NULL );
+ if (DISPATCH_READER < 0) {
+ if (pipe (dispatch_compipe) < 0) {
+ g_warning ("Call to pipe(2) failed!");
+
+ /* FIXME: better error handling. How do we react? */
+ return;
+ }
+ }
}
/**
@@ -536,62 +704,94 @@ static void check_compipe( void )
* See if our condition is initialized and do so if necessary
**/
-static void check_cond( void )
+static void
+check_cond (void)
{
- if( modal_cond == NULL )
- modal_cond = g_cond_new();
+ if (modal_cond == NULL)
+ modal_cond = g_cond_new ();
+
+ if (ready_cond == NULL)
+ ready_cond = g_cond_new ();
}
/**
* dispatch:
- * @clur: The function to execute and its userdata
+ * @clur: The operation to execute and its parameters
*
* Start a thread that executes the closure and exit
* it when done.
*/
-static void dispatch( closure_t *clur )
+static void *
+dispatch (void *unused)
{
- int res;
+ size_t len;
+ closure_t *clur;
+ com_msg_t msg;
- res = pthread_create( &dispatch_thread, NULL, (void *) &dispatch_func, clur );
+ /* Let the compipes be created */
+ sleep (1);
- if( res != 0 ) {
- g_warning( "Error launching dispatch thread!" );
- /* FIXME: more error handling */
- }
-}
+ while (1) {
+ clur = g_new (closure_t, 1);
+ len = read (DISPATCH_READER, clur, sizeof (closure_t));
-/**
- * dispatch_func:
- * @data: the closure to run
- *
- * Runs the closure and exits the thread.
- */
+ if (len <= 0)
+ break;
-static void *dispatch_func( void *data )
-{
- com_msg_t msg;
- closure_t *clur = (closure_t *) data;
+ if (len != sizeof (closure_t)) {
+ g_warning ("dispatcher: Didn't read full message!");
+ continue;
+ }
+
+ if (clur->spec == NULL)
+ break;
- msg.type = STARTING;
- msg.message = clur->prettyname;
- write( WRITER, &msg, sizeof( msg ) );
+ msg.type = STARTING;
+ msg.message = g_strdup (clur->gerund);
+ write (MAIN_WRITER, &msg, sizeof (msg));
- /*GDK_THREADS_ENTER ();*/
- (clur->callback)( clur->data );
- /*GDK_THREADS_LEAVE ();*/
+ mail_op_hide_progressbar ();
+
+ (clur->spec->callback) (clur->in_data, clur->op_data, clur->ex);
+
+ if (camel_exception_is_set (clur->ex)) {
+ if (clur->ex->id != CAMEL_EXCEPTION_USER_CANCEL) {
+ g_warning ("Callback failed for `%s': %s",
+ clur->infinitive,
+ camel_exception_get_description (clur->
+ ex));
+ mail_op_error ("Error while `%s':\n" "%s",
+ clur->gerund,
+ camel_exception_get_description (clur->
+ ex));
+ }
+ }
- msg.type = FINISHED;
- msg.func = clur->cleanup; /* NULL is ok */
- msg.userdata = clur->data;
- write( WRITER, &msg, sizeof( msg ) );
+ msg.type = FINISHED;
+ msg.clur = clur;
- g_free( clur->prettyname );
- g_free( data );
+ G_LOCK (ready_for_op);
+ write (MAIN_WRITER, &msg, sizeof (msg));
- pthread_exit( 0 );
- return NULL; /*NOTREACHED*/
+ ready_may_proceed = FALSE;
+ while (ready_may_proceed == FALSE)
+ g_cond_wait (ready_cond,
+ g_static_mutex_get_mutex (&G_LOCK_NAME
+ (ready_for_op)));
+ G_UNLOCK (ready_for_op);
+ }
+
+#ifdef G_THREADS_IMPL_POSIX
+ pthread_exit (0);
+#elif defined( G_THREADS_IMPL_SOLARIS )
+ thr_exit (NULL);
+#else /* no known impl */
+ Error_No_thread_exit_implemented ();
+ choke on this;
+#endif
+ return NULL;
+ /*NOTREACHED*/
}
/**
@@ -604,75 +804,82 @@ static void *dispatch_func( void *data )
* action.
**/
-static gboolean read_msg( GIOChannel *source, GIOCondition condition, gpointer userdata )
+static gboolean
+read_msg (GIOChannel * source, GIOCondition condition, gpointer userdata)
{
com_msg_t *msg;
- closure_t *clur;
- GSList *temp;
guint size;
- msg = g_new0( com_msg_t, 1 );
+ msg = g_new0 (com_msg_t, 1);
- g_io_channel_read( source, (gchar *) msg,
- sizeof( com_msg_t ) / sizeof( gchar ),
- &size );
+ g_io_channel_read (source, (gchar *) msg,
+ sizeof (com_msg_t) / sizeof (gchar), &size);
- if( size != sizeof( com_msg_t ) ) {
- g_warning( _("Incomplete message written on pipe!") );
+ if (size != sizeof (com_msg_t)) {
+ g_warning (_("Incomplete message written on pipe!"));
msg->type = ERROR;
- msg->message = g_strdup( _("Error reading commands from dispatching thread.") );
+ msg->message =
+ g_strdup (_
+ ("Error reading commands from dispatching thread."));
}
/* This is very important, though I'm not quite sure why
* it is as we are in the main thread right now.
*/
- GDK_THREADS_ENTER();
+ GDK_THREADS_ENTER ();
- switch( msg->type ) {
+ switch (msg->type) {
case STARTING:
- DEBUG (("*** Message -- STARTING\n"));
- gtk_label_set_text( GTK_LABEL( queue_window_message ), msg->message );
- gtk_progress_bar_update( GTK_PROGRESS_BAR( queue_window_progress ), 0.0 );
- g_free( msg );
+ DEBUG (("*** Message -- STARTING %s\n", msg->message));
+ gtk_label_set_text (GTK_LABEL (queue_window_message),
+ msg->message);
+ gtk_progress_bar_update (GTK_PROGRESS_BAR
+ (queue_window_progress), 0.0);
+ g_free (msg->message);
+ g_free (msg);
break;
case PERCENTAGE:
DEBUG (("*** Message -- PERCENTAGE\n"));
- gtk_progress_bar_update( GTK_PROGRESS_BAR( queue_window_progress ), msg->percentage );
- g_free( msg );
+ gtk_progress_bar_update (GTK_PROGRESS_BAR
+ (queue_window_progress),
+ msg->percentage);
+ g_free (msg);
break;
case HIDE_PBAR:
DEBUG (("*** Message -- HIDE_PBAR\n"));
- gtk_progress_set_activity_mode( GTK_PROGRESS( queue_window_progress ), TRUE );
- timeout_toggle( TRUE );
-
- g_free( msg );
+ gtk_progress_set_activity_mode (GTK_PROGRESS
+ (queue_window_progress),
+ TRUE);
+ timeout_toggle (TRUE);
+ g_free (msg);
break;
case SHOW_PBAR:
DEBUG (("*** Message -- SHOW_PBAR\n"));
- timeout_toggle( FALSE );
- gtk_progress_set_activity_mode( GTK_PROGRESS( queue_window_progress ), FALSE );
-
- g_free( msg );
+ timeout_toggle (FALSE);
+ gtk_progress_set_activity_mode (GTK_PROGRESS
+ (queue_window_progress),
+ FALSE);
+ g_free (msg);
break;
case MESSAGE:
DEBUG (("*** Message -- MESSAGE\n"));
- gtk_label_set_text( GTK_LABEL( queue_window_message ),
- msg->message );
- g_free( msg->message );
- g_free( msg );
+ gtk_label_set_text (GTK_LABEL (queue_window_message),
+ msg->message);
+ g_free (msg->message);
+ g_free (msg);
break;
case PASSWORD:
DEBUG (("*** Message -- PASSWORD\n"));
- g_assert( msg->reply );
- g_assert( msg->success );
- get_password( msg );
+ g_assert (msg->reply);
+ g_assert (msg->success);
+ get_password (msg);
/* don't free msg! done later */
break;
case ERROR:
DEBUG (("*** Message -- ERROR\n"));
- show_error( msg );
- g_free( msg );
+ show_error (msg);
+ g_free (msg);
break;
/* Don't fall through; dispatch_func does the FINISHED
@@ -680,40 +887,58 @@ static gboolean read_msg( GIOChannel *source, GIOCondition condition, gpointer u
*/
case FINISHED:
- DEBUG (("*** Message -- FINISH\n"));
- if( msg->func )
- (msg->func)( msg->userdata );
+ DEBUG (
+ ("*** Message -- FINISH %s\n",
+ msg->clur->gerund));
+
+ if (msg->clur->spec->cleanup)
+ (msg->clur->spec->cleanup) (msg->clur->in_data,
+ msg->clur->op_data,
+ msg->clur->ex);
+
+ G_LOCK (ready_for_op);
+ ready_may_proceed = TRUE;
+ g_cond_signal (ready_cond);
+ G_UNLOCK (ready_for_op);
+
+ if (camel_exception_is_set (msg->clur->ex) &&
+ msg->clur->ex->id != CAMEL_EXCEPTION_USER_CANCEL) {
+ g_warning ("Error on cleanup of `%s': %s",
+ msg->clur->infinitive,
+ camel_exception_get_description (msg->
+ clur->
+ ex));
+ }
- if( op_queue == NULL ) {
- g_print("\tNo more ops -- hide %p.\n", queue_window);
+ free_closure (msg->clur);
+ queue_len--;
+
+ if (queue_len == 0) {
+ g_print ("\tNo more ops -- hide %p.\n", queue_window);
/* All done! */
- gtk_widget_hide( queue_window );
- mail_operation_in_progress = FALSE;
+ /* gtk_widget_hide seems to have problems sometimes
+ * here... perhaps because we're in a gsource handler,
+ * not a GTK event handler? Anyway, we defer the hiding
+ * til an idle. */
+ /*gtk_idle_add (hide_queue_window, NULL);*/
+ /*gtk_widget_hide (queue_window); */
+ destroy_queue_window ();
} else {
- g_print("\tOperation left.\n");
-
- /* There's another operation left */
-
- /* Pop it off the front */
- clur = op_queue->data;
- temp = g_slist_next( op_queue );
- g_slist_free_1( op_queue );
- op_queue = temp;
+ g_print ("\tOperation(s) left.\n");
- /* Clear it out of the 'pending' vbox */
- remove_next_pending();
-
- /* Run run run little process */
- dispatch( clur );
+ /* There's another operation left :
+ * Clear it out of the 'pending' vbox
+ */
+ remove_next_pending ();
}
- g_free( msg );
+ g_free (msg);
break;
default:
- g_warning( _("Corrupted message from dispatching thread?") );
+ g_warning (_("Corrupted message from dispatching thread?"));
break;
}
- GDK_THREADS_LEAVE();
+ GDK_THREADS_LEAVE ();
return TRUE;
}
@@ -725,23 +950,30 @@ static gboolean read_msg( GIOChannel *source, GIOCondition condition, gpointer u
* 'pending' message.
**/
-static void remove_next_pending( void )
+static void
+remove_next_pending (void)
{
GList *children;
- children = gtk_container_children( GTK_CONTAINER( queue_window_pending ) );
+ children =
+ gtk_container_children (GTK_CONTAINER (queue_window_pending));
/* Skip past the header label */
- children = g_list_first( children );
- children = g_list_next( children );
+ children = g_list_first (children);
+ children = g_list_next (children);
+
+ if (!children) {
+ g_warning ("Mistake in queue window!");
+ return;
+ }
/* Nuke the one on top */
- gtk_container_remove( GTK_CONTAINER( queue_window_pending ),
- GTK_WIDGET( children->data ) );
+ gtk_container_remove (GTK_CONTAINER (queue_window_pending),
+ GTK_WIDGET (children->data));
/* Hide it? */
- if( g_list_next( children ) == NULL )
- gtk_widget_hide( queue_window_pending );
+ if (g_list_next (children) == NULL)
+ gtk_widget_hide (queue_window_pending);
}
/**
@@ -750,28 +982,36 @@ static void remove_next_pending( void )
* Show the error dialog and wait for user OK
**/
-static void show_error( com_msg_t *msg )
+static void
+show_error (com_msg_t * msg)
{
GtkWidget *err_dialog;
+ gchar *old_message;
+
+ err_dialog = gnome_error_dialog (msg->message);
+ gnome_dialog_set_close (GNOME_DIALOG (err_dialog), TRUE);
+ gtk_signal_connect (GTK_OBJECT (err_dialog), "clicked",
+ (GtkSignalFunc) show_error_clicked, NULL);
+ g_free (msg->message);
- err_dialog = gnome_error_dialog( msg->message );
- gnome_dialog_set_close( GNOME_DIALOG(err_dialog), TRUE );
- gtk_signal_connect( GTK_OBJECT( err_dialog ), "clicked", (GtkSignalFunc) show_error_clicked, NULL );
- g_free( msg->message );
+ /* Save the old message, but display a new one right now */
+ gtk_label_get (GTK_LABEL (queue_window_message), &old_message);
+ gtk_object_set_data (GTK_OBJECT (err_dialog), "old_message",
+ g_strdup (old_message));
+ gtk_label_set_text (GTK_LABEL (queue_window_message),
+ _("Waiting for user to close error dialog"));
- G_LOCK( modal_lock );
+ G_LOCK (modal_lock);
- timeout_toggle( FALSE );
+ timeout_toggle (FALSE);
modal_may_proceed = FALSE;
- gtk_widget_show( GTK_WIDGET( err_dialog ) );
- gnome_win_hints_set_layer( err_dialog,
- WIN_LAYER_ONTOP );
- gnome_win_hints_set_state( err_dialog,
- WIN_STATE_ARRANGE_IGNORE );
- gnome_win_hints_set_hints( err_dialog,
+ gtk_widget_show_all (GTK_WIDGET (err_dialog));
+ gnome_win_hints_set_layer (err_dialog, WIN_LAYER_ONTOP);
+ gnome_win_hints_set_state (err_dialog, WIN_STATE_ARRANGE_IGNORE);
+ gnome_win_hints_set_hints (err_dialog,
WIN_HINTS_SKIP_FOCUS |
WIN_HINTS_SKIP_WINLIST |
- WIN_HINTS_SKIP_TASKBAR );
+ WIN_HINTS_SKIP_TASKBAR);
}
/**
@@ -781,12 +1021,21 @@ static void show_error( com_msg_t *msg )
* the dispatch thread is allowed to continue.
**/
-static void show_error_clicked( void )
+static void
+show_error_clicked (GtkObject * obj)
{
+ gchar *old_message;
+
+ /* Restore the old message */
+ old_message = gtk_object_get_data (obj, "old_message");
+ gtk_label_set_text (GTK_LABEL (queue_window_message),
+ old_message);
+ g_free (old_message);
+
modal_may_proceed = TRUE;
- timeout_toggle( TRUE );
- g_cond_signal( modal_cond );
- G_UNLOCK( modal_lock );
+ timeout_toggle (TRUE);
+ g_cond_signal (modal_cond);
+ G_UNLOCK (modal_lock);
}
/**
@@ -795,66 +1044,81 @@ static void show_error_clicked( void )
* Ask for a password and put the answer in *(msg->reply)
**/
-static void get_password( com_msg_t *msg )
+static void
+get_password (com_msg_t * msg)
{
GtkWidget *dialog;
+ gchar *old_message;
+
+ dialog = gnome_request_dialog (msg->secret, msg->message, NULL,
+ 0, get_password_cb, msg, NULL);
+ gnome_dialog_set_close (GNOME_DIALOG (dialog), TRUE);
+ gtk_signal_connect (GTK_OBJECT (dialog), "clicked",
+ get_password_clicked, msg);
- dialog = gnome_request_dialog( msg->secret, msg->message, NULL,
- 0, get_password_cb, msg,
- NULL );
- gnome_dialog_set_close( GNOME_DIALOG(dialog), TRUE );
- gtk_signal_connect( GTK_OBJECT( dialog ), "clicked", get_password_clicked, msg );
+ /* Save the old message, but display a new one right now */
+ gtk_label_get (GTK_LABEL (queue_window_message), &old_message);
+ gtk_object_set_data (GTK_OBJECT (dialog), "old_message", g_strdup(old_message));
+ gtk_label_set_text (GTK_LABEL (queue_window_message),
+ _("Waiting for user to enter data"));
- G_LOCK( modal_lock );
+ G_LOCK (modal_lock);
modal_may_proceed = FALSE;
- if( dialog == NULL ) {
+ if (dialog == NULL) {
*(msg->success) = FALSE;
- *(msg->reply) = g_strdup( _("Could not create dialog box.") );
+ *(msg->reply) = g_strdup (_("Could not create dialog box."));
modal_may_proceed = TRUE;
- g_cond_signal( modal_cond );
- G_UNLOCK( modal_lock );
+ g_cond_signal (modal_cond);
+ G_UNLOCK (modal_lock);
} else {
*(msg->reply) = NULL;
- timeout_toggle( FALSE );
- gtk_widget_show( GTK_WIDGET( dialog ) );
- gnome_win_hints_set_layer( dialog,
- WIN_LAYER_ONTOP );
- gnome_win_hints_set_state( dialog,
- WIN_STATE_ARRANGE_IGNORE );
- gnome_win_hints_set_hints( dialog,
+ timeout_toggle (FALSE);
+ gtk_widget_show_all (GTK_WIDGET (dialog));
+ gnome_win_hints_set_layer (dialog, WIN_LAYER_ONTOP);
+ gnome_win_hints_set_state (dialog, WIN_STATE_ARRANGE_IGNORE);
+ gnome_win_hints_set_hints (dialog,
WIN_HINTS_SKIP_FOCUS |
WIN_HINTS_SKIP_WINLIST |
- WIN_HINTS_SKIP_TASKBAR );
+ WIN_HINTS_SKIP_TASKBAR);
}
}
-static void get_password_cb( gchar *string, gpointer data )
+static void
+get_password_cb (gchar * string, gpointer data)
{
com_msg_t *msg = (com_msg_t *) data;
- if (string)
- *(msg->reply) = g_strdup( string );
- else
- *(msg->reply) = NULL;
+ if (string)
+ *(msg->reply) = g_strdup (string);
+ else
+ *(msg->reply) = NULL;
}
-static void get_password_clicked( GnomeDialog *dialog, gint button, gpointer user_data )
+static void
+get_password_clicked (GnomeDialog * dialog, gint button, gpointer user_data)
{
com_msg_t *msg = (com_msg_t *) user_data;
+ gchar *old_message;
+
+ /* Restore the old message */
+ old_message = gtk_object_get_data (GTK_OBJECT (dialog), "old_message");
+ gtk_label_set_text (GTK_LABEL (queue_window_message),
+ old_message);
+ g_free (old_message);
- if( button == 1 || *(msg->reply) == NULL ) {
+ if (button == 1 || *(msg->reply) == NULL) {
*(msg->success) = FALSE;
- *(msg->reply) = g_strdup( _("User cancelled query.") );
+ *(msg->reply) = g_strdup (_("User cancelled query."));
} else
*(msg->success) = TRUE;
- g_free( msg );
+ g_free (msg);
modal_may_proceed = TRUE;
- timeout_toggle( TRUE );
- g_cond_signal( modal_cond );
- G_UNLOCK( modal_lock );
+ timeout_toggle (TRUE);
+ g_cond_signal (modal_cond);
+ G_UNLOCK (modal_lock);
}
/* NOT totally copied from gtk+/gtk/testgtk.c, really! */
@@ -865,8 +1129,14 @@ progress_timeout (gpointer data)
gfloat new_val;
GtkAdjustment *adj;
+ if (queue_window == NULL) {
+ gtk_timeout_remove (progress_timeout_handle);
+ progress_timeout_handle = -1;
+ return FALSE;
+ }
+
adj = GTK_PROGRESS (data)->adjustment;
-
+
new_val = adj->value + 1;
if (new_val > adj->upper)
new_val = adj->lower;
@@ -885,20 +1155,100 @@ progress_timeout (gpointer data)
**/
static void
-timeout_toggle( gboolean active )
+timeout_toggle (gboolean active)
{
- if( (GTK_PROGRESS( queue_window_progress ))->activity_mode == 0 )
+ if (!queue_window)
+ return;
+
+ if ((GTK_PROGRESS (queue_window_progress))->activity_mode == 0)
return;
- if( active ) {
- if( progress_timeout_handle < 0 )
- progress_timeout_handle = gtk_timeout_add( 80, progress_timeout, queue_window_progress );
+ if (active) {
+ /* We do this in case queue_window_progress gets reset */
+ if (progress_timeout_handle < 0) {
+ progress_timeout_handle =
+ gtk_timeout_add (80, progress_timeout,
+ queue_window_progress);
+ } else {
+ gtk_timeout_remove (progress_timeout_handle);
+ progress_timeout_handle =
+ gtk_timeout_add (80, progress_timeout,
+ queue_window_progress);
+ }
} else {
- if( progress_timeout_handle >= 0 ) {
- gtk_timeout_remove( progress_timeout_handle );
+ if (progress_timeout_handle >= 0) {
+ gtk_timeout_remove (progress_timeout_handle);
progress_timeout_handle = -1;
}
}
}
-#endif
+/* This can theoretically run into problems where if a short operation starts
+ * and finishes, then another short operation starts and finishes a second
+ * later, we will see the window prematurely. My response: oh noooooo!
+ *
+ * Solution: keep the timeout's handle and remove the timeout upon reception
+ * of FINISH, and zero out the handle in this function. Whatever.
+ */
+static gboolean
+display_timeout (gpointer data)
+{
+ if (queue_len > 0 && queue_window) {
+ gtk_widget_show (queue_window);
+ gnome_win_hints_set_layer (queue_window, WIN_LAYER_ONTOP);
+ gnome_win_hints_set_state (queue_window,
+ WIN_STATE_ARRANGE_IGNORE);
+ gnome_win_hints_set_hints (queue_window,
+ WIN_HINTS_SKIP_FOCUS |
+ WIN_HINTS_SKIP_WINLIST |
+ WIN_HINTS_SKIP_TASKBAR);
+
+ if (queue_len == 1)
+ gtk_widget_hide (queue_window_pending);
+ }
+
+ return FALSE;
+}
+
+static closure_t *
+new_closure (const mail_operation_spec * spec, gpointer input,
+ gboolean free_in_data)
+{
+ closure_t *clur;
+
+ clur = g_new0 (closure_t, 1);
+ clur->spec = spec;
+ clur->in_data = input;
+ clur->free_in_data = free_in_data;
+ clur->ex = camel_exception_new ();
+
+ clur->op_data = g_malloc (spec->datasize);
+
+ camel_exception_init (clur->ex);
+
+ clur->infinitive = (spec->describe) (input, FALSE);
+ clur->gerund = (spec->describe) (input, TRUE);
+
+ return clur;
+}
+
+static void
+free_closure (closure_t *clur)
+{
+ clur->spec = NULL;
+
+ if (clur->free_in_data)
+ g_free (clur->in_data);
+ clur->in_data = NULL;
+
+ g_free (clur->op_data);
+ clur->op_data = NULL;
+
+ camel_exception_free (clur->ex);
+ clur->ex = NULL;
+
+ g_free (clur->infinitive);
+ g_free (clur->gerund);
+
+ g_free (clur);
+}