From 49f8a687a41e635cd83807d33c74afe8e55fb3df Mon Sep 17 00:00:00 2001 From: Not Zed Date: Thu, 8 Feb 2001 01:42:53 +0000 Subject: Changed to push the operation into a status stack. 2001-02-07 Not Zed * camel-operation.c (camel_operation_start): Changed to push the operation into a status stack. (camel_operation_progress): Changed to only accept % complete. (camel_operation_reset): Free status stack as well. * providers/pop3/camel-pop3-folder.c (pop3_get_message): Get the octect count from the return line, and pass it to get_additional_data(). (pop3_refresh_info): Added status stuff. * providers/pop3/camel-pop3-store.c (camel_pop3_command_get_additional_data): Added a total bytes expected argument for progress reporting & fixed callers. (camel_pop3_command_get_additional_data): Added progress reporting. * providers/local/camel-mbox-summary.c (mbox_summary_sync_full): (mbox_summary_sync_quick): (summary_rebuild): Added progress reporting stuff. svn path=/trunk/; revision=8095 --- camel/ChangeLog | 35 ++ camel/Makefile.am | 2 + camel/camel-operation.c | 540 +++++++++++++++++++++++++++++ camel/camel-operation.h | 66 ++++ camel/camel-remote-store.c | 9 +- camel/camel-session.c | 254 -------------- camel/camel-session.h | 18 - camel/camel-stream-fs.c | 10 +- camel/providers/local/camel-mbox-summary.c | 42 ++- camel/providers/pop3/camel-pop3-folder.c | 31 +- camel/providers/pop3/camel-pop3-store.c | 15 +- camel/providers/pop3/camel-pop3-store.h | 3 +- 12 files changed, 729 insertions(+), 296 deletions(-) create mode 100644 camel/camel-operation.c create mode 100644 camel/camel-operation.h (limited to 'camel') diff --git a/camel/ChangeLog b/camel/ChangeLog index 0dca380b40..059966a30c 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,25 @@ +2001-02-07 Not Zed + + * camel-operation.c (camel_operation_start): Changed to push the + operation into a status stack. + (camel_operation_progress): Changed to only accept % complete. + (camel_operation_reset): Free status stack as well. + + * providers/pop3/camel-pop3-folder.c (pop3_get_message): Get the + octect count from the return line, and pass it to + get_additional_data(). + (pop3_refresh_info): Added status stuff. + + * providers/pop3/camel-pop3-store.c + (camel_pop3_command_get_additional_data): Added a total bytes + expected argument for progress reporting & fixed callers. + (camel_pop3_command_get_additional_data): Added progress + reporting. + + * providers/local/camel-mbox-summary.c (mbox_summary_sync_full): + (mbox_summary_sync_quick): + (summary_rebuild): Added progress reporting stuff. + 2001-02-07 Jeffrey Stedfast * camel-search-private.c (CAMEL_SEARCH_COMPARE): Macro for making @@ -27,6 +49,19 @@ 2001-02-06 Not Zed + * camel-session.c: Removed all the camel_cancel stuff. + + * camel-stream-fs.c (stream_read): Change to use camel_operation. + (stream_write): " + + * camel-remote-store.c (socket_connect): Change to use + camel_operation rather than camel_cancel stuff. + Removed gal include, WTF is that doing there anyway? + + * Makefile.am (libcamel_la_SOURCES): + (libcamelinclude_HEADERS): Added camel-operation.[ch], stuff to + handle notification and cancellation. + * camel-search-private.c: Removed unwanted header. It was never put in for a reason. Stop fixing irrelevant warnings. diff --git a/camel/Makefile.am b/camel/Makefile.am index 063ec03f08..55d28f48fc 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -46,6 +46,7 @@ libcamel_la_SOURCES = \ camel-movemail.c \ camel-multipart.c \ camel-object.c \ + camel-operation.c \ camel-provider.c \ camel-remote-store.c \ camel-search-private.c \ @@ -102,6 +103,7 @@ libcamelinclude_HEADERS = \ camel-movemail.h \ camel-multipart.h \ camel-object.h \ + camel-operation.h \ camel-provider.h \ camel-remote-store.h \ camel-seekable-stream.h \ diff --git a/camel/camel-operation.c b/camel/camel-operation.c new file mode 100644 index 0000000000..9d631fca7b --- /dev/null +++ b/camel/camel-operation.c @@ -0,0 +1,540 @@ + +#include "config.h" + +#include +#ifdef ENABLE_THREADS +#include +#endif + +#include +#include "camel-operation.h" +#include "e-util/e-msgport.h" + +#define d(x) + +/* ********************************************************************** */ + +struct _CamelOperation { + pthread_t id; /* id of running thread */ + guint32 flags; /* cancelled ? */ + int blocked; /* cancellation blocked depth */ + int refcount; + + CamelOperationStatusFunc status; + void *status_data; + time_t status_update; + + /* stack of status messages (char *) */ + GSList *status_stack; + +#ifdef ENABLE_THREADS + EMsgPort *cancel_port; + int cancel_fd; + pthread_mutex_t lock; +#endif +}; + +#define CAMEL_OPERATION_CANCELLED (1<<0) + +#ifdef ENABLE_THREADS +#define CAMEL_OPERATION_LOCK(cc) pthread_mutex_lock(&cc->lock) +#define CAMEL_OPERATION_UNLOCK(cc) pthread_mutex_unlock(&cc->lock) +#define CAMEL_ACTIVE_LOCK() pthread_mutex_lock(&operation_active_lock) +#define CAMEL_ACTIVE_UNLOCK() pthread_mutex_unlock(&operation_active_lock) +static pthread_mutex_t operation_active_lock = PTHREAD_MUTEX_INITIALIZER; +#else +#define CAMEL_OPERATION_LOCK(cc) +#define CAMEL_OPERATION_UNLOCK(cc) +#define CAMEL_ACTIVE_LOCK() +#define CAMEL_ACTIVE_UNLOCK() +#endif + +static GHashTable *operation_active; + +typedef struct _CamelOperationMsg { + EMsg msg; +} CamelOperationMsg ; + +/** + * camel_operation_new: + * @status: Callback for receiving status messages. + * @status_data: User data. + * + * Create a new camel operation handle. Camel operation handles can + * be used in a multithreaded application (or a single operation + * handle can be used in a non threaded appliation) to cancel running + * operations and to obtain notification messages of the internal + * status of messages. + * + * Return value: A new operation handle. + **/ +CamelOperation *camel_operation_new(CamelOperationStatusFunc status, void *status_data) +{ + CamelOperation *cc; + + cc = g_malloc0(sizeof(*cc)); + + cc->flags = 0; + cc->blocked = 0; + cc->refcount = 1; + cc->status = status; + cc->status_data = status_data; +#ifdef ENABLE_THREADS + cc->id = ~0; + cc->cancel_port = e_msgport_new(); + cc->cancel_fd = e_msgport_fd(cc->cancel_port); + pthread_mutex_init(&cc->lock, NULL); +#endif + + return cc; +} + +/** + * camel_operation_reset: + * @cc: + * + * Resets an operation cancel state and message. + **/ +void camel_operation_reset(CamelOperation *cc) +{ + GSList *n; + +#ifdef ENABLE_THREADS + CamelOperationMsg *msg; + + while ((msg = (CamelOperationMsg *)e_msgport_get(cc->cancel_port))) + g_free(msg); +#endif + + n = cc->status_stack; + while (n) { + g_free(n->data); + n = n->next; + } + g_slist_free(cc->status_stack); + cc->status_stack = NULL; + + cc->flags = 0; + cc->blocked = 0; +} + +/** + * camel_operation_ref: + * @cc: + * + * Add a reference to the CamelOperation @cc. + **/ +void camel_operation_ref(CamelOperation *cc) +{ + CAMEL_OPERATION_LOCK(cc); + cc->refcount++; + CAMEL_OPERATION_UNLOCK(cc); +} + +/** + * camel_operation_unref: + * @cc: + * + * Unref and potentially free @cc. + **/ +void camel_operation_unref(CamelOperation *cc) +{ + GSList *n; +#ifdef ENABLE_THREADS + CamelOperationMsg *msg; + + if (cc->refcount == 1) { + while ((msg = (CamelOperationMsg *)e_msgport_get(cc->cancel_port))) + g_free(msg); + + e_msgport_destroy(cc->cancel_port); +#endif + n = cc->status_stack; + while (n) { + g_warning("Camel operation status stack non empty: %s", (char *)n->data); + g_free(n->data); + n = n->next; + } + g_slist_free(cc->status_stack); + + g_free(cc); + } else { + CAMEL_OPERATION_LOCK(cc); + cc->refcount--; + CAMEL_OPERATION_UNLOCK(cc); + } +} + +/** + * camel_operation_cancel_block: + * @cc: + * + * Block cancellation for this operation. If @cc is NULL, then the + * current thread is blocked. + **/ +void camel_operation_cancel_block(CamelOperation *cc) +{ + CAMEL_ACTIVE_LOCK(); + if (operation_active == NULL) + operation_active = g_hash_table_new(NULL, NULL); + + if (cc == NULL) + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + CAMEL_ACTIVE_UNLOCK(); + + if (cc) { + CAMEL_OPERATION_LOCK(cc); + cc->blocked++; + CAMEL_OPERATION_UNLOCK(cc); + } +} + +/** + * camel_operation_cancel_unblock: + * @cc: + * + * Unblock cancellation, when the unblock count reaches the block + * count, then this operation can be cancelled. If @cc is NULL, then + * the current thread is unblocked. + **/ +void camel_operation_cancel_unblock(CamelOperation *cc) +{ + CAMEL_ACTIVE_LOCK(); + if (operation_active == NULL) + operation_active = g_hash_table_new(NULL, NULL); + + if (cc == NULL) + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + CAMEL_ACTIVE_UNLOCK(); + + if (cc) { + CAMEL_OPERATION_LOCK(cc); + cc->blocked--; + CAMEL_OPERATION_UNLOCK(cc); + } +} + +static void +cancel_thread(void *key, CamelOperation *cc, void *data) +{ + if (cc) + camel_operation_cancel(cc); +} + +/** + * camel_operation_cancel: + * @cc: + * + * Cancel a given operation. If @cc is NULL then all outstanding + * operations are cancelled. + **/ +void camel_operation_cancel(CamelOperation *cc) +{ + CamelOperationMsg *msg; + + if (cc == NULL) { + if (operation_active) { + CAMEL_ACTIVE_LOCK(); + g_hash_table_foreach(operation_active, (GHFunc)cancel_thread, NULL); + CAMEL_ACTIVE_UNLOCK(); + } + } else if ((cc->flags & CAMEL_OPERATION_CANCELLED) == 0) { + d(printf("cancelling thread %d\n", cc->id)); + + CAMEL_OPERATION_LOCK(cc); + msg = g_malloc0(sizeof(*msg)); + e_msgport_put(cc->cancel_port, (EMsg *)msg); + cc->flags |= CAMEL_OPERATION_CANCELLED; + CAMEL_OPERATION_UNLOCK(cc); + } +} + +/** + * camel_operation_register: + * @cc: + * + * Register a thread or the main thread for cancellation through @cc. + * If @cc is NULL, then a new cancellation is created for this thread, + * but may only be cancelled from the same thread. + * + * All calls to operation_register() should be matched with calls to + * operation_unregister(), or resources will be lost. + **/ +void camel_operation_register(CamelOperation *cc) +{ + pthread_t id = pthread_self(); + + CAMEL_ACTIVE_LOCK(); + + if (operation_active == NULL) + operation_active = g_hash_table_new(NULL, NULL); + + if (cc == NULL) { + cc = g_hash_table_lookup(operation_active, (void *)id); + if (cc == NULL) { + cc = camel_operation_new(NULL, NULL); + } + } + + cc->id = id; + g_hash_table_insert(operation_active, (void *)id, cc); + + d(printf("registering thread %ld for cancellation\n", id)); + + CAMEL_ACTIVE_UNLOCK(); + + camel_operation_ref(cc); +} + +/** + * camel_operation_unregister: + * @cc: + * + * Unregister a given operation from being cancelled. If @cc is NULL, + * then the current thread is used. + **/ +void camel_operation_unregister(CamelOperation *cc) +{ + CAMEL_ACTIVE_LOCK(); + + if (operation_active == NULL) + operation_active = g_hash_table_new(NULL, NULL); + + if (cc == NULL) { + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + if (cc == NULL) { + g_warning("Trying to unregister a thread that was never registered for cancellation"); + } + } + + if (cc) + g_hash_table_remove(operation_active, (void *)cc->id); + + CAMEL_ACTIVE_UNLOCK(); + + d({if (cc) printf("unregistering thread %d for cancellation\n", cc->id);}); + + if (cc) + camel_operation_unref(cc); +} + +/** + * camel_operation_cancel_check: + * @cc: + * + * Check if cancellation has been applied to @cc. If @cc is NULL, + * then the CamelOperation registered for the current thread is used. + * + * Return value: TRUE if the operation has been cancelled. + **/ +gboolean camel_operation_cancel_check(CamelOperation *cc) +{ + CamelOperationMsg *msg; + + d(printf("checking for cancel in thread %d\n", pthread_self())); + + if (cc == NULL) { + if (operation_active) { + CAMEL_ACTIVE_LOCK(); + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + CAMEL_ACTIVE_UNLOCK(); + } + if (cc == NULL) + return FALSE; + } + + if (cc->blocked > 0) { + d(printf("ahah! cancellation is blocked\n")); + return FALSE; + } + + if (cc->flags & CAMEL_OPERATION_CANCELLED) { + d(printf("previously cancelled\n")); + return TRUE; + } + + msg = (CamelOperationMsg *)e_msgport_get(cc->cancel_port); + if (msg) { + d(printf("Got cancellation message\n")); + CAMEL_OPERATION_LOCK(cc); + cc->flags |= CAMEL_OPERATION_CANCELLED; + CAMEL_OPERATION_UNLOCK(cc); + return TRUE; + } + return FALSE; +} + +/** + * camel_operation_cancel_fd: + * @cc: + * + * Retrieve a file descriptor that can be waited on (select, or poll) + * for read, to asynchronously detect cancellation. + * + * Return value: The fd, or -1 if cancellation is not available + * (blocked, or has not been registered for this thread). + **/ +int camel_operation_cancel_fd(CamelOperation *cc) +{ + if (cc == NULL) { + if (operation_active) { + CAMEL_ACTIVE_LOCK(); + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + CAMEL_ACTIVE_UNLOCK(); + } + if (cc == NULL) + return -1; + } + if (cc->blocked) + return -1; + + return cc->cancel_fd; +} + +/** + * camel_operation_start: + * @cc: + * @what: + * @: + * + * Report the start of an operation. All start operations should have + * similar end operations. + **/ +void camel_operation_start(CamelOperation *cc, char *what, ...) +{ + va_list ap; + char *msg; + + if (operation_active == NULL) + return; + + if (cc == NULL) { + CAMEL_ACTIVE_LOCK(); + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + CAMEL_ACTIVE_UNLOCK(); + if (cc == NULL) + return; + } + + if (cc->status == NULL) + return; + + va_start(ap, what); + msg = g_strdup_vprintf(what, ap); + va_end(ap); + cc->status(cc, msg, CAMEL_OPERATION_START, cc->status_data); + cc->status_update = 0; + cc->status_stack = g_slist_prepend(cc->status_stack, msg); + d(printf("start '%s'\n", msg, pc)); +} + +/** + * camel_operation_progress: + * @cc: Operation to report to. + * @pc: Percent complete, 0 to 100. + * + * Report progress on the current operation. If @cc is NULL, then the + * currently registered operation is used. @pc reports the current + * percentage of completion, which should be in the range of 0 to 100. + * + * If the total percentage is not know, then use + * camel_operation_progress_count(). + **/ +void camel_operation_progress(CamelOperation *cc, int pc) +{ + char *msg; + time_t now; + + if (operation_active == NULL) + return; + + if (cc == NULL) { + CAMEL_ACTIVE_LOCK(); + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + CAMEL_ACTIVE_UNLOCK(); + if (cc == NULL) + return; + } + + if (cc->status == NULL) + return; + + if (cc->status_stack == NULL) + return; + + now = time(0); + if (cc->status_update != now) { + msg =cc->status_stack->data; + cc->status(cc, msg, pc, cc->status_data); + d(printf("progress '%s' %d %%\n", msg, pc)); + cc->status_update = now; + } +} + +void camel_operation_progress_count(CamelOperation *cc, int sofar) +{ + char *msg; + time_t now; + + if (operation_active == NULL) + return; + + if (cc == NULL) { + CAMEL_ACTIVE_LOCK(); + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + CAMEL_ACTIVE_UNLOCK(); + if (cc == NULL) + return; + } + + if (cc->status == NULL) + return; + + if (cc->status_stack == NULL) + return; + + /* FIXME: generate some meaningful pc value */ + now = time(0); + if (cc->status_update != now) { + msg =cc->status_stack->data; + cc->status(cc, msg, sofar, cc->status_data); + d(printf("progress '%s' %d done\n", msg, sofar)); + cc->status_update = now; + } +} + +/** + * camel_operation_end: + * @cc: + * @what: Format string. + * @: + * + * Report the end of an operation. If @cc is NULL, then the currently + * registered operation is notified. + **/ +void camel_operation_end(CamelOperation *cc) +{ + char *msg; + + if (operation_active == NULL) + return; + + if (cc == NULL) { + CAMEL_ACTIVE_LOCK(); + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + CAMEL_ACTIVE_UNLOCK(); + if (cc == NULL) + return; + } + + if (cc->status == NULL) + return; + + if (cc->status_stack == NULL) + return; + + msg = cc->status_stack->data; + cc->status(cc, msg, CAMEL_OPERATION_END, cc->status_data); + g_free(msg); + cc->status_stack = g_slist_remove_link(cc->status_stack, cc->status_stack); +} diff --git a/camel/camel-operation.h b/camel/camel-operation.h new file mode 100644 index 0000000000..4a61b40dfc --- /dev/null +++ b/camel/camel-operation.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Authors: Michael Zucchi + * + * Copyright 2001 Ximian, Inc. (http://www.ximian.com/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef CAMEL_OPERATION_H +#define CAMEL_OPERATION_H 1 + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +/* cancellation helper stuff, not yet finalised */ + +typedef struct _CamelOperation CamelOperation; + +typedef void (*CamelOperationStatusFunc)(struct _CamelOperation *op, const char *what, int pc, void *data); + +enum _camel_operation_status_t { + CAMEL_OPERATION_START = -1, + CAMEL_OPERATION_END = -2, +}; + +/* main thread functions */ +CamelOperation *camel_operation_new(CamelOperationStatusFunc status, void *status_data); +void camel_operation_ref(CamelOperation *cc); +void camel_operation_unref(CamelOperation *cc); +void camel_operation_reset(CamelOperation *cc); +void camel_operation_cancel(CamelOperation *cc); +/* subthread functions */ +void camel_operation_register(CamelOperation *cc); +void camel_operation_unregister(CamelOperation *cc); +/* called internally by camel, for the current thread */ +void camel_operation_cancel_block(CamelOperation *cc); +void camel_operation_cancel_unblock(CamelOperation *cc); +int camel_operation_cancel_check(CamelOperation *cc); +int camel_operation_cancel_fd(CamelOperation *cc); + +void camel_operation_start(CamelOperation *cc, char *what, ...); +void camel_operation_progress(CamelOperation *cc, int pc); +void camel_operation_progress_count(CamelOperation *cc, int sofar); +void camel_operation_end(CamelOperation *cc); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_OPERATION_H */ diff --git a/camel/camel-remote-store.c b/camel/camel-remote-store.c index e81ef5e91e..d1352300b7 100644 --- a/camel/camel-remote-store.c +++ b/camel/camel-remote-store.c @@ -36,8 +36,6 @@ #include #include -#include - #include "camel-remote-store.h" #include "camel-folder.h" #include "camel-exception.h" @@ -49,6 +47,7 @@ #include "string-utils.h" #include "camel-private.h" +#include "camel-operation.h" #define d(x) x #if d(!)0 @@ -202,7 +201,7 @@ timeout_cb (gpointer data) return TRUE; } -/* this is a 'cancellable' connect, cancellable from camel_cancel etc */ +/* this is a 'cancellable' connect, cancellable from camel_operation_cancel etc */ /* returns -1 & errno == EINTR if the connection was cancelled */ static int socket_connect(struct hostent *h, int port) { @@ -214,7 +213,7 @@ static int socket_connect(struct hostent *h, int port) int cancel_fd; /* see if we're cancelled yet */ - if (camel_cancel_check(NULL)) { + if (camel_operation_cancel_check(NULL)) { errno = EINTR; return -1; } @@ -226,7 +225,7 @@ static int socket_connect(struct hostent *h, int port) fd = socket (h->h_addrtype, SOCK_STREAM, 0); - cancel_fd = camel_cancel_fd(NULL); + cancel_fd = camel_operation_cancel_fd(NULL); if (cancel_fd == -1) { ret = connect(fd, (struct sockaddr *)&sin, sizeof (sin)); if (ret == -1) { diff --git a/camel/camel-session.c b/camel/camel-session.c index 8f97c507b9..a8ca5983ec 100644 --- a/camel/camel-session.c +++ b/camel/camel-session.c @@ -32,7 +32,6 @@ #include #include #include -#include #include "camel-session.h" #include "camel-store.h" #include "camel-transport.h" @@ -509,256 +508,3 @@ camel_session_remove_timeout (CamelSession *session, guint handle) { return session->remover (handle); } - -/* ********************************************************************** */ - -struct _CamelCancel { - pthread_t id; /* id of running thread */ - guint32 flags; /* cancelled ? */ - int blocked; /* cancellation blocked depth */ - int refcount; -#ifdef ENABLE_THREADS - EMsgPort *cancel_port; - int cancel_fd; - pthread_mutex_t lock; -#endif -}; - -#define CAMEL_CANCEL_CANCELLED (1<<0) - -#ifdef ENABLE_THREADS -#define CAMEL_CANCEL_LOCK(cc) pthread_mutex_lock(&cc->lock) -#define CAMEL_CANCEL_UNLOCK(cc) pthread_mutex_unlock(&cc->lock) -#define CAMEL_ACTIVE_LOCK() pthread_mutex_lock(&cancel_active_lock) -#define CAMEL_ACTIVE_UNLOCK() pthread_mutex_unlock(&cancel_active_lock) -static pthread_mutex_t cancel_active_lock = PTHREAD_MUTEX_INITIALIZER; -#else -#define CAMEL_CANCEL_LOCK(cc) -#define CAMEL_CANCEL_UNLOCK(cc) -#define CAMEL_ACTIVE_LOCK() -#define CAMEL_ACTIVE_UNLOCK() -#endif - -static GHashTable *cancel_active; - -typedef struct _CamelCancelMsg { - EMsg msg; -} CamelCancelMsg ; - -/* creates a new cancel handle */ -CamelCancel *camel_cancel_new(void) -{ - CamelCancel *cc; - - cc = g_malloc0(sizeof(*cc)); - - cc->flags = 0; - cc->blocked = 0; - cc->refcount = 1; -#ifdef ENABLE_THREADS - cc->id = ~0; - cc->cancel_port = e_msgport_new(); - cc->cancel_fd = e_msgport_fd(cc->cancel_port); - pthread_mutex_init(&cc->lock, NULL); -#endif - - return cc; -} - -void camel_cancel_reset(CamelCancel *cc) -{ -#ifdef ENABLE_THREADS - CamelCancelMsg *msg; - - while ((msg = (CamelCancelMsg *)e_msgport_get(cc->cancel_port))) - g_free(msg); -#endif - - cc->flags = 0; - cc->blocked = 0; -} - -void camel_cancel_ref(CamelCancel *cc) -{ - CAMEL_CANCEL_LOCK(cc); - cc->refcount++; - CAMEL_CANCEL_UNLOCK(cc); -} - -void camel_cancel_unref(CamelCancel *cc) -{ -#ifdef ENABLE_THREADS - CamelCancelMsg *msg; - - if (cc->refcount == 1) { - while ((msg = (CamelCancelMsg *)e_msgport_get(cc->cancel_port))) - g_free(msg); - - e_msgport_destroy(cc->cancel_port); -#endif - g_free(cc); - } else { - CAMEL_CANCEL_LOCK(cc); - cc->refcount--; - CAMEL_CANCEL_UNLOCK(cc); - } -} - -/* block cancellation */ -void camel_cancel_block(CamelCancel *cc) -{ - CAMEL_CANCEL_LOCK(cc); - - cc->blocked++; - - CAMEL_CANCEL_UNLOCK(cc); -} - -/* unblock cancellation */ -void camel_cancel_unblock(CamelCancel *cc) -{ - CAMEL_CANCEL_LOCK(cc); - - cc->blocked--; - - CAMEL_CANCEL_UNLOCK(cc); -} - -static void -cancel_thread(void *key, CamelCancel *cc, void *data) -{ - if (cc) - camel_cancel_cancel(cc); -} - -/* cancels an operation */ -void camel_cancel_cancel(CamelCancel *cc) -{ - CamelCancelMsg *msg; - - if (cc == NULL) { - if (cancel_active) { - CAMEL_ACTIVE_LOCK(); - g_hash_table_foreach(cancel_active, (GHFunc)cancel_thread, NULL); - CAMEL_ACTIVE_UNLOCK(); - } - } else if ((cc->flags & CAMEL_CANCEL_CANCELLED) == 0) { - d(printf("cancelling thread %d\n", cc->id)); - - CAMEL_CANCEL_LOCK(cc); - msg = g_malloc0(sizeof(*msg)); - e_msgport_put(cc->cancel_port, (EMsg *)msg); - cc->flags |= CAMEL_CANCEL_CANCELLED; - CAMEL_CANCEL_UNLOCK(cc); - } -} - -/* register a thread for cancellation */ -void camel_cancel_register(CamelCancel *cc) -{ - pthread_t id = pthread_self(); - - CAMEL_ACTIVE_LOCK(); - - if (cancel_active == NULL) - cancel_active = g_hash_table_new(NULL, NULL); - - if (cc == NULL) { - cc = g_hash_table_lookup(cancel_active, (void *)id); - if (cc == NULL) { - cc = camel_cancel_new(); - } - } - - cc->id = id; - g_hash_table_insert(cancel_active, (void *)id, cc); - - d(printf("registering thread %d for cancellation\n", id)); - - CAMEL_ACTIVE_UNLOCK(); - - camel_cancel_ref(cc); -} - -/* remove a thread from being able to be cancelled */ -void camel_cancel_unregister(CamelCancel *cc) -{ - CAMEL_ACTIVE_LOCK(); - - if (cancel_active == NULL) - cancel_active = g_hash_table_new(NULL, NULL); - - if (cc == NULL) { - cc = g_hash_table_lookup(cancel_active, (void *)pthread_self()); - if (cc == NULL) { - g_warning("Trying to unregister a thread that was never registered for cancellation"); - } - } - - if (cc) - g_hash_table_remove(cancel_active, (void *)cc->id); - - CAMEL_ACTIVE_UNLOCK(); - - d({if (cc) printf("unregistering thread %d for cancellation\n", cc->id)}); - - if (cc) - camel_cancel_unref(cc); -} - -/* test for cancellation */ -gboolean camel_cancel_check(CamelCancel *cc) -{ - CamelCancelMsg *msg; - - d(printf("checking for cancel in thread %d\n", pthread_self())); - - if (cc == NULL) { - if (cancel_active) { - CAMEL_ACTIVE_LOCK(); - cc = g_hash_table_lookup(cancel_active, (void *)pthread_self()); - CAMEL_ACTIVE_UNLOCK(); - } - if (cc == NULL) - return FALSE; - } - - if (cc->blocked > 0) { - d(printf("ahah! cancellation is blocked\n")); - return FALSE; - } - - if (cc->flags & CAMEL_CANCEL_CANCELLED) { - d(printf("previously cancelled\n")); - return TRUE; - } - - msg = (CamelCancelMsg *)e_msgport_get(cc->cancel_port); - if (msg) { - d(printf("Got cancellation message\n")); - CAMEL_CANCEL_LOCK(cc); - cc->flags |= CAMEL_CANCEL_CANCELLED; - CAMEL_CANCEL_UNLOCK(cc); - return TRUE; - } - return FALSE; -} - -/* get the fd for cancellation waiting */ -int camel_cancel_fd(CamelCancel *cc) -{ - if (cc == NULL) { - if (cancel_active) { - CAMEL_ACTIVE_LOCK(); - cc = g_hash_table_lookup(cancel_active, (void *)pthread_self()); - CAMEL_ACTIVE_UNLOCK(); - } - if (cc == NULL) - return -1; - } - if (cc->blocked) - return -1; - - return cc->cancel_fd; -} - diff --git a/camel/camel-session.h b/camel/camel-session.h index d740830d7e..c530c6ffe6 100644 --- a/camel/camel-session.h +++ b/camel/camel-session.h @@ -140,24 +140,6 @@ guint camel_session_register_timeout (CamelSession *session, gboolean camel_session_remove_timeout (CamelSession *session, guint handle); - -/* cancellation helper stuff, not yet finalised */ -typedef struct _CamelCancel CamelCancel; -/* main thread functions */ -CamelCancel *camel_cancel_new(void); -void camel_cancel_ref(CamelCancel *cc); -void camel_cancel_unref(CamelCancel *cc); -void camel_cancel_reset(CamelCancel *cc); -void camel_cancel_cancel(CamelCancel *cc); -/* subthread functions */ -void camel_cancel_register(CamelCancel *cc); -void camel_cancel_unregister(CamelCancel *cc); -/* called internally by camel, for the current thread */ -void camel_cancel_block(CamelCancel *cc); -void camel_cancel_unblock(CamelCancel *cc); -gboolean camel_cancel_check(CamelCancel *cc); -int camel_cancel_fd(CamelCancel *cc); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/camel/camel-stream-fs.c b/camel/camel-stream-fs.c index 1e2a6bdf55..d19000b37e 100644 --- a/camel/camel-stream-fs.c +++ b/camel/camel-stream-fs.c @@ -33,7 +33,7 @@ #include #include -#include "camel-session.h" /* for camel_cancel_* */ +#include "camel-operation.h" static CamelSeekableStreamClass *parent_class = NULL; @@ -214,7 +214,7 @@ stream_read (CamelStream *stream, char *buffer, size_t n) ssize_t nread; int cancel_fd; - if (camel_cancel_check(NULL)) { + if (camel_operation_cancel_check(NULL)) { errno = EINTR; return -1; } @@ -222,7 +222,7 @@ stream_read (CamelStream *stream, char *buffer, size_t n) if (seekable->bound_end != CAMEL_STREAM_UNBOUND) n = MIN (seekable->bound_end - seekable->position, n); - cancel_fd = camel_cancel_fd(NULL); + cancel_fd = camel_operation_cancel_fd(NULL); if (cancel_fd == -1) { do { nread = read (stream_fs->fd, buffer, n); @@ -263,7 +263,7 @@ stream_write (CamelStream *stream, const char *buffer, size_t n) ssize_t v, written = 0; int cancel_fd; - if (camel_cancel_check(NULL)) { + if (camel_operation_cancel_check(NULL)) { errno = EINTR; return -1; } @@ -271,7 +271,7 @@ stream_write (CamelStream *stream, const char *buffer, size_t n) if (seekable->bound_end != CAMEL_STREAM_UNBOUND) n = MIN (seekable->bound_end - seekable->position, n); - cancel_fd = camel_cancel_fd(NULL); + cancel_fd = camel_operation_cancel_fd(NULL); if (cancel_fd == -1) { do { v = write (stream_fs->fd, buffer+written, n-written); diff --git a/camel/providers/local/camel-mbox-summary.c b/camel/providers/local/camel-mbox-summary.c index ab42e10df7..b8219b4c78 100644 --- a/camel/providers/local/camel-mbox-summary.c +++ b/camel/providers/local/camel-mbox-summary.c @@ -21,7 +21,8 @@ */ #include "camel-mbox-summary.h" -#include +#include "camel/camel-mime-message.h" +#include "camel/camel-operation.h" #include #include @@ -230,18 +231,26 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) CamelMimeParser *mp; int fd; int ok = 0; + struct stat st; + off_t size = 0; /* FIXME: If there is a failure, it shouldn't clear the summary and restart, it should try and merge the summary info's. This is a bit tricky. */ + camel_operation_start(NULL, _("Summarising folder")); + fd = open(cls->folder_path, O_RDONLY); if (fd == -1) { printf("%s failed to open: %s", cls->folder_path, strerror(errno)); camel_exception_setv(ex, 1, _("Could not open folder: %s: summarising from position %ld: %s"), cls->folder_path, offset, strerror(errno)); + camel_operation_end(NULL); return -1; } + if (fstat(fd, &st) == 0) + size = st.st_size; + mp = camel_mime_parser_new(); camel_mime_parser_init_with_fd(mp, fd); camel_mime_parser_scan_from(mp, TRUE); @@ -262,12 +271,16 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) d(printf("mime parser state ran out? state is %d\n", camel_mime_parser_state(mp))); camel_object_unref(CAMEL_OBJECT(mp)); /* end of file - no content? no error either */ + camel_operation_end(NULL); return 0; } } while (camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM) { CamelMessageInfo *info; + int pc = (camel_mime_parser_tell(mp)+1) * 100/size; + + camel_operation_progress(NULL, pc); info = camel_folder_summary_add_from_parser(s, mp); if (info == NULL) { @@ -284,8 +297,6 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) /* update the file size/mtime in the summary */ if (ok != -1) { - struct stat st; - if (stat(cls->folder_path, &st) == 0) { camel_folder_summary_touch(s); mbs->folder_size = st.st_size; @@ -293,6 +304,8 @@ summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex) } } + camel_operation_end(NULL); + return ok; } @@ -477,11 +490,14 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan d(printf("performing full summary/sync\n")); + camel_operation_start(NULL, _("Synchronising folder")); + fd = open(cls->folder_path, O_RDONLY); if (fd == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not open folder to summarise: %s: %s"), cls->folder_path, strerror(errno)); + camel_operation_end(NULL); return -1; } @@ -502,6 +518,10 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan count = camel_folder_summary_count(s); for (i = 0; i < count; i++) { + int pc = (i+1)*100/count; + + camel_operation_progress(NULL, pc); + info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i); g_assert(info); @@ -630,7 +650,8 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan tmpname = NULL; camel_object_unref((CamelObject *)mp); - + camel_operation_end(NULL); + return 0; error: if (fd != -1) @@ -648,6 +669,8 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan if (info) camel_folder_summary_info_free(s, (CamelMessageInfo *)info); + camel_operation_end(NULL); + return -1; } @@ -668,11 +691,15 @@ mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderCha d(printf("Performing quick summary sync\n")); + camel_operation_start(NULL, _("Synchronising folder")); + fd = open(cls->folder_path, O_RDWR); if (fd == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not open folder to summarise: %s: %s"), cls->folder_path, strerror(errno)); + + camel_operation_end(NULL); return -1; } @@ -684,6 +711,9 @@ mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderCha count = camel_folder_summary_count(s); for (i = 0; i < count; i++) { int xevoffset; + int pc = (i+1)*100/count; + + camel_operation_progress(NULL, pc); info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i); @@ -769,6 +799,8 @@ mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderCha } camel_object_unref((CamelObject *)mp); + + camel_operation_end(NULL); return 0; error: @@ -779,6 +811,8 @@ mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderCha if (info) camel_folder_summary_info_free(s, (CamelMessageInfo *)info); + camel_operation_end(NULL); + return -1; } diff --git a/camel/providers/pop3/camel-pop3-folder.c b/camel/providers/pop3/camel-pop3-folder.c index cf55a95661..810faed846 100644 --- a/camel/providers/pop3/camel-pop3-folder.c +++ b/camel/providers/pop3/camel-pop3-folder.c @@ -29,6 +29,7 @@ #include "camel-stream-mem.h" #include "camel-stream-filter.h" #include "camel-mime-message.h" +#include "camel-operation.h" #include #include @@ -141,9 +142,13 @@ pop3_refresh_info (CamelFolder *folder, CamelException *ex) CamelPop3Folder *pop3_folder = (CamelPop3Folder *) folder; CamelPop3Store *pop3_store = CAMEL_POP3_STORE (folder->parent_store); + camel_operation_start(NULL, _("Retrieving POP summary")); + status = camel_pop3_command (pop3_store, &data, ex, "STAT"); - if (status != CAMEL_POP3_OK) + if (status != CAMEL_POP3_OK) { + camel_operation_end(NULL); return; + } count = atoi (data); g_free (data); @@ -155,6 +160,7 @@ pop3_refresh_info (CamelFolder *folder, CamelException *ex) pop3_store->supports_uidl = FALSE; break; case CAMEL_POP3_FAIL: + camel_operation_end(NULL); return; } } @@ -167,8 +173,10 @@ pop3_refresh_info (CamelFolder *folder, CamelException *ex) for (i = 0; i < count; i++) uids->pdata[i] = g_strdup_printf ("%d", i + 1); + camel_operation_end(NULL); } else { - data = camel_pop3_command_get_additional_data (pop3_store, ex); + data = camel_pop3_command_get_additional_data (pop3_store, 0, ex); + camel_operation_end(NULL); if (camel_exception_is_set (ex)) return; @@ -263,7 +271,7 @@ uid_to_number (CamelPop3Folder *pop3_folder, const char *uid) static CamelMimeMessage * pop3_get_message (CamelFolder *folder, const char *uid, CamelException *ex) { - int status, num; + int status, num, total; char *result, *body; CamelStream *msgstream; CamelMimeMessage *msg; @@ -275,19 +283,28 @@ pop3_get_message (CamelFolder *folder, const char *uid, CamelException *ex) return NULL; } + camel_operation_start(NULL, _("Retrieving POP message %d"), num); + status = camel_pop3_command (CAMEL_POP3_STORE (folder->parent_store), &result, ex, "RETR %d", num); - if (status != CAMEL_POP3_OK) + if (status != CAMEL_POP3_OK) { + camel_operation_end(NULL); return NULL; - g_free (result); + } + + /* this should be "nnn octets" ? */ + if (sscanf(result, "%d", &total) != 1) + total = 0; - body = camel_pop3_command_get_additional_data (CAMEL_POP3_STORE (folder->parent_store), ex); + g_free (result); + body = camel_pop3_command_get_additional_data (CAMEL_POP3_STORE (folder->parent_store), total, ex); if (!body) { CamelService *service = CAMEL_SERVICE (folder->parent_store); camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, _("Could not retrieve message from POP " "server %s: %s"), service->url->host, camel_exception_get_description (ex)); + camel_operation_end(NULL); return NULL; } @@ -300,6 +317,8 @@ pop3_get_message (CamelFolder *folder, const char *uid, CamelException *ex) camel_object_unref (CAMEL_OBJECT (msgstream)); + camel_operation_end(NULL); + return msg; } diff --git a/camel/providers/pop3/camel-pop3-store.c b/camel/providers/pop3/camel-pop3-store.c index aefabebfa4..f0338a8584 100644 --- a/camel/providers/pop3/camel-pop3-store.c +++ b/camel/providers/pop3/camel-pop3-store.c @@ -35,6 +35,8 @@ #include #include +#include "camel-operation.h" + #ifdef HAVE_KRB4 /* Specified nowhere */ #define KPOP_PORT 1109 @@ -250,7 +252,7 @@ connect_to_server (CamelService *service, /*gboolean real, */CamelException *ex) char *p; int len; - buf = camel_pop3_command_get_additional_data (store, ex); + buf = camel_pop3_command_get_additional_data (store, 0, ex); if (camel_exception_is_set (ex)) return FALSE; @@ -632,6 +634,7 @@ pop3_get_response (CamelPop3Store *store, char **ret, CamelException *ex) * camel_pop3_command_get_additional_data: get "additional data" from * a POP3 command. * @store: the POP3 store + * @total: Total bytes expected (for progress reporting), use 0 for 'unknown'. * * This command gets the additional data returned by "multi-line" POP * commands, such as LIST, RETR, TOP, and UIDL. This command _must_ @@ -643,11 +646,12 @@ pop3_get_response (CamelPop3Store *store, char **ret, CamelException *ex) * Return value: the data, which the caller must free. **/ char * -camel_pop3_command_get_additional_data (CamelPop3Store *store, CamelException *ex) +camel_pop3_command_get_additional_data (CamelPop3Store *store, int total, CamelException *ex) { GPtrArray *data; char *buf, *p; int i, len = 0, status = CAMEL_POP3_OK; + int pc = 0; data = g_ptr_array_new (); while (1) { @@ -661,6 +665,13 @@ camel_pop3_command_get_additional_data (CamelPop3Store *store, CamelException *e g_ptr_array_add (data, buf); len += strlen (buf) + 1; + + if (total) { + pc = (len+1) * 100 / total; + camel_operation_progress(NULL, pc); + } else { + camel_operation_progress_count(NULL, len); + } } if (buf) diff --git a/camel/providers/pop3/camel-pop3-store.h b/camel/providers/pop3/camel-pop3-store.h index 65bf1cbdba..078a317a14 100644 --- a/camel/providers/pop3/camel-pop3-store.h +++ b/camel/providers/pop3/camel-pop3-store.h @@ -65,8 +65,7 @@ void camel_pop3_store_expunge (CamelPop3Store *store, CamelException *ex); /* support functions */ enum { CAMEL_POP3_OK, CAMEL_POP3_ERR, CAMEL_POP3_FAIL }; int camel_pop3_command (CamelPop3Store *store, char **ret, CamelException *ex, char *fmt, ...); -char *camel_pop3_command_get_additional_data (CamelPop3Store *store, - CamelException *ex); +char *camel_pop3_command_get_additional_data (CamelPop3Store *store, int total, CamelException *ex); /* Standard Camel function */ CamelType camel_pop3_store_get_type (void); -- cgit v1.2.3