Porting async calls to GTask: newcomers welcome!

Dan Williams dcbw at redhat.com
Mon Feb 1 16:53:19 CET 2016


On Sat, 2016-01-30 at 19:27 -0800, Aleksander Morgado wrote:
> Hey hey,
> 
> ModemManager git master is already using GTask already for some
> things, instead of the good old GSimpleAsyncResult. GTask was
> released
> in GLib/GIO 2.36, and it has a really good API, including built-in
> cancellation support.
> 
> https://developer.gnome.org/gio/stable/GTask.html
> 
> Porting to GTask all the ModemManager code is a huge task, and
> definitely not a high priority thing, but it really is something good
> to have. So, if there is anyone out there willing to help with this,
> it's more than appreciated.
> 
> Given the scope of the work, it can really be done per-object, as the
> object API is anyway kept the same. As an example of the work needed,
> I ported the methods in MMSimQmi to using GTask:
> 
> http://cgit.freedesktop.org/ModemManager/ModemManager/commit/?id=aeb6
> 3621723fd3d7e39870466d2cffcf56fc6570

One downside I note is that with g_task_propagate_pointer() we no
longer have easy access to pointer itself, so we drop the debug
logging...

But overall a better API than before!

Dan

> Both GTask and GSimpleAsyncResult implement the GAsyncResult
> interface, which means that the API of the methods don't need to
> change, just the internals.
> 
> The main differences between GTask and GSimpleAsyncResult, regarding
> how the methods are implemented in MM, are:
> 
> 1) Context owned by the GTask, not the other way around.
> 
> For the async methods which need some context, we usually created a
> Context struct which contains the 'self' pointer as well as the
> GSimpleAsyncResult result (e.g. the LoadCapabilitiesContext in
> mm-broadband-modem.c). In this case, the context is owning the
> GSimpleAsyncResult, and during the async method steps, we pass the
> full context around. Once we have set a final result in the
> GSimpleAsyncResult, we would call a context_complete_and_free(),
> which
> would complete the GSimpleAsyncResult and free the context.
> 
> With GTask, this approach changes slightly. It is now the GTask the
> one which owns the Context, via g_task_set_task_data(). This method
> also receives a GDestroyNotify callback where we can pass the method
> to free the context. The 'self' pointer can be retrieved directly
> (transfer none, no new reference returned) with
> g_task_get_source_object(). Also, it is now the GTask object the one
> passed around in the internals of the async method, instead of the
> context struct.
> 
> 2) Completing the async result in idle or not in idle
> 
> With GSimpleAsyncResult we had to take care of completing it in an
> idle if the async() call was finishing right away, and we could
> complete not-in-idle otherwise. With GTask, this is taken care of
> automatically, all the g_task_return_() calls will do the right
> thing.
> Oh, and these calls do both setting the result and completing the
> call, unlike with GSimpleAsyncResult, which needed first a
> set_op_res_() call and then a complete() or complete_in_idle().
> 
> 3) Ownership of the task result data
> 
> In GSimpleAsyncResult, when a pointer data was set with
> g_simple_async_result_set_op_res_gpointer(), we could pass ownership
> of the data by explicitly providing a GDestroyNotify callback,
> meaning
> that when the GSimpleAsyncResult was disposed, the data would also be
> disposed with it. In the finish() function we would then need to dup
> or ref explicitly the data we got from
> g_simple_async_result_get_op_res_gpointer(), to get our own copy that
> we can return to the caller.
> 
> With GTask, we also set ownership of the data to the GTask in
> g_task_return_pointer(), but once we are in the finish() step, we can
> just g_task_propagate_pointer() to return the data itself to the
> caller, no need to get a dup or an extra reference.
> 
> 4) No need for 2 steps to return error and return data in finish()
> 
> With GSimpleAsyncResult, we usually had to first check if an error
> had
> to be returned with g_simple_async_result_propagate_error() and if
> not, then use e.g. the get_op_res_gpointer() call to prepare the
> output to return.
> 
> With GTask, a single g_task_propagate_() call does both things
> already.
> 
> 5) Built-in cancellations
> 
> With GSimpleAsyncResult we had to handle cancellations of async calls
> manually (and therefore we didn't do it much).
> 
> With GTask, the cancellation support is built in; the API of the
> GTask
> expects an optional GCancellable, and if this is ever cancelled, the
> async method will *always* return a G_IO_ERROR_CANCELLED error
> (regardless of whether a result was already set or not). Of course,
> cancellation points still need to be handled manually in the async
> operation. The not-yet-merge work on port probing cancellations uses
> the built-in GTask cancellation support:
> 
> http://cgit.freedesktop.org/ModemManager/ModemManager/commit/?h=aleks
> ander/plugin-manager&id=334f908e735e248b79e644c3ed4cf0301f7f864f
> 
> I think those points are the ones most applicable to ModemManager.
> And
> given the type of work, I really think that newcomers wanting to
> learn
> about GLib/GIO async calls could benefit a lot.
> 
> Anyone up to this (G)Task? ;)
> 


More information about the ModemManager-devel mailing list