GTask allows (buggy) application code to cause callback to be called twice, without warning
I discovered while testing some application code that it is possible to cause a GTask
's callback to be called twice, without any warnings from GLib, by returning both an error and a non-error value. Here is a sample program that demonstrates this: gtask-multiple-returns.c. It has four modes:
- If run with no arguments, it calls
g_task_return_boolean()
followed byg_task_return_new_error()
from outside anyGMainContext
, which forces the callback to be deferred to an idle. In this case, the call tog_task_return_new_error()
hits ag_return_if_fail()
guard, and GTask behaves as I would expect: it issues a critical for the invalid second return, and only calls the callback once. - If run with
--error-first
, it callsg_task_return_new_error()
followed byg_task_return_boolean()
. In this case, the callback is called twice from two successive idles, and GTask is silent. - If run with
--return-in-idle
, it calls those two functions (in one or other order, depending on--error-first
) from an idle. In this case, the callback fires synchronously, and because the callback immediately callsg_task_propagate_boolean()
, the error/success state of the GTask is cleared and GTask happily calls the callback a second time.
The second case bothers me most because GTask has all the information it needs to make the call to g_task_return_boolean()
fail. The third/fourth cases are maybe less bothersome because g_task_propagate_boolean()
has this line in its documentation, which you could argue gives GTask license to discard all of its internal state about the success/failure of the operation:
Since this method transfers ownership of the return value (or error) to the caller, you may only call it once.
But I think GTask should try pretty hard to ensure the callback is only called once, even in the face of application bugs, given its introspection annotations:
* @callback: (scope async): a #GAsyncReadyCallback.
* @callback_data: (closure): user data passed to @callback.
These are not true, so a Python program equivalent to a cut-down version of case 2 above segfaults. gtask-multiple-returns.py