]> git.0d.be Git - empathy.git/commitdiff
Allow cancellation of avatar load operations
authorPhilip Withnall <philip.withnall@collabora.co.uk>
Wed, 18 Aug 2010 17:13:02 +0000 (18:13 +0100)
committerPhilip Withnall <philip.withnall@collabora.co.uk>
Wed, 18 Aug 2010 17:55:09 +0000 (18:55 +0100)
Automatically cancel all pending avatar load operations when disposing of
an EmpathyIndividualStore. This prevents crashes when linking individuals in
the case that the EmpathyIndividualStore in the linking dialogue gets
notified of the new linked individual just before it's destroyed.

libempathy-gtk/empathy-individual-store.c
libempathy-gtk/empathy-ui-utils.c
libempathy-gtk/empathy-ui-utils.h

index 161787299c84248851b865e1cbaf19e6cd93addb..26b56954e4cfc6242e3abc3437668c0036444870 100644 (file)
@@ -71,6 +71,8 @@ typedef struct
   guint setup_idle_id;
   gboolean dispose_has_run;
   GHashTable *status_icons;
+  /* List of owned GCancellables for each pending avatar load operation */
+  GList *avatar_cancellables;
 } EmpathyIndividualStorePriv;
 
 typedef struct
@@ -552,13 +554,16 @@ individual_store_contact_active_cb (ShowActiveData *data)
   return FALSE;
 }
 
+typedef struct {
+  EmpathyIndividualStore *store; /* weak */
+  GCancellable *cancellable; /* owned */
+} LoadAvatarData;
+
 static void
-individual_avatar_pixbuf_received_cb (GObject *object,
+individual_avatar_pixbuf_received_cb (FolksIndividual *individual,
     GAsyncResult *result,
-    gpointer user_data)
+    LoadAvatarData *data)
 {
-  FolksIndividual *individual = FOLKS_INDIVIDUAL (object);
-  EmpathyIndividualStore *self = user_data;
   GError *error = NULL;
   GdkPixbuf *pixbuf;
 
@@ -572,18 +577,32 @@ individual_avatar_pixbuf_received_cb (GObject *object,
           error->message);
       g_clear_error (&error);
     }
-  else
+  else if (data->store != NULL)
     {
       GList *iters, *l;
 
-      iters = individual_store_find_contact (self, individual);
+      iters = individual_store_find_contact (data->store, individual);
       for (l = iters; l; l = l->next)
         {
-          gtk_tree_store_set (GTK_TREE_STORE (self), l->data,
+          gtk_tree_store_set (GTK_TREE_STORE (data->store), l->data,
               EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR, pixbuf,
               -1);
         }
     }
+
+  /* Free things */
+  if (data->store != NULL)
+    {
+      EmpathyIndividualStorePriv *priv = GET_PRIV (data->store);
+
+      g_object_remove_weak_pointer (G_OBJECT (data->store),
+          (gpointer *) &data->store);
+      priv->avatar_cancellables = g_list_remove (priv->avatar_cancellables,
+          data->cancellable);
+    }
+
+  g_object_unref (data->cancellable);
+  g_slice_free (LoadAvatarData, data);
 }
 
 static void
@@ -604,6 +623,7 @@ individual_store_contact_update (EmpathyIndividualStore *self,
   gboolean do_set_refresh = FALSE;
   gboolean show_avatar = FALSE;
   GdkPixbuf *pixbuf_status;
+  LoadAvatarData *load_avatar_data;
 
   priv = GET_PRIV (self);
 
@@ -678,8 +698,19 @@ individual_store_contact_update (EmpathyIndividualStore *self,
       show_avatar = TRUE;
     }
 
-  empathy_pixbuf_avatar_from_individual_scaled_async (individual,
-      individual_avatar_pixbuf_received_cb, 32, 32, self);
+  /* Load the avatar asynchronously */
+  load_avatar_data = g_slice_new (LoadAvatarData);
+  load_avatar_data->store = self;
+  g_object_add_weak_pointer (G_OBJECT (self),
+      (gpointer *) &load_avatar_data->store);
+  load_avatar_data->cancellable = g_cancellable_new ();
+
+  priv->avatar_cancellables = g_list_prepend (priv->avatar_cancellables,
+      load_avatar_data->cancellable);
+  empathy_pixbuf_avatar_from_individual_scaled_async (individual, 32, 32,
+      load_avatar_data->cancellable,
+      (GAsyncReadyCallback) individual_avatar_pixbuf_received_cb,
+      load_avatar_data);
 
   pixbuf_status =
       empathy_individual_store_get_individual_status_icon (self, individual);
@@ -957,6 +988,14 @@ individual_store_dispose (GObject *object)
     return;
   priv->dispose_has_run = TRUE;
 
+  /* Cancel any pending avatar load operations */
+  for (l = priv->avatar_cancellables; l != NULL; l = l->next)
+    {
+      /* The cancellables are freed in individual_avatar_pixbuf_received_cb() */
+      g_cancellable_cancel (G_CANCELLABLE (l->data));
+    }
+  g_list_free (priv->avatar_cancellables);
+
   contacts = empathy_individual_manager_get_members (priv->manager);
   for (l = contacts; l; l = l->next)
     {
index 46a26d8f7bebc5a487c5813c9dfd249209dc6543..0a2d4f4f42064da35065ee1a851e958d6006c416 100644 (file)
@@ -564,7 +564,7 @@ avatar_file_load_contents_cb (GObject      *object,
 {
        GFile *file = G_FILE (object);
        PixbufAvatarFromIndividualClosure *closure = user_data;
-       char *data;
+       char *data = NULL;
        gsize data_size;
        struct SizeData size_data;
        GError *error = NULL;
@@ -617,9 +617,10 @@ out:
 void
 empathy_pixbuf_avatar_from_individual_scaled_async (
                FolksIndividual     *individual,
-               GAsyncReadyCallback  callback,
                gint                 width,
                gint                 height,
+               GCancellable        *cancellable,
+               GAsyncReadyCallback  callback,
                gpointer             user_data)
 {
        GFile *avatar_file;
@@ -639,7 +640,7 @@ empathy_pixbuf_avatar_from_individual_scaled_async (
        if (closure == NULL)
                goto out;
 
-       g_file_load_contents_async (avatar_file, NULL,
+       g_file_load_contents_async (avatar_file, cancellable,
                        avatar_file_load_contents_cb, closure);
 
        g_object_unref (result);
index 9c3ec6517c4d5322e43f90aa804f55f979018b0a..0b76d09a359495d869c8706f1974ff5464f3ff96 100644 (file)
@@ -77,9 +77,10 @@ GdkPixbuf *   empathy_pixbuf_from_data_and_mime         (gchar            *data,
                                                         gsize             data_size,
                                                         gchar           **mime_type);
 void empathy_pixbuf_avatar_from_individual_scaled_async (FolksIndividual     *individual,
-                                                        GAsyncReadyCallback  callback,
                                                         gint                 width,
                                                         gint                 height,
+                                                        GCancellable        *cancellable,
+                                                        GAsyncReadyCallback  callback,
                                                         gpointer             user_data);
 GdkPixbuf * empathy_pixbuf_avatar_from_individual_scaled_finish (
                                                         FolksIndividual  *individual,