[evolution-data-server] Bug 631594 - Various races in CamelOperation
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug 631594 - Various races in CamelOperation
- Date: Sun, 10 Oct 2010 17:11:11 +0000 (UTC)
commit fba927b873c5dd5f8341faeec070bf51d8105123
Author: Matthew Barnes <mbarnes redhat com>
Date: Sun Oct 10 12:59:41 2010 -0400
Bug 631594 - Various races in CamelOperation
camel/camel-operation.c | 119 +++++++++++++++++++++++++++++------------------
1 files changed, 74 insertions(+), 45 deletions(-)
---
diff --git a/camel/camel-operation.c b/camel/camel-operation.c
index 88ee3e5..7850bab 100644
--- a/camel/camel-operation.c
+++ b/camel/camel-operation.c
@@ -41,6 +41,7 @@
typedef struct _StatusNode StatusNode;
struct _StatusNode {
+ volatile gint ref_count;
CamelOperation *operation;
guint source_id; /* for timeout or idle */
gchar *message;
@@ -72,14 +73,45 @@ static guint signals[LAST_SIGNAL];
G_DEFINE_TYPE (CamelOperation, camel_operation, G_TYPE_CANCELLABLE)
+static StatusNode *
+status_node_new (void)
+{
+ StatusNode *node;
+
+ node = g_slice_new0 (StatusNode);
+ node->ref_count = 1;
+
+ return node;
+}
+
+static StatusNode *
+status_node_ref (StatusNode *node)
+{
+ g_return_val_if_fail (node != NULL, NULL);
+ g_return_val_if_fail (node->ref_count > 0, node);
+
+ g_atomic_int_add (&node->ref_count, 1);
+
+ return node;
+}
+
static void
-status_node_free (StatusNode *node)
+status_node_unref (StatusNode *node)
{
- g_free (node->message);
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (node->ref_count > 0);
+
+ if (g_atomic_int_exchange_and_add (&node->ref_count, -1) > 1)
+ return;
+
+ if (node->operation != NULL)
+ g_object_unref (node->operation);
if (node->source_id > 0)
g_source_remove (node->source_id);
+ g_free (node->message);
+
g_slice_free (StatusNode, node);
}
@@ -87,40 +119,25 @@ static gboolean
operation_emit_status_cb (StatusNode *node)
{
StatusNode *head_node;
- gchar *message = NULL;
- gint percent = 0;
-
- /* Guard against reference counting errors. */
- g_return_val_if_fail (node != NULL, FALSE);
- g_return_val_if_fail (CAMEL_IS_OPERATION (node->operation), FALSE);
-
- /* Keep the operation alive until we emit the signal,
- * otherwise it might be finalized between unlocking
- * the mutex and emitting the signal. */
- g_object_ref (node->operation);
+ gboolean emit_status;
LOCK ();
node->source_id = 0;
+ /* Check if we've been preempted by another StatusNode,
+ * or if we've been cancelled and popped off the stack. */
head_node = g_queue_peek_head (&node->operation->priv->status_stack);
-
- if (node == head_node) {
- message = g_strdup (node->message);
- percent = node->percent;
- }
+ emit_status = (node == head_node);
UNLOCK ();
- if (message != NULL) {
+ if (emit_status)
g_signal_emit (
node->operation,
signals[STATUS], 0,
- message, percent);
- g_free (message);
- }
-
- g_object_unref (node->operation);
+ node->message,
+ node->percent);
return FALSE;
}
@@ -143,7 +160,6 @@ static void
operation_finalize (GObject *object)
{
CamelOperationPrivate *priv;
- StatusNode *node;
priv = CAMEL_OPERATION_GET_PRIVATE (object);
@@ -154,12 +170,10 @@ operation_finalize (GObject *object)
operation_flush_msgport (CAMEL_OPERATION (object));
camel_msgport_destroy (priv->cancel_port);
- while ((node = g_queue_pop_head (&priv->status_stack)) != NULL) {
- g_warning (
- "CamelOperation status stack non-empty: %s",
- node->message);
- status_node_free (node);
- }
+ /* Because each StatusNode holds a reference to its
+ * CamelOperation, the fact that we're being finalized
+ * implies the stack should be empty now. */
+ g_warn_if_fail (g_queue_is_empty (&priv->status_stack));
UNLOCK ();
@@ -400,17 +414,22 @@ camel_operation_push_message (GCancellable *cancellable,
va_start (ap, format);
- node = g_slice_new0 (StatusNode);
+ node = status_node_new ();
node->message = g_strdup_vprintf (format, ap);
- node->operation = operation; /* not referenced */
+ node->operation = g_object_ref (operation);
if (g_queue_is_empty (&operation->priv->status_stack))
- node->source_id = g_idle_add (
- (GSourceFunc) operation_emit_status_cb, node);
+ node->source_id = g_idle_add_full (
+ G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc) operation_emit_status_cb,
+ status_node_ref (node),
+ (GDestroyNotify) status_node_unref);
else
- node->source_id = g_timeout_add_seconds (
- TRANSIENT_DELAY, (GSourceFunc)
- operation_emit_status_cb, node);
+ node->source_id = g_timeout_add_seconds_full (
+ G_PRIORITY_DEFAULT, TRANSIENT_DELAY,
+ (GSourceFunc) operation_emit_status_cb,
+ status_node_ref (node),
+ (GDestroyNotify) status_node_unref);
g_queue_push_head (&operation->priv->status_stack, node);
@@ -448,14 +467,22 @@ camel_operation_pop_message (GCancellable *cancellable)
operation = CAMEL_OPERATION (cancellable);
node = g_queue_pop_head (&operation->priv->status_stack);
- if (node != NULL)
- status_node_free (node);
+ if (node != NULL) {
+ if (node->source_id > 0) {
+ g_source_remove (node->source_id);
+ node->source_id = 0;
+ }
+ status_node_unref (node);
+ }
node = g_queue_peek_head (&operation->priv->status_stack);
if (node != NULL && node->source_id == 0)
- node->source_id = g_idle_add (
- (GSourceFunc) operation_emit_status_cb, node);
+ node->source_id = g_idle_add_full (
+ G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc) operation_emit_status_cb,
+ status_node_ref (node),
+ (GDestroyNotify) status_node_unref);
UNLOCK ();
}
@@ -497,9 +524,11 @@ camel_operation_progress (GCancellable *cancellable,
/* Rate limit progress updates. */
if (node->source_id == 0)
- node->source_id = g_timeout_add (
- PROGRESS_DELAY, (GSourceFunc)
- operation_emit_status_cb, node);
+ node->source_id = g_timeout_add_full (
+ G_PRIORITY_DEFAULT, PROGRESS_DELAY,
+ (GSourceFunc) operation_emit_status_cb,
+ status_node_ref (node),
+ (GDestroyNotify) status_node_unref);
}
UNLOCK ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]