[Spice-devel] [spice-gtk v2] Introduce gtask-helper.[ch]

Fabiano FidĂȘncio fabiano at fidencio.org
Wed Mar 23 14:41:34 UTC 2016


On Wed, Mar 23, 2016 at 2:45 PM, Victor Toso <lists at victortoso.com> wrote:
> Hi,
>
> On Wed, Mar 23, 2016 at 02:32:06PM +0100, Christophe Fergeau wrote:
>> On Wed, Mar 23, 2016 at 01:04:03PM +0100, Fabiano FidĂȘncio wrote:
>> > gtask-helper provide methods that can easily be used for returning in
>> > idle, as a few issues have been found in the GTask code used in
>> > spice-gtk due to a immediately return when a return in idle was
>> > expected. As examples of these issues, you can take a look on commits
>> > 7774b8c and e81d97c.
>> >
>> > Not all the functions introduced in gtask-helper.h are being used, but I
>> > still decided to add them for completeness reasons.
>> >
>> > Also, all the functions called in idle are the same that were being
>> > called in idle when using GSimpleAsyncResult. So, no issues should be
>> > found after this change and no behavior change should noticed.
>>
>> This is indeed the safe way to go given the few crashes we had. However,
>> it would be nice to try to get rid of as much of these idle calls as
>> possible (when it's safe to do so) in the future so that the places
>> which need to be special are obvious.
>>
>> >
>>
>>
>> > diff --git a/src/gtask-helper.c b/src/gtask-helper.c
>> > new file mode 100644
>> > index 0000000..3c72396
>> > --- /dev/null
>> > +++ b/src/gtask-helper.c
>> > @@ -0,0 +1,153 @@
>> > +
>> > +
>> > +void __attribute__((format(gnu_printf, 4, 5)))
>>
>> glib provides G_GNUC_PRINTF()
>> Doesn't this belong in the header file though rather than in the .c
>> file?
>>
>> > +g_task_helper_return_new_error_in_idle(GTask *task,
>> > +                                       GQuark domain,
>> > +                                       gint code,
>> > +                                       const char *format,
>> > +                                       ...)
>> > +{
>> > +    GTaskHelperData *data = g_task_helper_new();
>> > +    va_list args;
>> > +
>> > +    data->task = g_object_ref(task);
>> > +    va_start(args, format);
>> > +    data->error = g_error_new_valist(domain, code, format, args);
>> > +    va_end(args);
>> > +
>> > +    g_idle_add(complete_in_idle_error_cb, data);
>> > +}
>> > +
>> > +void g_task_helper_return_pointer_in_idle(GTask *task,
>> > +                                          gpointer result,
>> > +                                          GDestroyNotify result_destroy)
>> > +{
>> > +    GTaskHelperData *data = g_task_helper_new();
>> > +    data->task = g_object_ref(task);
>> > +    data->pointer = result;
>> > +    data->destroy_notify_cb = result_destroy;
>> > +
>> > +    g_idle_add(complete_in_idle_pointer_cb, data);
>> > +}
>> > diff --git a/src/gtask-helper.h b/src/gtask-helper.h
>> > new file mode 100644
>> > index 0000000..81c041f
>> > --- /dev/null
>> > +++ b/src/gtask-helper.h
>> > @@ -0,0 +1,34 @@
>> > +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
>> > +/*
>> > +   Copyright (C) 2016 Red Hat, Inc.
>> > +
>> > +   This library is free software; you can redistribute it and/or
>> > +   modify it under the terms of the GNU Lesser General Public
>> > +   License as published by the Free Software Foundation; either
>> > +   version 2.0 of the License, or (at your option) any later version.
>> > +
>> > +   This library is distributed in the hope that it will be useful,
>> > +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> > +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> > +   Lesser General Public License for more details.
>> > +
>> > +   You should have received a copy of the GNU Lesser General Public
>> > +   License along with this library; if not, write to the Free Software
>> > +   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
>> > +*/
>> > +#ifndef __G_TASK_HELPER_H__
>> > +#define __G_TASK_HELPER_H__
>> > +
>> > +#include <gio/gio.h>
>> > +
>> > +G_BEGIN_DECLS
>> > +
>> > +void g_task_helper_return_boolean_in_idle(GTask *task, gboolean result);
>> > +void g_task_helper_return_int_in_idle(GTask *task, gssize result);
>> > +void g_task_helper_return_error_in_idle(GTask *task, GError *error);
>> > +void g_task_helper_return_new_error_in_idle(GTask *task, GQuark domain, gint code, const char *format, ...);
>> > +void g_task_helper_return_pointer_in_idle(GTask *task, gpointer result, GDestroyNotify result_destroy);
>> > +
>> > +G_END_DECLS
>> > +
>> > +#endif /* __G_TASK_HELPER_H__ */
>> > diff --git a/src/spice-channel.c b/src/spice-channel.c
>> > index 8ae0e4d..fdbc45b 100644
>> > --- a/src/spice-channel.c
>> > +++ b/src/spice-channel.c
>> > @@ -17,6 +17,8 @@
>> >  */
>> >  #include "config.h"
>> >
>> > +#include "gtask-helper.h"
>> > +
>> >  #include "spice-client.h"
>> >  #include "spice-common.h"
>> >
>> > @@ -2191,7 +2193,7 @@ static void spice_channel_flushed(SpiceChannel *channel, gboolean success)
>> >      GSList *l;
>> >
>> >      for (l = c->flushing; l != NULL; l = l->next) {
>> > -        g_task_return_boolean(G_TASK(l->data), success);
>> > +        g_task_helper_return_boolean_in_idle(G_TASK(l->data), success);
>> >      }
>> >
>> >      g_slist_free_full(c->flushing, g_object_unref);
>> > diff --git a/src/spice-gstaudio.c b/src/spice-gstaudio.c
>> > index a7c3c24..d810911 100644
>> > --- a/src/spice-gstaudio.c
>> > +++ b/src/spice-gstaudio.c
>> > @@ -22,6 +22,8 @@
>> >  #include <gst/app/gstappsink.h>
>> >  #include <gst/audio/streamvolume.h>
>> >
>> > +#include "gtask-helper.h"
>> > +
>> >  #include "spice-gstaudio.h"
>> >  #include "spice-common.h"
>> >  #include "spice-session.h"
>> > @@ -572,7 +574,7 @@ static void spice_gstaudio_get_playback_volume_info_async(SpiceAudio *audio,
>> >  {
>> >      GTask *task = g_task_new(audio, cancellable, callback, user_data);
>> >
>> > -    g_task_return_boolean(task, TRUE);
>> > +    g_task_helper_return_boolean_in_idle(task, TRUE);
>> >  }
>> >
>> >  static gboolean spice_gstaudio_get_playback_volume_info_finish(SpiceAudio *audio,
>> > @@ -654,7 +656,7 @@ static void spice_gstaudio_get_record_volume_info_async(SpiceAudio *audio,
>> >  {
>> >      GTask *task = g_task_new(audio, cancellable, callback, user_data);
>> >
>> > -    g_task_return_boolean(task, TRUE);
>> > +    g_task_helper_return_boolean_in_idle(task, TRUE);
>> >  }
>> >
>> >  static gboolean spice_gstaudio_get_record_volume_info_finish(SpiceAudio *audio,
>> > diff --git a/src/usb-acl-helper.c b/src/usb-acl-helper.c
>> > index 487e1ee..5d03879 100644
>> > --- a/src/usb-acl-helper.c
>> > +++ b/src/usb-acl-helper.c
>> > @@ -25,6 +25,8 @@
>> >  #include <stdio.h>
>> >  #include <string.h>
>> >
>> > +#include "gtask-helper.h"
>> > +
>> >  #include "usb-acl-helper.h"
>> >
>> >  /* ------------------------------------------------------------------ */
>> > @@ -91,7 +93,7 @@ static void spice_usb_acl_helper_class_init(SpiceUsbAclHelperClass *klass)
>> >
>> >  static void async_result_set_cancelled(GTask *task)
>> >  {
>> > -    g_task_return_new_error(task,
>> > +    g_task_helper_return_new_error_in_idle(task,
>> >                  G_IO_ERROR, G_IO_ERROR_CANCELLED,
>> >                  "Setting USB device node ACL cancelled");
>>
>> Indentation is broken here
>>
>> >  }
>> > @@ -120,11 +122,11 @@ static gboolean cb_out_watch(GIOChannel    *channel,
>> >              string[strlen(string) - 1] = 0;
>> >              if (!strcmp(string, "SUCCESS")) {
>> >                  success = TRUE;
>> > -                g_task_return_boolean(priv->task, TRUE);
>> > +                g_task_helper_return_boolean_in_idle(priv->task, TRUE);
>> >              } else if (!strcmp(string, "CANCELED")) {
>> >                  async_result_set_cancelled(priv->task);
>> >              } else {
>> > -                g_task_return_new_error(priv->task,
>> > +                g_task_helper_return_new_error_in_idle(priv->task,
>> >                              SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
>> >                              "Error setting USB device node ACL: '%s'",
>> >                              string);
>>
>> and there.
>>
>> > @@ -132,10 +134,10 @@ static gboolean cb_out_watch(GIOChannel    *channel,
>> >              g_free(string);
>> >              break;
>> >          case G_IO_STATUS_ERROR:
>> > -            g_task_return_error(priv->task, err);
>> > +            g_task_helper_return_error_in_idle(priv->task, err);
>> >              break;
>> >          case G_IO_STATUS_EOF:
>> > -            g_task_return_new_error(priv->task,
>> > +            g_task_helper_return_new_error_in_idle(priv->task,
>> >                          SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
>> >                          "Unexpected EOF reading from acl helper stdout");
>> >              break;
>> > diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
>> > index 85231a1..eaaf6a4 100644
>> > --- a/src/usb-device-manager.c
>> > +++ b/src/usb-device-manager.c
>> > @@ -42,6 +42,8 @@
>> >  #include "usbutil.h"
>> >  #endif
>> >
>> > +#include "gtask-helper.h"
>> > +
>> >  #include "spice-session-priv.h"
>> >  #include "spice-client.h"
>> >  #include "spice-marshal.h"
>> > @@ -1442,7 +1444,7 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
>> >      guint i;
>> >
>> >      if (spice_usb_device_manager_is_device_connected(self, device)) {
>> > -        g_task_return_new_error(task,
>> > +        g_task_helper_return_new_error_in_idle(task,
>> >                              SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
>> >                              "Cannot connect an already connected usb device");
>> >          goto done;
>> > @@ -1466,10 +1468,10 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
>> >              g_ptr_array_remove(priv->devices, device);
>> >              g_signal_emit(self, signals[DEVICE_REMOVED], 0, device);
>> >              spice_usb_device_unref(device);
>> > -            g_task_return_new_error(task,
>> > -                                    SPICE_CLIENT_ERROR,
>> > -                                    SPICE_CLIENT_ERROR_FAILED,
>> > -                                    _("Device was not found"));
>> > +            g_task_helper_return_new_error_in_idle(task,
>> > +                                           SPICE_CLIENT_ERROR,
>> > +                                           SPICE_CLIENT_ERROR_FAILED,
>> > +                                           _("Device was not found"));
>> >              goto done;
>> >          }
>> >  #endif
>> > @@ -1484,9 +1486,9 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
>> >      }
>> >  #endif
>> >
>> > -    g_task_return_new_error(task,
>> > -                            SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
>> > -                            _("No free USB channel"));
>> > +    g_task_helper_return_new_error_in_idle(task,
>> > +                                   SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
>> > +                                   _("No free USB channel"));
>> >  #ifdef USE_USBREDIR
>> >  done:
>> >  #endif
>> > diff --git a/src/vmcstream.c b/src/vmcstream.c
>> > index 5dd2799..2df31e5 100644
>> > --- a/src/vmcstream.c
>> > +++ b/src/vmcstream.c
>> > @@ -19,6 +19,7 @@
>> >
>> >  #include <string.h>
>> >
>> > +#include "gtask-helper.h"
>> >  #include "vmcstream.h"
>> >  #include "spice-channel-priv.h"
>> >  #include "gio-coroutine.h"
>> > @@ -97,24 +98,6 @@ spice_vmc_input_stream_new(void)
>> >      return self;
>> >  }
>> >
>> > -typedef struct _complete_in_idle_cb_data {
>> > -    GTask *task;
>> > -    gssize pos;
>> > -} complete_in_idle_cb_data;
>> > -
>> > -static gboolean
>> > -complete_in_idle_cb(gpointer user_data)
>> > -{
>> > -    complete_in_idle_cb_data *data = user_data;
>> > -
>> > -    g_task_return_int(data->task, data->pos);
>> > -
>> > -    g_object_unref (data->task);
>> > -    g_free (data);
>> > -
>> > -    return FALSE;
>> > -}
>> > -
>> >  /* coroutine */
>> >  /**
>> >   * Feed a SpiceVmc stream with new data from a coroutine
>> > @@ -134,8 +117,6 @@ spice_vmc_input_stream_co_data(SpiceVmcInputStream *self,
>> >      self->coroutine = coroutine_self();
>> >
>> >      while (size > 0) {
>> > -        complete_in_idle_cb_data *cb_data;
>> > -
>> >          SPICE_DEBUG("spicevmc co_data %p", self->task);
>> >          if (!self->task)
>> >              coroutine_yield(NULL);
>> > @@ -157,13 +138,7 @@ spice_vmc_input_stream_co_data(SpiceVmcInputStream *self,
>> >          if (self->all && min > 0 && self->pos != self->count)
>> >              continue;
>> >
>> > -        /* Let's deal with the task complete in idle by ourselves, as GTask
>> > -         * heuristic only makes sense in a non-coroutine case.
>> > -         */
>> > -        cb_data = g_new(complete_in_idle_cb_data , 1);
>> > -        cb_data->task = g_object_ref(self->task);
>> > -        cb_data->pos = self->pos;
>> > -        g_idle_add(complete_in_idle_cb, cb_data);
>> > +        g_task_helper_return_int_in_idle(self->task, self->pos);
>> >
>> >          g_cancellable_disconnect(g_task_get_cancellable(self->task), self->cancel_id);
>> >          g_clear_object(&self->task);
>> > @@ -179,9 +154,9 @@ read_cancelled(GCancellable *cancellable,
>> >      SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(user_data);
>> >
>> >      SPICE_DEBUG("read cancelled, %p", self->task);
>> > -    g_task_return_new_error(self->task,
>> > -                            G_IO_ERROR, G_IO_ERROR_CANCELLED,
>> > -                            "read cancelled");
>> > +    g_task_helper_return_new_error_in_idle(self->task,
>> > +                                   G_IO_ERROR, G_IO_ERROR_CANCELLED,
>> > +                                   "read cancelled");
>>
>> Indentation
>>
>> >
>> >      g_clear_object(&self->task);
>> >
>> > diff --git a/src/win-usb-driver-install.c b/src/win-usb-driver-install.c
>> > index 0d4627a..14de369 100644
>> > --- a/src/win-usb-driver-install.c
>> > +++ b/src/win-usb-driver-install.c
>> > @@ -31,6 +31,7 @@
>> >  #include <gio/gio.h>
>> >  #include <gio/gwin32inputstream.h>
>> >  #include <gio/gwin32outputstream.h>
>> > +#include "gtask-helper.h"
>> >  #include "spice-util.h"
>> >  #include "win-usb-clerk.h"
>> >  #include "win-usb-driver-install.h"
>> > @@ -143,13 +144,13 @@ void win_usb_driver_handle_reply_cb(GObject *gobject,
>> >
>> >      if (err) {
>> >          g_warning("failed to read reply from usbclerk (%s)", err->message);
>> > -        g_task_return_error(priv->task, err);
>> > +        g_task_helper_return_error_in_idle(priv->task, err);
>> >          goto failed_reply;
>> >      }
>> >
>> >      if (bytes == 0) {
>> >          g_warning("unexpected EOF from usbclerk");
>> > -        g_task_return_new_error(priv->task,
>> > +        g_task_helper_return_new_error_in_idle(priv->task,
>> >                                  SPICE_WIN_USB_DRIVER_ERROR,
>> >                                  SPICE_WIN_USB_DRIVER_ERROR_FAILED,
>> >                                  "unexpected EOF from usbclerk");
>>
>> Indentation (there are more below, and maybe before too).
>>
>>
>> Acked-by: Christophe Fergeau <cfergeau at redhat.com>
>>
>> Christophe
>
> I would rather that we wait to see if we find more issues. At the
> moment, we don't have any. The coroutine context is the only one so far
> that it was necessary to complete-in-idle.

Well, it was not the only one as 7774b8c shows ...

> The other on
> spice_usbredir_channel_open_acl_cb() was quite simple (finalize before
> calling the callback).

... a crash for the same root cause ...

>
> Not having any issue to address and including this g-task-helper does
> not make much sense for me.
>

I really would prefer to play safe than sorry, but that's just my opinion.

> cheers,
>   toso
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel

I'm not going to waste my time on this anymore ...

Best Regards,
-- 
Fabiano FidĂȘncio


More information about the Spice-devel mailing list