From ed1732d95cc8750f7713cbc2f5446351ee1d35e6 Mon Sep 17 00:00:00 2001 From: xclaesse Date: Fri, 21 Nov 2008 16:24:36 +0000 Subject: Fix ref counting of tp_file, make it singleton per channel, and add a _keep_alive method that adds a tmp ref until the transfer finishes. git-svn-id: svn+ssh://svn.gnome.org/svn/empathy/trunk@1904 4ee84921-47dd-4033-b63a-18d7a039a3e4 --- libempathy/empathy-dispatcher.c | 5 ++- libempathy/empathy-tp-file.c | 73 ++++++++++++++++++++++++++++++++++++++--- libempathy/empathy-tp-file.h | 1 + src/empathy-ft-manager.c | 17 ++++++---- src/empathy.c | 2 ++ 5 files changed, 83 insertions(+), 15 deletions(-) diff --git a/libempathy/empathy-dispatcher.c b/libempathy/empathy-dispatcher.c index 4691ef6c1..21b6bcccb 100644 --- a/libempathy/empathy-dispatcher.c +++ b/libempathy/empathy-dispatcher.c @@ -957,11 +957,10 @@ file_channel_create_cb (TpConnection *connection, request->handle, NULL); - /* FIXME: tp_file is leaked, but we need to keep it alive until the data - * transfer is done. This is not the object that is going to be used - * for the ft manager. */ tp_file = empathy_tp_file_new (channel); empathy_tp_file_offer (tp_file, request->gfile, NULL); + empathy_tp_file_keep_alive (tp_file); + g_object_unref (tp_file); g_object_unref (request->gfile); g_slice_free (FileChannelRequest, request); diff --git a/libempathy/empathy-tp-file.c b/libempathy/empathy-tp-file.c index c885e0f20..420182f5f 100644 --- a/libempathy/empathy-tp-file.c +++ b/libempathy/empathy-tp-file.c @@ -297,6 +297,7 @@ struct _EmpathyTpFilePriv { time_t start_time; gchar *unix_socket_path; GCancellable *cancellable; + gboolean keep_alive; }; enum { @@ -325,6 +326,18 @@ empathy_tp_file_init (EmpathyTpFile *tp_file) tp_file->priv = priv; } +static void +tp_file_check_keep_alive (EmpathyTpFile *tp_file) +{ + if (tp_file->priv->keep_alive && + (tp_file->priv->state == EMP_FILE_TRANSFER_STATE_COMPLETED || + tp_file->priv->state == EMP_FILE_TRANSFER_STATE_CANCELLED)) + { + tp_file->priv->keep_alive = FALSE; + g_object_unref (tp_file); + } +} + static void tp_file_invalidated_cb (TpProxy *proxy, guint domain, @@ -343,14 +356,14 @@ tp_file_invalidated_cb (TpProxy *proxy, EMP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR; g_object_notify (G_OBJECT (tp_file), "state"); } + + tp_file_check_keep_alive (tp_file); } static void tp_file_finalize (GObject *object) { - EmpathyTpFile *tp_file; - - tp_file = EMPATHY_TP_FILE (object); + EmpathyTpFile *tp_file = EMPATHY_TP_FILE (object); if (tp_file->priv->channel) { @@ -475,6 +488,7 @@ tp_file_state_changed_cb (TpProxy *proxy, tp_file->priv->state_change_reason = reason; g_object_notify (G_OBJECT (tp_file), "state"); + tp_file_check_keep_alive (tp_file); } static void @@ -674,22 +688,50 @@ tp_file_set_property (GObject *object, }; } +static GHashTable *ft_table = NULL; + +static void +tp_file_weak_notify_cb (gpointer channel, + GObject *tp_file) +{ + g_hash_table_remove (ft_table, channel); +} + /** * empathy_tp_file_new: * @channel: a Telepathy channel * - * Creates a new #EmpathyTpFile wrapping @channel. + * Creates a new #EmpathyTpFile wrapping @channel, or return a new ref to an + * existing #EmpathyTpFile for that channel. * * Returns: a new #EmpathyTpFile */ EmpathyTpFile * empathy_tp_file_new (TpChannel *channel) { + EmpathyTpFile *tp_file; + g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL); - return g_object_new (EMPATHY_TYPE_TP_FILE, + if (ft_table != NULL) + { + tp_file = g_hash_table_lookup (ft_table, channel); + if (tp_file != NULL) { + return g_object_ref (tp_file); + } + } + else + ft_table = g_hash_table_new_full (empathy_proxy_hash, + empathy_proxy_equal, (GDestroyNotify) g_object_unref, NULL); + + tp_file = g_object_new (EMPATHY_TYPE_TP_FILE, "channel", channel, NULL); + + g_hash_table_insert (ft_table, g_object_ref (channel), tp_file); + g_object_weak_ref (G_OBJECT (tp_file), tp_file_weak_notify_cb, channel); + + return tp_file; } /** @@ -798,6 +840,27 @@ empathy_tp_file_offer (EmpathyTpFile *tp_file, GFile *gfile, GError **error) ¬hing, tp_file_method_cb, NULL, NULL, G_OBJECT (tp_file)); } +/** + * empathy_tp_file_keep_alive: + * @tp_file: an #EmpathyTpFile + * + * Keep @tp_file alive until the file transfer is COMPLETED or CANCELLED, by + * adding a temporary reference. + */ +void +empathy_tp_file_keep_alive (EmpathyTpFile *tp_file) +{ + g_return_if_fail (EMPATHY_IS_TP_FILE (tp_file)); + + if (tp_file->priv->keep_alive) + return; + + tp_file->priv->keep_alive = TRUE; + if (tp_file->priv->state != EMP_FILE_TRANSFER_STATE_COMPLETED && + tp_file->priv->state != EMP_FILE_TRANSFER_STATE_CANCELLED) + g_object_ref (tp_file); +} + EmpathyContact * empathy_tp_file_get_contact (EmpathyTpFile *tp_file) { diff --git a/libempathy/empathy-tp-file.h b/libempathy/empathy-tp-file.h index 7c656d2aa..e6c0dae7d 100644 --- a/libempathy/empathy-tp-file.h +++ b/libempathy/empathy-tp-file.h @@ -72,6 +72,7 @@ void empathy_tp_file_accept (EmpathyTpFile *tp_file, guint64 offset, void empathy_tp_file_cancel (EmpathyTpFile *tp_file); void empathy_tp_file_offer (EmpathyTpFile *tp_file, GFile *gfile, GError **error); +void empathy_tp_file_keep_alive (EmpathyTpFile *tp_file); guint64 empathy_tp_file_get_transferred_bytes (EmpathyTpFile *tp_file); EmpathyContact *empathy_tp_file_get_contact (EmpathyTpFile *tp_file); diff --git a/src/empathy-ft-manager.c b/src/empathy-ft-manager.c index b9839c4e3..2a9d1b229 100644 --- a/src/empathy-ft-manager.c +++ b/src/empathy-ft-manager.c @@ -186,6 +186,8 @@ ft_manager_update_buttons (EmpathyFTManager *ft_manager) /* I can abort if the transfer is not already finished */ abort_enabled = (state != EMP_FILE_TRANSFER_STATE_CANCELLED && state != EMP_FILE_TRANSFER_STATE_COMPLETED); + + g_object_unref (tp_file); } gtk_widget_set_sensitive (ft_manager->priv->open_button, open_enabled); @@ -439,7 +441,7 @@ ft_manager_configure_event_cb (GtkWidget *widget, } static void -ft_manager_remove_file_from_list (EmpathyFTManager *ft_manager, +ft_manager_remove_file_from_model (EmpathyFTManager *ft_manager, EmpathyTpFile *tp_file) { GtkTreeRowReference *row_ref; @@ -471,7 +473,6 @@ ft_manager_remove_file_from_list (EmpathyFTManager *ft_manager, else empty = TRUE; } - g_object_unref (tp_file); /* Select the next row */ if (!empty) @@ -494,7 +495,7 @@ remove_finished_transfer_foreach (gpointer key, if (state == EMP_FILE_TRANSFER_STATE_COMPLETED || state == EMP_FILE_TRANSFER_STATE_CANCELLED) { - ft_manager_remove_file_from_list (self, tp_file); + ft_manager_remove_file_from_model (self, tp_file); return TRUE; } @@ -551,9 +552,8 @@ ft_manager_add_tp_file_to_list (EmpathyFTManager *ft_manager, ft_manager->priv->model), path); gtk_tree_path_free (path); - g_object_ref (tp_file); - g_hash_table_insert (ft_manager->priv->tp_file_to_row_ref, tp_file, - row_ref); + g_hash_table_insert (ft_manager->priv->tp_file_to_row_ref, + g_object_ref (tp_file), row_ref); ft_manager_update_ft_row (ft_manager, tp_file); @@ -604,6 +604,7 @@ ft_manager_open (EmpathyFTManager *ft_manager) uri = g_object_get_data (G_OBJECT (tp_file), "uri"); DEBUG ("Opening URI: %s", uri); empathy_url_show (uri); + g_object_unref (tp_file); } static void @@ -627,6 +628,7 @@ ft_manager_stop (EmpathyFTManager *ft_manager) empathy_tp_file_get_filename (tp_file)); empathy_tp_file_cancel (tp_file); + g_object_unref (tp_file); } static void @@ -1059,7 +1061,8 @@ empathy_ft_manager_init (EmpathyFTManager *ft_manager) ft_manager->priv = priv; priv->tp_file_to_row_ref = g_hash_table_new_full (g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) gtk_tree_row_reference_free); + g_direct_equal, (GDestroyNotify) g_object_unref, + (GDestroyNotify) gtk_tree_row_reference_free); ft_manager_build_ui (ft_manager); } diff --git a/src/empathy.c b/src/empathy.c index c05f005ac..fc9640556 100644 --- a/src/empathy.c +++ b/src/empathy.c @@ -112,6 +112,8 @@ dispatch_channel_cb (EmpathyDispatcher *dispatcher, ft_manager = empathy_ft_manager_get_default (); tp_file = empathy_tp_file_new (channel); empathy_ft_manager_add_tp_file (ft_manager, tp_file); + empathy_tp_file_keep_alive (tp_file); + g_object_unref (tp_file); } g_free (channel_type); -- cgit v1.2.3