From 7f8d540689172bfd697e6d22775a94cc73259a3f Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Wed, 11 Jan 2012 15:30:51 +0100 Subject: channel-contacts.c: Fix crash when preparing TpCallChannel If if a new contact fetch is queued from the callback of last contact fetch, it leads to a crash. This is because when queueing the new item it is processed right away since it is the only item in queue, then when it returns from g_simple_async_result_complete() it calls process_contacts_queue() again so the item is prepared twice, and then freed twice. Fix this by keeping currently being processes item outside the queue. --- telepathy-glib/channel-contacts.c | 22 +++++++++++----------- telepathy-glib/channel-internal.h | 4 ++++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/telepathy-glib/channel-contacts.c b/telepathy-glib/channel-contacts.c index 3e5fdd6c9..efefe38b7 100644 --- a/telepathy-glib/channel-contacts.c +++ b/telepathy-glib/channel-contacts.c @@ -232,13 +232,13 @@ _tp_channel_contacts_group_init (TpChannel *self, } } -typedef struct +struct _ContactsQueueItem { GSimpleAsyncResult *result; GPtrArray *contacts; GPtrArray *ids; GArray *handles; -} ContactsQueueItem; +}; static ContactsQueueItem * contacts_queue_item_new (GPtrArray *contacts, @@ -273,9 +273,7 @@ static void contacts_queue_head_ready (TpChannel *self, const GError *error) { - ContactsQueueItem *item; - - item = g_queue_pop_head (self->priv->contacts_queue); + ContactsQueueItem *item = self->priv->current_item; if (error != NULL) { @@ -284,6 +282,7 @@ contacts_queue_head_ready (TpChannel *self, } g_simple_async_result_complete (item->result); + self->priv->current_item = NULL; process_contacts_queue (self); contacts_queue_item_free (item); @@ -366,8 +365,7 @@ process_contacts_queue (TpChannel *self) GArray *features; const GError *error = NULL; - item = g_queue_peek_head (self->priv->contacts_queue); - if (item == NULL) + if (self->priv->current_item != NULL) return; /* self can't die while there are queued items because item->result keeps a @@ -387,6 +385,11 @@ process_contacts_queue (TpChannel *self) return; } + item = g_queue_pop_head (self->priv->contacts_queue); + if (item == NULL) + return; + self->priv->current_item = item; + features = tp_simple_client_factory_dup_contact_features ( tp_proxy_get_factory (self->priv->connection), self->priv->connection); @@ -450,10 +453,7 @@ contacts_queue_item (TpChannel *self, g_simple_async_result_set_op_res_gpointer (item->result, item, NULL); g_queue_push_tail (self->priv->contacts_queue, item); - - /* If this is the only item in the queue, we can process it right away */ - if (self->priv->contacts_queue->length == 1) - process_contacts_queue (self); + process_contacts_queue (self); } void diff --git a/telepathy-glib/channel-internal.h b/telepathy-glib/channel-internal.h index 312dfe633..0f13666d2 100644 --- a/telepathy-glib/channel-internal.h +++ b/telepathy-glib/channel-internal.h @@ -35,6 +35,8 @@ typedef struct { gchar *message; } LocalPendingInfo; +typedef struct _ContactsQueueItem ContactsQueueItem; + struct _TpChannelPrivate { gulong conn_invalidated_id; @@ -87,6 +89,8 @@ struct _TpChannelPrivate { gboolean cm_too_old_for_contacts; GQueue *contacts_queue; + /* Item currently being prepared, not part of contacts_queue anymore */ + ContactsQueueItem *current_item; /* NULL, or TpHandle => TpChannelChatState; * if non-NULL, we're watching for ChatStateChanged */ -- cgit v1.2.1