From 8cb514d6dd9497893a35a089d07a132d51263ee7 Mon Sep 17 00:00:00 2001 From: Peter Williams Date: Thu, 10 Aug 2000 17:30:50 +0000 Subject: Merge with camel-async. svn path=/trunk/; revision=4687 --- mail/mail-threads.c | 1016 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 683 insertions(+), 333 deletions(-) (limited to 'mail/mail-threads.c') 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 -#ifdef USE_BROKEN_THREADS - #include #include #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 + +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); +} -- cgit v1.2.3