Crash while updating apps when built against Mogwai
Please describe the issue you’re experiencing:
When built against mogwai, gnome-software reliably hits an assertion failure while performing Flatpak updates. I reproduce this daily with automatic updates – I haven't yet tested whether it affects manually-triggered updates but I don't see why not.
This is on Endless OS master with gnome-software 45.3, though the relevant code is unchanged in main.
To reproduce:
- Ensure
gnome-software
is built against mogwai - Quit
gnome-software
- Reset all timers as follows: (I can never remember which timer controls what so I reset them all):
for i in check-timestamp flatpak-purge-timestamp install-timestamp update-notification-timestamp upgrade-notification-timestamp
do
gsettings reset org.gnome.software $i
done
- Launch
gnome-software
. Within a couple of minutes it aborts with an assertion failure:
Gs:ERROR:../lib/gs-metered.c:124:block_data_free: assertion failed: (data->cancelled_id == 0)
Bail out! Gs:ERROR:../lib/gs-metered.c:124:block_data_free: assertion failed: (data->cancelled_id == 0)
The backtrace is as follows:
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
#1 0x00007f3d6e8b1e8f in __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2 0x00007f3d6e862fb2 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3 0x00007f3d6e84d472 in __GI_abort () at ./stdlib/abort.c:79
#4 0x00007f3d6f60ef0e in g_assertion_message (domain=domain@entry=0x7f3d6fa8a7ea "Gs", file=file@entry=0x7f3d6fa91ccd "../lib/gs-metered.c", line=line@entry=124, func=func@entry=0x7f3d6fa91f40 <__func__.4> "block_data_free",
message=message@entry=0x7f3d510ce2d0 "assertion failed: (data->cancelled_id == 0)") at ../../../glib/gtestutils.c:3497
#5 0x00007f3d6f67506a in g_assertion_message_expr (domain=domain@entry=0x7f3d6fa8a7ea "Gs", file=file@entry=0x7f3d6fa91ccd "../lib/gs-metered.c", line=line@entry=124, func=func@entry=0x7f3d6fa91f40 <__func__.4> "block_data_free",
expr=expr@entry=0x7f3d6fa91cfb "data->cancelled_id == 0") at ../../../glib/gtestutils.c:3523
#6 0x00007f3d6fa65be6 in block_data_free (data=0x7f3d521c82d0) at ../lib/gs-metered.c:124
#7 0x00007f3d6f848858 in g_task_finalize (object=0x7f3d2b2e48a0) at ../../../gio/gtask.c:773
#8 0x00007f3d6f7503ec in g_object_unref (_object=0x7f3d2b2e48a0) at ../../../gobject/gobject.c:3941
#9 0x00007f3d6f74ac78 in closure_invoke_notifiers (notify_type=<optimized out>, closure=<optimized out>) at ../../../gobject/gclosure.c:267
#10 g_closure_unref (closure=0x7f3d2b4e6a90) at ../../../gobject/gclosure.c:630
#11 0x00007f3d6f75d229 in handler_unref_R (signal_id=<optimized out>, instance=instance@entry=0x55f29fa5e700, handler=0x7f3d52293c70) at ../../../gobject/gsignal.c:771
#12 0x00007f3d6f75d772 in handler_unref_R (handler=<optimized out>, instance=0x55f29fa5e700, signal_id=<optimized out>) at ../../../gobject/gsignal.c:2778
#13 0x00007f3d6f764467 in g_signal_handler_disconnect (instance=instance@entry=0x55f29fa5e700, handler_id=handler_id@entry=45160) at ../../../gobject/gsignal.c:2757
#14 0x00007f3d6f7e784b in g_cancellable_disconnect (cancellable=cancellable@entry=0x55f29fa5e700, handler_id=45160) at ../../../gio/gcancellable.c:662
#15 0x00007f3d6fa660a4 in block_check_cb (task=task@entry=0x7f3d2b2e48a0, invalidated_error=invalidated_error@entry=0x0) at ../lib/gs-metered.c:300
#16 0x00007f3d6fa66265 in block_scheduler_schedule_cb (source_object=<optimized out>, result=<optimized out>, user_data=0x0) at ../lib/gs-metered.c:251
#17 0x00007f3d6f848003 in g_task_return_now (task=task@entry=0x7f3d523d5710) at ../../../gio/gtask.c:1371
#18 0x00007f3d6f848bf3 in g_task_return (type=<optimized out>, task=0x7f3d523d5710) at ../../../gio/gtask.c:1440
#19 g_task_return (task=0x7f3d523d5710, type=<optimized out>) at ../../../gio/gtask.c:1397
#20 0x00007f3d6eb76137 in ?? () from /lib/x86_64-linux-gnu/libmogwai-schedule-client-0.so.0
#21 0x00007f3d6f848003 in g_task_return_now (task=task@entry=0x7f3d525c9b10) at ../../../gio/gtask.c:1371
#22 0x00007f3d6f848bf3 in g_task_return (type=<optimized out>, task=0x7f3d525c9b10) at ../../../gio/gtask.c:1440
#23 g_task_return (task=0x7f3d525c9b10, type=<optimized out>) at ../../../gio/gtask.c:1397
#24 0x00007f3d6eb73c07 in ?? () from /lib/x86_64-linux-gnu/libmogwai-schedule-client-0.so.0
#25 0x00007f3d6f848003 in g_task_return_now (task=task@entry=0x7f3d519c2f30) at ../../../gio/gtask.c:1371
#26 0x00007f3d6f848bf3 in g_task_return (type=<optimized out>, task=0x7f3d519c2f30) at ../../../gio/gtask.c:1440
#27 g_task_return (task=0x7f3d519c2f30, type=<optimized out>) at ../../../gio/gtask.c:1397
#28 0x00007f3d6eb716d0 in ?? () from /lib/x86_64-linux-gnu/libmogwai-schedule-client-0.so.0
#29 0x00007f3d6f848003 in g_task_return_now (task=task@entry=0x7f3d2b6a7be0) at ../../../gio/gtask.c:1371
#30 0x00007f3d6f848bf3 in g_task_return (type=<optimized out>, task=0x7f3d2b6a7be0) at ../../../gio/gtask.c:1440
#31 g_task_return (task=0x7f3d2b6a7be0, type=<optimized out>) at ../../../gio/gtask.c:1397
#32 0x00007f3d6f8b11ba in init_second_async_cb (source_object=0x7f3d50659d90, res=<optimized out>, user_data=0x7f3d2b6a7be0) at ../../../gio/gdbusproxy.c:1771
#33 0x00007f3d6f848003 in g_task_return_now (task=task@entry=0x7f3d51782f50) at ../../../gio/gtask.c:1371
#34 0x00007f3d6f848bf3 in g_task_return (type=<optimized out>, task=0x7f3d51782f50) at ../../../gio/gtask.c:1440
#35 g_task_return (task=0x7f3d51782f50, type=<optimized out>) at ../../../gio/gtask.c:1397
#36 0x00007f3d6f8b02e3 in async_init_get_all_cb (connection=<optimized out>, res=<optimized out>, user_data=0x7f3d51782f50) at ../../../gio/gdbusproxy.c:1399
#37 0x00007f3d6f848003 in g_task_return_now (task=task@entry=0x7f3d2b42ff90) at ../../../gio/gtask.c:1371
#38 0x00007f3d6f848bf3 in g_task_return (type=<optimized out>, task=0x7f3d2b42ff90) at ../../../gio/gtask.c:1440
#39 g_task_return (task=0x7f3d2b42ff90, type=<optimized out>) at ../../../gio/gtask.c:1397
#40 0x00007f3d6f8a43c2 in g_dbus_connection_call_done (source=0x55f29f565f20, result=<optimized out>, user_data=0x7f3d2b42ff90) at ../../../gio/gdbusconnection.c:5897
#41 0x00007f3d6f848003 in g_task_return_now (task=task@entry=0x7f3d2af391f0) at ../../../gio/gtask.c:1371
#42 0x00007f3d6f848039 in complete_in_idle_cb (task=0x7f3d2af391f0) at ../../../gio/gtask.c:1385
#43 0x00007f3d6f646fdb in g_main_dispatch (context=context@entry=0x7f3d52309140) at ../../../glib/gmain.c:3476
#44 0x00007f3d6f649ff7 in g_main_context_dispatch_unlocked (context=0x7f3d52309140) at ../../../glib/gmain.c:4284
#45 g_main_context_iterate_unlocked (context=context@entry=0x7f3d52309140, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../../../glib/gmain.c:4349
#46 0x00007f3d6f64a5cc in g_main_context_iteration (context=context@entry=0x7f3d52309140, may_block=may_block@entry=1) at ../../../glib/gmain.c:4414
#47 0x00007f3d6fa6656d in gs_metered_block_on_download_scheduler (parameters=parameters@entry=0x7f3cfc80fa20, schedule_entry_handle_out=schedule_entry_handle_out@entry=0x7f3d5997a768, cancellable=cancellable@entry=0x55f29fa5e700,
error=error@entry=0x7f3d5997a760) at ../lib/gs-metered.c:102
#48 0x00007f3d6fa6682f in gs_metered_block_app_list_on_download_scheduler (app_list=app_list@entry=0x7f3d5053eff0, schedule_entry_handle_out=schedule_entry_handle_out@entry=0x7f3d5997a768, cancellable=cancellable@entry=0x55f29fa5e700,
error=error@entry=0x7f3d5997a760) at ../lib/gs-metered.c:549
#49 0x00007f3d5a207f7b in update_apps_thread_cb (task=0x55f2a0881a00, source_object=0x55f29fa2fd70, task_data=<optimized out>, cancellable=0x55f29fa5e700) at ../plugins/flatpak/gs-plugin-flatpak.c:1204
#50 0x00007f3d6fa847d6 in work_run_cb (_data=0x55f2a2384fd0) at ../lib/gs-worker-thread.c:236
#51 0x00007f3d6f646fdb in g_main_dispatch (context=context@entry=0x55f29fa542d0) at ../../../glib/gmain.c:3476
#52 0x00007f3d6f649ff7 in g_main_context_dispatch_unlocked (context=0x55f29fa542d0) at ../../../glib/gmain.c:4284
#53 g_main_context_iterate_unlocked (context=context@entry=0x55f29fa542d0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../../../glib/gmain.c:4349
#54 0x00007f3d6f64a5cc in g_main_context_iteration (context=0x55f29fa542d0, may_block=may_block@entry=1) at ../../../glib/gmain.c:4414
#55 0x00007f3d6fa8473e in thread_cb (data=0x55f29f47c780) at ../lib/gs-worker-thread.c:175
#56 0x00007f3d6f676b7d in g_thread_proxy (data=0x55f29fa54390) at ../../../glib/gthread.c:831
#57 0x00007f3d6e8b0134 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#58 0x00007f3d6e9307dc in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
Frame 16 is the following function (comments added):
static void
block_scheduler_schedule_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
MwscScheduler *scheduler = MWSC_SCHEDULER (source_object);
g_autoptr(GTask) task = g_steal_pointer (&user_data);
// ^^^ Notice the `task` reference is owned by this function and will be automatically released when this function returns
GCancellable *cancellable = g_task_get_cancellable (task);
g_autoptr(MwscScheduleEntry) schedule_entry = NULL;
g_autoptr(BlockData) data = NULL;
g_autoptr(GError) local_error = NULL;
schedule_entry = mwsc_scheduler_schedule_finish (scheduler, result, &local_error);
if (schedule_entry == NULL) {
g_task_return_error (task, g_steal_pointer (&local_error));
return;
}
/* Wait until the download is allowed to proceed. */
data = g_new0 (BlockData, 1);
data->schedule_entry = g_object_ref (schedule_entry);
data->notify_id = g_signal_connect_object (schedule_entry, "notify::download-now",
(GCallback) download_now_cb, task, G_CONNECT_DEFAULT);
data->invalidated_id = g_signal_connect_object (schedule_entry, "invalidated",
(GCallback) invalidated_cb, task, G_CONNECT_DEFAULT);
data->cancelled_id = g_cancellable_connect (cancellable,
(GCallback) cancelled_cb, task, (GDestroyNotify) g_object_unref);
// ^^^ Notice that this signal handler has `task` as its user_data parameter; and `g_object_unref` to release a reference to it when the handler is disconnected
// BUT no additional reference has been taken on `task`.
g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) block_data_free);
// We associate `data` with `task`, for use in callbacks and to be freed when `task` is freed
/* Do the initial check. */
block_check_cb (task, NULL);
// ^^^ Then we make this call.
}
Frame 15:
static void
block_check_cb (GTask *task,
const GError *invalidated_error)
{
BlockData *data = g_task_get_task_data (task);
GCancellable *cancellable = g_task_get_cancellable (task);
// […]
g_cancellable_disconnect (cancellable, data->cancelled_id);
// ^^^ we are at this line when the crash occurs. This is disconnecting the signal handler that was connected in frame 16
data->cancelled_id = 0;
// […]
}
Frame 8 is calling g_object_unref
on the task
, as described in the g_cancellable_connect
call in frame 16.
Frame 7 is finalizing that task – apparently we have released the final reference to it.
Frame 6 is calling block_data_free
as we requested in frame 16.
And the assertion fails because we didn't reach the line that zeroes data->cancelled_id
in frame 15.