diff options
Diffstat (limited to 'camel/camel-operation.c')
-rw-r--r-- | camel/camel-operation.c | 676 |
1 files changed, 0 insertions, 676 deletions
diff --git a/camel/camel-operation.c b/camel/camel-operation.c deleted file mode 100644 index 6c490df73f..0000000000 --- a/camel/camel-operation.c +++ /dev/null @@ -1,676 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#ifdef ENABLE_THREADS -#include <pthread.h> -#endif - -#include <sys/time.h> -#include <unistd.h> - -#include <glib.h> -#include "camel-operation.h" -#include "e-util/e-msgport.h" - -#define d(x) - -/* ********************************************************************** */ - -struct _status_stack { - guint32 flags; - char *msg; - int pc; /* last pc reported */ - unsigned int stamp; /* last stamp reported */ -}; - -struct _CamelOperation { - pthread_t id; /* id of running thread */ - guint32 flags; /* cancelled ? */ - int blocked; /* cancellation blocked depth */ - int refcount; - - CamelOperationStatusFunc status; - void *status_data; - unsigned int status_update; - - /* stack of status messages (struct _status_stack *) */ - GSList *status_stack; - struct _status_stack *lastreport; - -#ifdef ENABLE_THREADS - EMsgPort *cancel_port; - int cancel_fd; -#endif -}; - -#define CAMEL_OPERATION_CANCELLED (1<<0) -#define CAMEL_OPERATION_TRANSIENT (1<<1) - -/* Delay before a transient operation has any effect on the status */ -#define CAMEL_OPERATION_TRANSIENT_DELAY (5) - -#ifdef ENABLE_THREADS -#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_ACTIVE_LOCK() -#define CAMEL_ACTIVE_UNLOCK() -#endif - -static unsigned int stamp (void); - -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 = -1; -#endif - - return cc; -} - -/* return the registered operation, or NULL if none registered */ -/* need to unref when done with it */ -CamelOperation *camel_operation_registered(void) -{ - CamelOperation *cc = NULL; - - CAMEL_ACTIVE_LOCK(); - if (operation_active != NULL - && (cc = g_hash_table_lookup(operation_active, (void *)pthread_self()))) { - g_assert(cc->refcount > 0); - cc->refcount++; - } - CAMEL_ACTIVE_UNLOCK(); - - 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) -{ - g_assert(cc->refcount > 0); - - CAMEL_ACTIVE_LOCK(); - cc->refcount++; - CAMEL_ACTIVE_UNLOCK(); -} - -/** - * camel_operation_unref: - * @cc: - * - * Unref and potentially free @cc. - **/ -void camel_operation_unref(CamelOperation *cc) -{ - GSList *n; - - g_assert(cc->refcount > 0); - - CAMEL_ACTIVE_LOCK(); - if (cc->refcount == 1) { -#ifdef ENABLE_THREADS - CamelOperationMsg *msg; - - while ((msg = (CamelOperationMsg *)e_msgport_get(cc->cancel_port))) - g_free(msg); - - e_msgport_destroy(cc->cancel_port); -#endif - - if (cc->id != (~0)) { - g_warning("Unreffing operation status which was still registered: %p\n", cc); - g_hash_table_remove(operation_active, (void *)cc->id); - } - - 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 { - cc->refcount--; - } - CAMEL_ACTIVE_UNLOCK(); -} - -/** - * 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()); - - if (cc) - cc->blocked++; - CAMEL_ACTIVE_UNLOCK(); -} - -/** - * 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()); - - if (cc) - cc->blocked--; - CAMEL_ACTIVE_UNLOCK(); -} - -static void -cancel_thread(void *key, CamelOperation *cc, void *data) -{ - CamelOperationMsg *msg; - - if (cc) { - d(printf("cancelling thread %d\n", cc->id)); - - cc->flags |= CAMEL_OPERATION_CANCELLED; - msg = g_malloc0(sizeof(*msg)); - e_msgport_put(cc->cancel_port, (EMsg *)msg); - } -} - -/** - * 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; - - CAMEL_ACTIVE_LOCK(); - - if (cc == NULL) { - if (operation_active) { - g_hash_table_foreach(operation_active, (GHFunc)cancel_thread, NULL); - } - } else if ((cc->flags & CAMEL_OPERATION_CANCELLED) == 0) { - d(printf("cancelling thread %d\n", cc->id)); - - cc->flags |= CAMEL_OPERATION_CANCELLED; - msg = g_malloc0(sizeof(*msg)); - e_msgport_put(cc->cancel_port, (EMsg *)msg); - } - - CAMEL_ACTIVE_UNLOCK(); -} - -/** - * 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); - } - } - - if (cc->id == (~0)) { - cc->id = id; - g_hash_table_insert(operation_active, (void *)id, cc); - } else { - g_warning("Re-registering thread %lu for cancellation as thread %lu", cc->id, id); - } - - d(printf("registering thread %ld for cancellation\n", id)); - - CAMEL_ACTIVE_UNLOCK(); -} - -/** - * 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) { - if (cc->id != (~0)) { - g_hash_table_remove(operation_active, (void *)cc->id); - cc->id = ~0; - } else { - g_warning("Unregistering an operation that was already unregistered"); - } - } - - CAMEL_ACTIVE_UNLOCK(); - - d({if (cc) printf("unregistering thread %d for cancellation\n", cc->id);}); -} - -/** - * 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; - int cancelled; - - d(printf("checking for cancel in thread %d\n", pthread_self())); - - CAMEL_ACTIVE_LOCK(); - - if (cc == NULL && operation_active) - cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); - - if (cc == NULL || cc->blocked > 0) { - d(printf("ahah! cancellation is blocked\n")); - cancelled = FALSE; - } else if (cc->flags & CAMEL_OPERATION_CANCELLED) { - d(printf("previously cancelled\n")); - cancelled = TRUE; - } else if ((msg = (CamelOperationMsg *)e_msgport_get(cc->cancel_port))) { - d(printf("Got cancellation message\n")); - g_free(msg); - cc->flags |= CAMEL_OPERATION_CANCELLED; - cancelled = TRUE; - } else - cancelled = FALSE; - - CAMEL_ACTIVE_UNLOCK(); - - return cancelled; -} - -/** - * 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) -{ - CAMEL_ACTIVE_LOCK(); - - if (cc == NULL && operation_active) { - cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); - } - - if (cc == NULL - || cc->blocked) { - CAMEL_ACTIVE_UNLOCK(); - return -1; - } - - if (cc->cancel_fd == -1) - cc->cancel_fd = e_msgport_fd(cc->cancel_port); - - CAMEL_ACTIVE_UNLOCK(); - - 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; - struct _status_stack *s; - - if (operation_active == NULL) - return; - - CAMEL_ACTIVE_LOCK(); - - if (cc == NULL) - cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); - - if (cc == NULL || cc->status == NULL) { - CAMEL_ACTIVE_UNLOCK(); - return; - } - - va_start(ap, what); - msg = g_strdup_vprintf(what, ap); - va_end(ap); - cc->status_update = 0; - s = g_malloc0(sizeof(*s)); - s->msg = msg; - s->flags = 0; - cc->lastreport = s; - cc->status_stack = g_slist_prepend(cc->status_stack, s); - - CAMEL_ACTIVE_UNLOCK(); - - cc->status(cc, msg, CAMEL_OPERATION_START, cc->status_data); - - d(printf("start '%s'\n", msg, pc)); -} - -/** - * camel_operation_start_transient: - * @cc: - * @what: - * @: - * - * Start a transient event. We only update this to the display if it - * takes very long to process, and if we do, we then go back to the - * previous state when finished. - **/ -void camel_operation_start_transient(CamelOperation *cc, char *what, ...) -{ - va_list ap; - char *msg; - struct _status_stack *s; - - if (operation_active == NULL) - return; - - CAMEL_ACTIVE_LOCK(); - - if (cc == NULL) - cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); - - if (cc == NULL || cc->status == NULL) { - CAMEL_ACTIVE_UNLOCK(); - return; - } - - va_start(ap, what); - msg = g_strdup_vprintf(what, ap); - va_end(ap); - cc->status_update = 0; - s = g_malloc0(sizeof(*s)); - s->msg = msg; - s->flags = CAMEL_OPERATION_TRANSIENT; - s->stamp = stamp(); - cc->status_stack = g_slist_prepend(cc->status_stack, s); - d(printf("start '%s'\n", msg, pc)); - - CAMEL_ACTIVE_UNLOCK(); - - /* we dont report it yet */ - /*cc->status(cc, msg, CAMEL_OPERATION_START, cc->status_data);*/ -} - -static unsigned int stamp(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - /* update 4 times/second */ - return (tv.tv_sec * 4) + tv.tv_usec / (1000000/4); -} - -/** - * 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) -{ - unsigned int now; - struct _status_stack *s; - char *msg = NULL; - - if (operation_active == NULL) - return; - - CAMEL_ACTIVE_LOCK(); - - if (cc == NULL) - cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); - - if (cc == NULL || cc->status == NULL || cc->status_stack == NULL) { - CAMEL_ACTIVE_UNLOCK(); - return; - } - - s = cc->status_stack->data; - s->pc = pc; - - /* Transient messages dont start updating till 4 seconds after - they started, then they update every second */ - now = stamp(); - if (cc->status_update == now) { - cc = NULL; - } else if (s->flags & CAMEL_OPERATION_TRANSIENT) { - if (s->stamp + CAMEL_OPERATION_TRANSIENT_DELAY > now) { - cc = NULL; - } else { - cc->status_update = now; - cc->lastreport = s; - msg = g_strdup(s->msg); - } - } else { - s->stamp = cc->status_update = now; - cc->lastreport = s; - msg = g_strdup(s->msg); - } - - CAMEL_ACTIVE_UNLOCK(); - - if (cc) { - cc->status(cc, msg, pc, cc->status_data); - g_free(msg); - } -} - -void camel_operation_progress_count(CamelOperation *cc, int sofar) -{ - camel_operation_progress(cc, sofar); -} - -/** - * 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) -{ - struct _status_stack *s, *p; - unsigned int now; - char *msg = NULL; - int pc = 0; - - if (operation_active == NULL) - return; - - CAMEL_ACTIVE_LOCK(); - - if (cc == NULL) - cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); - - if (cc == NULL || cc->status == NULL || cc->status_stack == NULL) { - CAMEL_ACTIVE_UNLOCK(); - return; - } - - /* so what we do here is this. If the operation that just - * ended was transient, see if we have any other transient - * messages that haven't been updated yet above us, otherwise, - * re-update as a non-transient at the last reported pc */ - now = stamp(); - s = cc->status_stack->data; - if (s->flags & CAMEL_OPERATION_TRANSIENT) { - if (cc->lastreport == s) { - GSList *l = cc->status_stack->next; - while (l) { - p = l->data; - if (p->flags & CAMEL_OPERATION_TRANSIENT) { - if (p->stamp + CAMEL_OPERATION_TRANSIENT_DELAY < now) { - msg = g_strdup(p->msg); - pc = p->pc; - cc->lastreport = p; - break; - } - } else { - msg = g_strdup(p->msg); - pc = p->pc; - cc->lastreport = p; - break; - } - l = l->next; - } - } - g_free(s->msg); - } else { - msg = s->msg; - pc = CAMEL_OPERATION_END; - cc->lastreport = s; - } - g_free(s); - cc->status_stack = g_slist_remove_link(cc->status_stack, cc->status_stack); - - CAMEL_ACTIVE_UNLOCK(); - - if (msg) { - cc->status(cc, msg, pc, cc->status_data); - g_free(msg); - } -} |