]> git.0d.be Git - empathy.git/commitdiff
Allow the dispatcher to be freed once a request is finished.
authorJonny Lamb <jonny.lamb@collabora.co.uk>
Fri, 30 Jan 2009 17:16:02 +0000 (17:16 +0000)
committerXavier Claessens <xclaesse@src.gnome.org>
Fri, 30 Jan 2009 17:16:02 +0000 (17:16 +0000)
Previously, if there was no ref to the dispatcher, it would be freed
before the request could be satisfied. By keeping a ref in the
DispatcherRequestData, it is freed at the right time. This also
disconnects the signal handler from all channels and connections
when freeing the dispatcher as the "invalidated" signal callback can be
called after the dispatcher has been freed.

Signed-off-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
svn path=/trunk/; revision=2306

libempathy/empathy-dispatcher.c

index e8f7b8c7e9f1d2fc947e4a72f586aa9d5c3b916a..a8e0b18440c70922804a56f734ec3b7c8be3efac 100644 (file)
@@ -57,6 +57,9 @@ typedef struct
   GHashTable *accounts;
   gpointer token;
   GSList *tubes;
+
+  /* channels which the dispatcher is listening "invalidated" */
+  GList *channels;
 } EmpathyDispatcherPriv;
 
 G_DEFINE_TYPE (EmpathyDispatcher, empathy_dispatcher, G_TYPE_OBJECT);
@@ -142,7 +145,7 @@ new_dispatcher_request_data (EmpathyDispatcher *dispatcher,
 {
   DispatcherRequestData *result = g_slice_new0 (DispatcherRequestData);
 
-  result->dispatcher = dispatcher;
+  result->dispatcher = g_object_ref (dispatcher);
   result->connection = connection;
 
   result->channel_type = g_strdup (channel_type);
@@ -164,6 +167,9 @@ free_dispatcher_request_data (DispatcherRequestData *r)
 {
   g_free (r->channel_type);
 
+  if (r->dispatcher != NULL)
+    g_object_unref (r->dispatcher);
+
   if (r->contact != NULL)
     g_object_unref (r->contact);
 
@@ -306,6 +312,8 @@ dispatcher_channel_invalidated_cb (TpProxy *proxy,
   g_hash_table_remove (cd->dispatched_channels, object_path);
   g_hash_table_remove (cd->dispatching_channels, object_path);
 
+  priv->channels = g_list_remove (priv->channels, proxy);
+
   operation = g_hash_table_lookup (cd->outstanding_channels, object_path);
   if (operation != NULL)
     {
@@ -387,6 +395,7 @@ dispatch_operation_ready_cb (EmpathyDispatchOperation *operation,
   g_object_unref (G_OBJECT (connection));
 
   g_object_ref (operation);
+  g_object_ref (dispatcher);
 
   dispatch_operation_flush_requests (dispatcher, operation, NULL, cd);
   status = empathy_dispatch_operation_get_status (operation);
@@ -409,6 +418,7 @@ dispatch_operation_ready_cb (EmpathyDispatchOperation *operation,
       g_signal_emit (dispatcher, signals[DISPATCH], 0, operation);
     }
 
+  g_object_unref (dispatcher);
 }
 
 static void
@@ -549,6 +559,8 @@ dispatcher_connection_new_channel (EmpathyDispatcher *dispatcher,
     G_CALLBACK (dispatcher_channel_invalidated_cb),
     dispatcher);
 
+  priv->channels = g_list_append (priv->channels, channel);
+
   if (handle_type == TP_CONN_HANDLE_TYPE_CONTACT)
     {
       EmpathyContactFactory *factory = empathy_contact_factory_dup_singleton ();
@@ -755,7 +767,6 @@ dispatcher_connection_ready_cb (TpConnection *connection,
   g_signal_connect (connection, "invalidated",
     G_CALLBACK (dispatcher_connection_invalidated_cb), dispatcher);
 
-
   if (tp_proxy_has_interface_by_id (TP_PROXY (connection),
       TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS))
     {
@@ -866,10 +877,28 @@ static void
 dispatcher_finalize (GObject *object)
 {
   EmpathyDispatcherPriv *priv = GET_PRIV (object);
+  GList *l;
+  GHashTableIter iter;
+  gpointer connection;
 
   g_signal_handlers_disconnect_by_func (priv->account_manager,
       dispatcher_account_connection_cb, object);
 
+  for (l = priv->channels; l; l = l->next)
+    {
+      g_signal_handlers_disconnect_by_func (l->data,
+          dispatcher_channel_invalidated_cb, object);
+    }
+
+  g_list_free (priv->channels);
+
+  g_hash_table_iter_init (&iter, priv->connections);
+  while (g_hash_table_iter_next (&iter, &connection, NULL))
+    {
+      g_signal_handlers_disconnect_by_func (connection,
+          dispatcher_connection_invalidated_cb, object);
+    }
+
   g_object_unref (priv->account_manager);
   g_object_unref (priv->mc);
 
@@ -941,6 +970,8 @@ empathy_dispatcher_init (EmpathyDispatcher *dispatcher)
   priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
     g_object_unref, (GDestroyNotify) free_connection_data);
 
+  priv->channels = NULL;
+
   accounts = mc_accounts_list_by_enabled (TRUE);
 
   for (l = accounts; l; l = l->next)
@@ -1034,6 +1065,8 @@ dispatcher_connection_new_requested_channel (EmpathyDispatcher *dispatcher,
             G_CALLBACK (dispatcher_channel_invalidated_cb),
             request_data->dispatcher);
 
+          priv->channels = g_list_append (priv->channels, channel);
+
           operation = empathy_dispatch_operation_new (request_data->connection,
              channel, request_data->contact, FALSE);
           g_object_unref (channel);