From a16344fff4780b6b1c5d8c2d6531963e1fe07d52 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Wed, 3 Jan 2001 01:18:21 +0000 Subject: Fix for mail_get_message change, use queue thread. 2001-01-02 Not Zed * mail-callbacks.c (view_msg): Fix for mail_get_message change, use queue thread. * folder-browser.c (done_message_selected): Fix mail_Get_message calls, use new thread. (do_message_selected): " * mail-ops.c (mail_get_message): Add a thread argument so callers can specify which queue it executes on. * mail-mt.c (mail_msg_free): Fix a free order problem. (mail_msg_destroy): Call mail_msg_free to do the work. (mail_msgport_replied): " (mail_msgport_replied): Check/display errors if we get them. (mail_msgport_received): If we have a describe function, say what we're doing, also set busy/unbusy. (mail_msgport_replied): Clear busy when we get a reply. (mail_get_password): Unset busy. (mail_msg_received): Set busy as we go. (mail_msg_destroy): Unset busy when done. (mail_status): Blah blah, new status interface, the other wans't workable with the way the shell api works. 2000-12-29 Not Zed * folder-browser.c (do_message_selected): If we are reconfiguring, just keep polling till we are done (yeah kinda shitty, but easy). (folder_browser_set_uri): Clear reconfigure flag here. ick. (got_folder): And here too. (on_right_click): Remove locking. (hide_sender): and here too. (hide_subject): And here. (on_right_click): If we are in reconfigure, then the whole menu is disabled. * mail-mt.c (status_busy_timeout): Clear the status_busy_timeout_id. * mail-local.c (local_storage_new_folder_cb): Made getting folders completely synchronous. The shell expects it, and it was only synchronous before by a sideeffect. (do_reconfigure_folder): Remove locking stuff. (do_reconfigure_folder): Use our own much simpler copying routine than that stupid move_folder_contents thing. (update_progress): Use mail_status_message() instead. (do_reconfigure_folder): Set the reconfigure flag during reconfigure & set busy flag. (cleanup_reconfigure_folder): clear busy flag. * mail-tools.c (mail_tool_uri_to_folder): Remove the tool_lock stuff. (mail_tool_uri_to_folder_noex): Clear exception on exit. (mail_tool_move_folder_contents): Get rid of this really stupid function that is only used in one place. * component-factory.c (owner_set_cb): Use direct calls to get the folders, as this code must run synchronous. Remove the event wait stuff. * mail-callbacks.c (edit_msg): Call mail_get_messages, and create the composers ourself. (do_edit_messages): get_messages callback, create the composers and connect to signals we need. (view_msg): Dont call do_view_messages, just call mail_get_messge for each to get them in parallel. (do_view_message): view a single message. * mail-ops.c (mail_edit_messages): Just use mail_get_messages for this operation. Removed the other async operation stuff. Changed my mind, just removed entirely. (mail_do_view_messages): Removed. (mail_do_setup_folder): Removed. (mail_do_scan_subfolders): Make this run synchronously, as every caller expects it to (even if they didn't realise). 2000-12-28 Not Zed * mail-callbacks.c (send_queued_mail): Dont expunge the folder here, but in send_queue, otherwise it might execute out of order. (expunge_folder): Remove the talbe prechange stuff, and infact references to the message_list folder, as we have our own folder. Also, dont allow expunge if we're already expunging. (expunged_folder): Clkear the expunging flag if we're finished. * folder-browser-factory.c (control_deactivate): Likewise here. Hrm, i thought this function required a callback, silly me. * mail-tools.c (mail_tool_make_message_attachment): Remov e locking. * folder-browser.c (on_message_selected): Use a timeout handler so we dont select immediately. (folder_browser_set_uri): Changed to use mail_get_folder. (got_folder): New callback called when get_folder is finished. (folder_browser_destroy): Use new sync interface. * mail-ops.c (mail_get_message): New function to asynchrounously get a message. : #define out mail_tool_camel_lock stuff entirely. (mail_get_folder): New function to asynchrounously get a folder. (mail_do_load_folder): Removed, replaced by more generic function above. (mail_do_display_message): Removed, replaced by the more generic funciton get_message. (mail_get_messages): New function to get a list of messages asynchronously. (mail_sync_folder): New interface to sync a folder async. (mail_expunge_folder): New interface for expunging folder, with callback. (do_send_queue): Remove lock stuff, and expunge if (and only if) successful, also sync the sent folder while we're at it. * session.c (mail_session_request_dialog): Changed to use new mail_get_password call. * mail-mt.[ch]: New threading/interthread messaging framework. * main.c (main): Init the message/thread system. svn path=/trunk/; revision=7223 --- mail/mail-mt.c | 517 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 517 insertions(+) create mode 100644 mail/mail-mt.c (limited to 'mail/mail-mt.c') diff --git a/mail/mail-mt.c b/mail/mail-mt.c new file mode 100644 index 0000000000..3441228ea4 --- /dev/null +++ b/mail/mail-mt.c @@ -0,0 +1,517 @@ + +#include +#include + +#include "e-util/e-msgport.h" +#include +#include + +#include "mail-mt.h" + +#include +#include +#include +#include +#include + +#include "folder-browser-factory.h" + +#define d(x) + +static void set_view_data(const char *current_message, int busy); + +static unsigned int mail_msg_seq; + +void *mail_msg_new(mail_msg_op_t *ops, EMsgPort *reply_port, size_t size) +{ + struct _mail_msg *msg; + + msg = g_malloc0(size); + msg->ops = ops; + msg->seq = mail_msg_seq++; + msg->msg.reply_port = reply_port; + camel_exception_init(&msg->ex); + + return msg; +} + +void mail_msg_free(void *msg) +{ + struct _mail_msg *m = msg; + + if (m->ops->destroy_msg) + m->ops->destroy_msg(m); + camel_exception_clear(&m->ex); + g_free(m); +} + +void mail_msg_check_error(void *msg) +{ + struct _mail_msg *m = msg; + char *what = NULL; + char *text; + GnomeDialog *gd; + + if (!camel_exception_is_set(&m->ex)) + return; + + if (m->ops->describe_msg) + what = m->ops->describe_msg(m, FALSE); + + if (what) + text = g_strdup_printf(_("Error while '%s':\n%s"), what, camel_exception_get_description(&m->ex)); + else + text = g_strdup_printf(_("Error while performing operation:\n%s"), camel_exception_get_description(&m->ex)); + + gd = (GnomeDialog *)gnome_error_dialog(text); + gnome_dialog_run_and_close(gd); + g_free(text); +} + +EMsgPort *mail_gui_port; +static GIOChannel *mail_gui_channel; +EMsgPort *mail_gui_reply_port; +static GIOChannel *mail_gui_reply_channel; + +/* a couple of global threads available */ +EThread *mail_thread_queued; /* for operations that can (or should) be queued */ +EThread *mail_thread_new; /* for operations that should run in a new thread each time */ + +static gboolean +mail_msgport_replied(GIOChannel *source, GIOCondition cond, void *d) +{ + EMsgPort *port = (EMsgPort *)d; + mail_msg_t *m; + + while (( m = (mail_msg_t *)e_msgport_get(port))) { + if (m->ops->reply_msg) + m->ops->reply_msg(m); + mail_msg_check_error(m); + if (m->ops->describe_msg) + mail_status_end(); + mail_msg_free(m); + } + + return TRUE; +} + +static gboolean +mail_msgport_received(GIOChannel *source, GIOCondition cond, void *d) +{ + EMsgPort *port = (EMsgPort *)d; + mail_msg_t *m; + + while (( m = (mail_msg_t *)e_msgport_get(port))) { + if (m->ops->describe_msg) { + char *text = m->ops->describe_msg(m, FALSE); + mail_status_start(text); + g_free(text); + } + if (m->ops->receive_msg) + m->ops->receive_msg(m); + if (m->msg.reply_port) + e_msgport_reply((EMsg *)m); + else { + if (m->ops->reply_msg) + m->ops->reply_msg(m); + if (m->ops->describe_msg) + mail_status_end(); + mail_msg_free(m); + } + } + + return TRUE; +} + +static void +mail_msg_destroy(EThread *e, EMsg *msg, void *data) +{ + mail_msg_t *m = (mail_msg_t *)msg; + + if (m->ops->describe_msg) + mail_status_end(); + mail_msg_free(m); +} + +static void +mail_msg_received(EThread *e, EMsg *msg, void *data) +{ + mail_msg_t *m = (mail_msg_t *)msg; + + if (m->ops->describe_msg) { + char *text = m->ops->describe_msg(m, FALSE); + printf("message received at thread\n"); + mail_status_start(text); + g_free(text); + } + + if (m->ops->receive_msg) + m->ops->receive_msg(m); +} + +void mail_msg_init(void) +{ + mail_gui_reply_port = e_msgport_new(); + mail_gui_reply_channel = g_io_channel_unix_new(e_msgport_fd(mail_gui_reply_port)); + g_io_add_watch(mail_gui_reply_channel, G_IO_IN, mail_msgport_replied, mail_gui_reply_port); + + mail_gui_port = e_msgport_new(); + mail_gui_channel = g_io_channel_unix_new(e_msgport_fd(mail_gui_port)); + g_io_add_watch(mail_gui_channel, G_IO_IN, mail_msgport_received, mail_gui_port); + + mail_thread_queued = e_thread_new(E_THREAD_QUEUE); + e_thread_set_msg_destroy(mail_thread_queued, mail_msg_destroy, 0); + e_thread_set_msg_received(mail_thread_queued, mail_msg_received, 0); + e_thread_set_reply_port(mail_thread_queued, mail_gui_reply_port); + + mail_thread_new = e_thread_new(E_THREAD_NEW); + e_thread_set_msg_destroy(mail_thread_new, mail_msg_destroy, 0); + e_thread_set_msg_received(mail_thread_new, mail_msg_received, 0); + e_thread_set_reply_port(mail_thread_new, mail_gui_reply_port); +} + +/* ********************************************************************** */ + +struct _set_msg { + struct _mail_msg msg; + char *text; +}; + +/* locks */ +static pthread_mutex_t status_lock = PTHREAD_MUTEX_INITIALIZER; +#define STATUS_BUSY_PENDING (2) + +#define MAIL_MT_LOCK(x) pthread_mutex_lock(&x) +#define MAIL_MT_UNLOCK(x) pthread_mutex_unlock(&x) + +/* blah blah */ + +#define STATUS_DELAY (5) + +static int status_depth; +static int status_showing; +static int status_shown; +static char *status_message_next; +static int status_message_clear; +static int status_timeout_id; +static int status_busy; + +struct _status_msg { + struct _mail_msg msg; + char *text; + int busy; +}; + +static gboolean +status_timeout(void *data) +{ + char *msg; + int busy = 0; + + d(printf("got status timeout\n")); + + MAIL_MT_LOCK(status_lock); + if (status_message_next) { + d(printf("setting message to '%s' busy %d\n", status_message_next, status_busy)); + msg = status_message_next; + status_message_next = NULL; + busy = status_depth > 0; + status_message_clear = 0; + MAIL_MT_UNLOCK(status_lock); + + /* copy msg so we can set it outside the lock */ + /* unset first is a hack to avoid the stack stuff that doesn't and can't work anyway */ + if (status_shown) + set_view_data(NULL, FALSE); + status_shown = TRUE; + set_view_data(msg, busy); + g_free(msg); + return TRUE; + } + + /* the delay-clear stuff doesn't work yet. Dont care ... */ + + status_showing = FALSE; + status_message_clear++; + if (status_message_clear >= STATUS_DELAY && status_depth==0) { + d(printf("clearing message, busy = %d\n", status_depth)); + } else { + d(printf("delaying clear\n")); + MAIL_MT_UNLOCK(status_lock); + return TRUE; + } + + status_timeout_id = 0; + + MAIL_MT_UNLOCK(status_lock); + + if (status_shown) + set_view_data(NULL, FALSE); + status_shown = FALSE; + + return FALSE; +} + +static void do_set_status(struct _mail_msg *mm) +{ + struct _status_msg *m = (struct _status_msg *)mm; + + MAIL_MT_LOCK(status_lock); + + if (status_timeout_id != 0) + gtk_timeout_remove(status_timeout_id); + + status_timeout_id = gtk_timeout_add(500, status_timeout, 0); + status_message_clear = 0; + + MAIL_MT_UNLOCK(status_lock); + + /* the 'clear' stuff doesn't really work yet, but oh well, + this stuff here needs a little changing for it to work */ + if (status_shown) + set_view_data(NULL, status_depth != 0); + status_shown = 0; + + if (m->text) { + status_shown = 1; + set_view_data(m->text, status_depth != 0); + } +} + +static void do_del_status(struct _mail_msg *mm) +{ + struct _status_msg *m = (struct _status_msg *)mm; + + g_free(m->text); +} + +struct _mail_msg_op set_status_op = { + NULL, + do_set_status, + NULL, + do_del_status, +}; + +/* start a new operation */ +void mail_status_start(const char *msg) +{ + struct _status_msg *m = NULL; + + MAIL_MT_LOCK(status_lock); + status_depth++; + MAIL_MT_UNLOCK(status_lock); + + if (msg == NULL || msg[0] == 0) + msg = _("Working"); + + m = mail_msg_new(&set_status_op, NULL, sizeof(*m)); + m->text = g_strdup(msg); + m->busy = TRUE; + + e_msgport_put(mail_gui_port, &m->msg.msg); +} + +/* end it */ +void mail_status_end(void) +{ + struct _status_msg *m = NULL; + + m = mail_msg_new(&set_status_op, NULL, sizeof(*m)); + m->text = NULL; + + MAIL_MT_LOCK(status_lock); + status_depth--; + m->busy = status_depth = 0; + MAIL_MT_UNLOCK(status_lock); + + e_msgport_put(mail_gui_port, &m->msg.msg); +} + +/* message during it */ +void mail_status(const char *msg) +{ + if (msg == NULL || msg[0] == 0) + msg = _("Working"); + + MAIL_MT_LOCK(status_lock); + + g_free(status_message_next); + status_message_next = g_strdup(msg); + + MAIL_MT_UNLOCK(status_lock); +} + +void mail_statusf(const char *fmt, ...) +{ + va_list ap; + char *text; + + va_start(ap, fmt); + text = g_strdup_vprintf(fmt, ap); + va_end(ap); + mail_status(text); + g_free(text); +} + +/* ********************************************************************** */ + +struct _pass_msg { + struct _mail_msg msg; + char *prompt; + int secret; + char *result; +}; + +/* libgnomeui's idea of an api/gui is very weird ... hence this dumb hack */ +static void focus_on_entry(GtkWidget *widget, void *user_data) +{ + if (GTK_IS_ENTRY(widget)) + gtk_widget_grab_focus(widget); +} + +static void pass_got(char *string, void *data) +{ + struct _pass_msg *m = data; + + if (string) + m->result = g_strdup (string); +} + +static void +do_get_pass(struct _mail_msg *mm) +{ + struct _pass_msg *m = (struct _pass_msg *)mm; + GtkWidget *dialogue; + + /* this api is just awful ... hence the hacks */ + dialogue = gnome_request_dialog(m->secret, m->prompt, NULL, + 0, pass_got, m, NULL); + e_container_foreach_leaf((GtkContainer *)dialogue, focus_on_entry, NULL); + + /* hrm, we can't run this async since the gui_port from which we're called + will reply to our message for us */ + gnome_dialog_run_and_close((GnomeDialog *)dialogue); + + /*gtk_widget_show(dialogue);*/ +} + +static void +do_free_pass(struct _mail_msg *mm) +{ + /*struct _pass_msg *m = (struct _pass_msg *)mm;*/ + + /* the string is passed out so we dont need to free it */ +} + +struct _mail_msg_op get_pass_op = { + NULL, + do_get_pass, + NULL, + do_free_pass, +}; + +/* returns the password, or NULL if cancelled */ +char * +mail_get_password(char *prompt, gboolean secret) +{ + char *ret; + struct _pass_msg *m, *r; + EMsgPort *pass_reply; + + pass_reply = e_msgport_new(); + + m = mail_msg_new(&get_pass_op, pass_reply, sizeof(*m)); + + m->prompt = prompt; + m->secret = secret; + + e_msgport_put(mail_gui_port, (EMsg *)m); + e_msgport_wait(pass_reply); + r = (struct _pass_msg *)e_msgport_get(pass_reply); + + g_assert(r == m); + + ret = m->result; + + mail_msg_free(m); + e_msgport_destroy(pass_reply); + + return ret; +} + + + +/* ******************** */ + +/* FIXME FIXME FIXME This is a totally evil hack. */ + +static GNOME_Evolution_ShellView +retrieve_shell_view_interface_from_control (BonoboControl *control) +{ + Bonobo_ControlFrame control_frame; + GNOME_Evolution_ShellView shell_view_interface; + CORBA_Environment ev; + + control_frame = bonobo_control_get_control_frame (control); + + if (control_frame == NULL) + return CORBA_OBJECT_NIL; + + CORBA_exception_init (&ev); + shell_view_interface = Bonobo_Unknown_queryInterface (control_frame, + "IDL:GNOME/Evolution/ShellView:1.0", + &ev); + CORBA_exception_free (&ev); + + if (shell_view_interface != CORBA_OBJECT_NIL) + gtk_object_set_data (GTK_OBJECT (control), + "mail_threads_shell_view_interface", + shell_view_interface); + else + g_warning ("Control frame doesn't have Evolution/ShellView."); + + return shell_view_interface; +} + +static void +set_view_data(const char *current_message, int busy) +{ + EList *controls; + EIterator *it; + + controls = folder_browser_factory_get_control_list (); + for (it = e_list_get_iterator (controls); e_iterator_is_valid (it); e_iterator_next (it)) { + BonoboControl *control; + GNOME_Evolution_ShellView shell_view_interface; + CORBA_Environment ev; + + control = BONOBO_CONTROL (e_iterator_get (it)); + + shell_view_interface = gtk_object_get_data (GTK_OBJECT (control), "mail_threads_shell_view_interface"); + + if (shell_view_interface == CORBA_OBJECT_NIL) + shell_view_interface = retrieve_shell_view_interface_from_control (control); + + CORBA_exception_init (&ev); + + if (shell_view_interface != CORBA_OBJECT_NIL) { + if ((current_message == NULL || current_message[0] == 0) && ! busy) { + printf("clearing msg\n"); + GNOME_Evolution_ShellView_unsetMessage (shell_view_interface, &ev); + } else { + printf("setting msg %s\n", current_message); + GNOME_Evolution_ShellView_setMessage (shell_view_interface, + current_message?current_message:"", + busy, + &ev); + } + } + + CORBA_exception_free (&ev); + + /* yeah we only set the first one. Why? Because it seems to leave + random ones lying around otherwise. Shrug. */ + break; + } + gtk_object_unref(GTK_OBJECT(it)); +} -- cgit v1.2.3