mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-08 03:30:17 +00:00
GtkPathBar: Centralize handling of outstanding cancellables
The path bar would crash if we disposed it before all pending I/O operations had finished. Now we remember all the outstanding operations directly in the GtkPathBarPrivate, and deal with them consistently.
This commit is contained in:
parent
ea91ab1d99
commit
559d32420c
133
gtk/gtkpathbar.c
133
gtk/gtkpathbar.c
@ -45,6 +45,30 @@ struct _GtkPathBarPrivate
|
|||||||
GFile *home_file;
|
GFile *home_file;
|
||||||
GFile *desktop_file;
|
GFile *desktop_file;
|
||||||
|
|
||||||
|
/* List of running GCancellable. When we cancel one, we remove it from this list.
|
||||||
|
* The pathbar cancels all outstanding cancellables when it is disposed.
|
||||||
|
*
|
||||||
|
* In code that queues async I/O operations:
|
||||||
|
*
|
||||||
|
* - Obtain a cancellable from the async I/O APIs, and call add_cancellable().
|
||||||
|
*
|
||||||
|
* To cancel a cancellable:
|
||||||
|
*
|
||||||
|
* - Call cancel_cancellable().
|
||||||
|
*
|
||||||
|
* In async I/O callbacks:
|
||||||
|
*
|
||||||
|
* - Check right away if g_cancellable_is_cancelled(): if true, just
|
||||||
|
* g_object_unref() the cancellable and return early (also free your
|
||||||
|
* closure data if you have one).
|
||||||
|
*
|
||||||
|
* - If it was not cancelled, call cancellable_async_done(). This will
|
||||||
|
* unref the cancellable and unqueue it from the pathbar's outstanding
|
||||||
|
* cancellables. Do your normal work to process the async result and free
|
||||||
|
* your closure data if you have one.
|
||||||
|
*/
|
||||||
|
GList *cancellables;
|
||||||
|
|
||||||
GCancellable *get_info_cancellable;
|
GCancellable *get_info_cancellable;
|
||||||
|
|
||||||
GIcon *root_icon;
|
GIcon *root_icon;
|
||||||
@ -167,6 +191,52 @@ static void gtk_path_bar_scroll_controller_scroll (GtkEventControllerScroll *scr
|
|||||||
gdouble dy,
|
gdouble dy,
|
||||||
GtkPathBar *path_bar);
|
GtkPathBar *path_bar);
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_cancellable (GtkPathBar *path_bar,
|
||||||
|
GCancellable *cancellable)
|
||||||
|
{
|
||||||
|
g_assert (g_list_find (path_bar->priv->cancellables, cancellable) == NULL);
|
||||||
|
path_bar->priv->cancellables = g_list_prepend (path_bar->priv->cancellables, cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
drop_node_for_cancellable (GtkPathBar *path_bar,
|
||||||
|
GCancellable *cancellable)
|
||||||
|
{
|
||||||
|
GList *node;
|
||||||
|
|
||||||
|
node = g_list_find (path_bar->priv->cancellables, cancellable);
|
||||||
|
g_assert (node != NULL);
|
||||||
|
node->data = NULL;
|
||||||
|
path_bar->priv->cancellables = g_list_delete_link (path_bar->priv->cancellables, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cancel_cancellable (GtkPathBar *path_bar,
|
||||||
|
GCancellable *cancellable)
|
||||||
|
{
|
||||||
|
drop_node_for_cancellable (path_bar, cancellable);
|
||||||
|
g_cancellable_cancel (cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cancellable_async_done (GtkPathBar *path_bar,
|
||||||
|
GCancellable *cancellable)
|
||||||
|
{
|
||||||
|
drop_node_for_cancellable (path_bar, cancellable);
|
||||||
|
g_object_unref (cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cancel_all_cancellables (GtkPathBar *path_bar)
|
||||||
|
{
|
||||||
|
while (path_bar->priv->cancellables)
|
||||||
|
{
|
||||||
|
GCancellable *cancellable = path_bar->priv->cancellables->data;
|
||||||
|
cancel_cancellable (path_bar, cancellable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_slider_unmap (GtkWidget *widget,
|
on_slider_unmap (GtkWidget *widget,
|
||||||
GtkPathBar *path_bar)
|
GtkPathBar *path_bar)
|
||||||
@ -207,6 +277,7 @@ gtk_path_bar_init (GtkPathBar *path_bar)
|
|||||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
|
gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
|
||||||
|
|
||||||
path_bar->priv->get_info_cancellable = NULL;
|
path_bar->priv->get_info_cancellable = NULL;
|
||||||
|
path_bar->priv->cancellables = NULL;
|
||||||
|
|
||||||
path_bar->priv->scroll_controller =
|
path_bar->priv->scroll_controller =
|
||||||
gtk_event_controller_scroll_new (GTK_WIDGET (path_bar),
|
gtk_event_controller_scroll_new (GTK_WIDGET (path_bar),
|
||||||
@ -282,6 +353,7 @@ gtk_path_bar_finalize (GObject *object)
|
|||||||
|
|
||||||
path_bar = GTK_PATH_BAR (object);
|
path_bar = GTK_PATH_BAR (object);
|
||||||
|
|
||||||
|
cancel_all_cancellables (path_bar);
|
||||||
gtk_path_bar_stop_scrolling (path_bar);
|
gtk_path_bar_stop_scrolling (path_bar);
|
||||||
|
|
||||||
g_list_free (path_bar->priv->button_list);
|
g_list_free (path_bar->priv->button_list);
|
||||||
@ -323,9 +395,8 @@ gtk_path_bar_dispose (GObject *object)
|
|||||||
|
|
||||||
remove_settings_signal (path_bar, gtk_widget_get_screen (GTK_WIDGET (object)));
|
remove_settings_signal (path_bar, gtk_widget_get_screen (GTK_WIDGET (object)));
|
||||||
|
|
||||||
if (path_bar->priv->get_info_cancellable)
|
|
||||||
g_cancellable_cancel (path_bar->priv->get_info_cancellable);
|
|
||||||
path_bar->priv->get_info_cancellable = NULL;
|
path_bar->priv->get_info_cancellable = NULL;
|
||||||
|
cancel_all_cancellables (path_bar);
|
||||||
|
|
||||||
G_OBJECT_CLASS (gtk_path_bar_parent_class)->dispose (object);
|
G_OBJECT_CLASS (gtk_path_bar_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
@ -1203,18 +1274,21 @@ set_button_image_get_info_cb (GCancellable *cancellable,
|
|||||||
GIcon *icon;
|
GIcon *icon;
|
||||||
struct SetButtonImageData *data = user_data;
|
struct SetButtonImageData *data = user_data;
|
||||||
|
|
||||||
if (cancellable != data->button_data->cancellable)
|
if (cancelled)
|
||||||
goto out;
|
|
||||||
|
|
||||||
data->button_data->cancellable = NULL;
|
|
||||||
|
|
||||||
if (!data->button_data->button)
|
|
||||||
{
|
{
|
||||||
g_free (data->button_data);
|
g_free (data);
|
||||||
goto out;
|
g_object_unref (cancellable);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled || error)
|
g_assert (GTK_IS_PATH_BAR (data->path_bar));
|
||||||
|
g_assert (G_OBJECT (data->path_bar)->ref_count > 0);
|
||||||
|
|
||||||
|
g_assert (cancellable == data->button_data->cancellable);
|
||||||
|
cancellable_async_done (data->path_bar, cancellable);
|
||||||
|
data->button_data->cancellable = NULL;
|
||||||
|
|
||||||
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
icon = g_file_info_get_symbolic_icon (info);
|
icon = g_file_info_get_symbolic_icon (info);
|
||||||
@ -1238,7 +1312,6 @@ set_button_image_get_info_cb (GCancellable *cancellable,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
g_free (data);
|
g_free (data);
|
||||||
g_object_unref (cancellable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1280,7 +1353,9 @@ set_button_image (GtkPathBar *path_bar,
|
|||||||
data->button_data = button_data;
|
data->button_data = button_data;
|
||||||
|
|
||||||
if (button_data->cancellable)
|
if (button_data->cancellable)
|
||||||
g_cancellable_cancel (button_data->cancellable);
|
{
|
||||||
|
cancel_cancellable (path_bar, button_data->cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
button_data->cancellable =
|
button_data->cancellable =
|
||||||
_gtk_file_system_get_info (path_bar->priv->file_system,
|
_gtk_file_system_get_info (path_bar->priv->file_system,
|
||||||
@ -1288,6 +1363,7 @@ set_button_image (GtkPathBar *path_bar,
|
|||||||
"standard::symbolic-icon",
|
"standard::symbolic-icon",
|
||||||
set_button_image_get_info_cb,
|
set_button_image_get_info_cb,
|
||||||
data);
|
data);
|
||||||
|
add_cancellable (path_bar, button_data->cancellable);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DESKTOP_BUTTON:
|
case DESKTOP_BUTTON:
|
||||||
@ -1302,7 +1378,9 @@ set_button_image (GtkPathBar *path_bar,
|
|||||||
data->button_data = button_data;
|
data->button_data = button_data;
|
||||||
|
|
||||||
if (button_data->cancellable)
|
if (button_data->cancellable)
|
||||||
g_cancellable_cancel (button_data->cancellable);
|
{
|
||||||
|
cancel_cancellable (path_bar, button_data->cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
button_data->cancellable =
|
button_data->cancellable =
|
||||||
_gtk_file_system_get_info (path_bar->priv->file_system,
|
_gtk_file_system_get_info (path_bar->priv->file_system,
|
||||||
@ -1310,6 +1388,7 @@ set_button_image (GtkPathBar *path_bar,
|
|||||||
"standard::symbolic-icon",
|
"standard::symbolic-icon",
|
||||||
set_button_image_get_info_cb,
|
set_button_image_get_info_cb,
|
||||||
data);
|
data);
|
||||||
|
add_cancellable (path_bar, button_data->cancellable);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NORMAL_BUTTON:
|
case NORMAL_BUTTON:
|
||||||
@ -1330,9 +1409,6 @@ button_data_free (ButtonData *button_data)
|
|||||||
|
|
||||||
button_data->button = NULL;
|
button_data->button = NULL;
|
||||||
|
|
||||||
if (button_data->cancellable)
|
|
||||||
g_cancellable_cancel (button_data->cancellable);
|
|
||||||
else
|
|
||||||
g_free (button_data);
|
g_free (button_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1429,7 +1505,6 @@ make_directory_button (GtkPathBar *path_bar,
|
|||||||
file_is_hidden = !! file_is_hidden;
|
file_is_hidden = !! file_is_hidden;
|
||||||
/* Is it a special button? */
|
/* Is it a special button? */
|
||||||
button_data = g_new0 (ButtonData, 1);
|
button_data = g_new0 (ButtonData, 1);
|
||||||
|
|
||||||
button_data->type = find_button_type (path_bar, file);
|
button_data->type = find_button_type (path_bar, file);
|
||||||
button_data->button = gtk_toggle_button_new ();
|
button_data->button = gtk_toggle_button_new ();
|
||||||
atk_obj = gtk_widget_get_accessible (button_data->button);
|
atk_obj = gtk_widget_get_accessible (button_data->button);
|
||||||
@ -1613,20 +1688,21 @@ gtk_path_bar_get_info_callback (GCancellable *cancellable,
|
|||||||
const gchar *display_name;
|
const gchar *display_name;
|
||||||
gboolean is_hidden;
|
gboolean is_hidden;
|
||||||
|
|
||||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
if (cancelled)
|
||||||
return;
|
|
||||||
|
|
||||||
if (cancellable != file_info->path_bar->priv->get_info_cancellable)
|
|
||||||
{
|
{
|
||||||
gtk_path_bar_set_file_finish (file_info, FALSE);
|
gtk_path_bar_set_file_finish (file_info, FALSE);
|
||||||
g_object_unref (cancellable);
|
g_object_unref (cancellable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_unref (cancellable);
|
g_assert (GTK_IS_PATH_BAR (file_info->path_bar));
|
||||||
|
g_assert (G_OBJECT (file_info->path_bar)->ref_count > 0);
|
||||||
|
|
||||||
|
g_assert (cancellable == file_info->path_bar->priv->get_info_cancellable);
|
||||||
|
cancellable_async_done (file_info->path_bar, cancellable);
|
||||||
file_info->path_bar->priv->get_info_cancellable = NULL;
|
file_info->path_bar->priv->get_info_cancellable = NULL;
|
||||||
|
|
||||||
if (cancelled || !info)
|
if (!info)
|
||||||
{
|
{
|
||||||
gtk_path_bar_set_file_finish (file_info, FALSE);
|
gtk_path_bar_set_file_finish (file_info, FALSE);
|
||||||
return;
|
return;
|
||||||
@ -1638,7 +1714,7 @@ gtk_path_bar_get_info_callback (GCancellable *cancellable,
|
|||||||
button_data = make_directory_button (file_info->path_bar, display_name,
|
button_data = make_directory_button (file_info->path_bar, display_name,
|
||||||
file_info->file,
|
file_info->file,
|
||||||
file_info->first_directory, is_hidden);
|
file_info->first_directory, is_hidden);
|
||||||
g_object_unref (file_info->file);
|
g_clear_object (&file_info->file);
|
||||||
|
|
||||||
file_info->new_buttons = g_list_prepend (file_info->new_buttons, button_data);
|
file_info->new_buttons = g_list_prepend (file_info->new_buttons, button_data);
|
||||||
|
|
||||||
@ -1668,6 +1744,7 @@ gtk_path_bar_get_info_callback (GCancellable *cancellable,
|
|||||||
"standard::display-name,standard::is-hidden,standard::is-backup",
|
"standard::display-name,standard::is-hidden,standard::is-backup",
|
||||||
gtk_path_bar_get_info_callback,
|
gtk_path_bar_get_info_callback,
|
||||||
file_info);
|
file_info);
|
||||||
|
add_cancellable (file_info->path_bar, file_info->path_bar->priv->get_info_cancellable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1693,7 +1770,9 @@ _gtk_path_bar_set_file (GtkPathBar *path_bar,
|
|||||||
info->parent_file = g_file_get_parent (info->file);
|
info->parent_file = g_file_get_parent (info->file);
|
||||||
|
|
||||||
if (path_bar->priv->get_info_cancellable)
|
if (path_bar->priv->get_info_cancellable)
|
||||||
g_cancellable_cancel (path_bar->priv->get_info_cancellable);
|
{
|
||||||
|
cancel_cancellable (path_bar, path_bar->priv->get_info_cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
path_bar->priv->get_info_cancellable =
|
path_bar->priv->get_info_cancellable =
|
||||||
_gtk_file_system_get_info (path_bar->priv->file_system,
|
_gtk_file_system_get_info (path_bar->priv->file_system,
|
||||||
@ -1701,7 +1780,7 @@ _gtk_path_bar_set_file (GtkPathBar *path_bar,
|
|||||||
"standard::display-name,standard::is-hidden,standard::is-backup",
|
"standard::display-name,standard::is-hidden,standard::is-backup",
|
||||||
gtk_path_bar_get_info_callback,
|
gtk_path_bar_get_info_callback,
|
||||||
info);
|
info);
|
||||||
|
add_cancellable (path_bar, path_bar->priv->get_info_cancellable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: This should be a construct-only property */
|
/* FIXME: This should be a construct-only property */
|
||||||
|
Loading…
Reference in New Issue
Block a user