[virt-tools-list] [libosinfo 2/2] Async variant of osinfo_media_create_from_location()

Daniel P. Berrange berrange at redhat.com
Tue Aug 30 15:42:01 UTC 2011


On Tue, Aug 23, 2011 at 11:47:43PM +0300, Zeeshan Ali (Khattak) wrote:
> From: "Zeeshan Ali (Khattak)" <zeeshanak at gnome.org>
> 
> ---
>  docs/reference/Libosinfo-sections.txt |    2 +
>  osinfo/libosinfo.syms                 |    2 +
>  osinfo/osinfo_media.c                 |  351 +++++++++++++++++++++++++-------
>  osinfo/osinfo_media.h                 |    7 +
>  4 files changed, 285 insertions(+), 77 deletions(-)
> 
> diff --git a/docs/reference/Libosinfo-sections.txt b/docs/reference/Libosinfo-sections.txt
> index e2454da..68464e3 100644
> --- a/docs/reference/Libosinfo-sections.txt
> +++ b/docs/reference/Libosinfo-sections.txt
> @@ -404,6 +404,8 @@ OsinfMediaoClass
>  OsinfMediaoPrivate
>  osinfo_media_new
>  osinfo_media_create_from_location
> +osinfo_media_create_from_location_async
> +osinfo_media_create_from_location_finish
>  osinfo_media_get_architecture
>  osinfo_media_get_url
>  osinfo_media_get_volume_id
> diff --git a/osinfo/libosinfo.syms b/osinfo/libosinfo.syms
> index 6541a90..2be90ee 100644
> --- a/osinfo/libosinfo.syms
> +++ b/osinfo/libosinfo.syms
> @@ -144,6 +144,8 @@ LIBOSINFO_0.0.1 {
>  	osinfo_media_error_quark;
>  	osinfo_media_new;
>  	osinfo_media_create_from_location;
> +	osinfo_media_create_from_location_async;
> +	osinfo_media_create_from_location_finish;
>  	osinfo_media_get_architecture;
>  	osinfo_media_get_url;
>  	osinfo_media_get_volume_id;
> diff --git a/osinfo/osinfo_media.c b/osinfo/osinfo_media.c
> index 431ae72..6adf623 100644
> --- a/osinfo/osinfo_media.c
> +++ b/osinfo/osinfo_media.c
> @@ -53,6 +53,44 @@ struct _SupplementaryVolumeDescriptor {
>      gchar  system[MAX_SYSTEM]; /* System ID */
>  };
>  
> +typedef struct _CreateFromLocationAsyncData CreateFromLocationAsyncData;
> +struct _CreateFromLocationAsyncData {
> +    GFile *file;
> +
> +    gint priority;
> +    GCancellable *cancellable;
> +
> +    GSimpleAsyncResult *res;
> +
> +    PrimaryVolumeDescriptor pvd;
> +    SupplementaryVolumeDescriptor svd;
> +};
> +
> +static void create_from_location_async_data_free
> +                                (CreateFromLocationAsyncData *data)
> +{
> +   g_object_unref(data->file);
> +   g_clear_object(&data->cancellable);
> +   g_object_unref(data->res);
> +
> +   g_slice_free(CreateFromLocationAsyncData, data);
> +}
> +
> +typedef struct _CreateFromLocationData CreateFromLocationData;
> +struct _CreateFromLocationData {
> +    GMainLoop *main_loop;
> +
> +    GAsyncResult *res;
> +};
> +
> +static void create_from_location_data_free(CreateFromLocationData *data)
> +{
> +   g_object_unref(data->res);
> +   g_main_loop_unref(data->main_loop);
> +
> +   g_slice_free(CreateFromLocationData, data);
> +}
> +
>  GQuark
>  osinfo_media_error_quark (void)
>  {
> @@ -114,7 +152,7 @@ OsinfoMedia *osinfo_media_new(const gchar *id,
>                                const gchar *architecture)
>  {
>      OsinfoMedia *media;
> -  
> +
>      media = g_object_new(OSINFO_TYPE_MEDIA,
>                           "id", id,
>                           NULL);
> @@ -126,6 +164,17 @@ OsinfoMedia *osinfo_media_new(const gchar *id,
>      return media;
>  }
>  
> +static void on_media_create_from_location_ready (GObject *source_object,
> +                                                 GAsyncResult *res,
> +                                                 gpointer user_data)
> +{
> +    CreateFromLocationData *data = (CreateFromLocationData *)user_data;
> +
> +    data->res = g_object_ref(res);
> +
> +    g_main_loop_quit(data->main_loop);
> +}
> +
>  /**
>   * osinfo_media_create_from_location:
>   * @location: the location of an installation media
> @@ -143,91 +192,61 @@ OsinfoMedia *osinfo_media_create_from_location(const gchar *location,
>                                                 GCancellable *cancellable,
>                                                 GError **error)
>  {
> -    OsinfoMedia *ret = NULL;
> -    PrimaryVolumeDescriptor pvd;
> -    SupplementaryVolumeDescriptor svd;
> -    GFile *file;
> -    GFileInputStream *stream;
> -    gchar *uri;
> +    CreateFromLocationData *data;
> +    OsinfoMedia *ret;
>  
> -    g_return_val_if_fail(location != NULL, NULL);
> -    g_return_val_if_fail(error == NULL || *error == NULL, NULL);
> -
> -    file = g_file_new_for_commandline_arg(location);
> -    stream = g_file_read(file, cancellable, error);
> -    if (error != NULL && *error != NULL) {
> -        g_prefix_error(error, "Failed to open file");
> +    data = g_slice_new0(CreateFromLocationData);
> +    data->main_loop = g_main_loop_new (g_main_context_get_thread_default (),
> +                                       TRUE);
>  
> -        goto EXIT;
> -    }
> +    osinfo_media_create_from_location_async(location,
> +                                            G_PRIORITY_DEFAULT,
> +                                            cancellable,
> +                                            on_media_create_from_location_ready,
> +                                            data);
>  
> -    memset(&pvd, 0, sizeof(pvd));
> -    if (g_input_stream_skip(G_INPUT_STREAM(stream),
> -                            PVD_OFFSET,
> -                            cancellable,
> -                            error) < sizeof(pvd)) {
> -        if (*error)
> -            g_prefix_error(error, "Failed to skip %d bytes", PVD_OFFSET);
> -        else
> -            g_set_error(error,
> -                         OSINFO_MEDIA_ERROR,
> -                         OSINFO_MEDIA_ERROR_NO_DESCRIPTORS,
> -                         "No volume descriptors");
> -
> -        goto EXIT;
> -    }
> -
> -    if (g_input_stream_read(G_INPUT_STREAM(stream),
> -                            &pvd,
> -                            sizeof(pvd),
> -                            cancellable,
> -                            error) < sizeof(pvd)) {
> -        if (*error)
> -            g_prefix_error(error, "Failed to read primary volume descriptor");
> -        else
> -            g_set_error(error,
> -                        OSINFO_MEDIA_ERROR,
> -                        OSINFO_MEDIA_ERROR_NO_PVD,
> -                        "Primary volume descriptor unavailable");
> +    /* Loop till we get a reply (or time out) */
> +    if (g_main_loop_is_running (data->main_loop))
> +        g_main_loop_run (data->main_loop);
>  
> -        goto EXIT;
> -    }
> +    ret = osinfo_media_create_from_location_finish(data->res, error);
> +    create_from_location_data_free(data);
>  
> -    pvd.volume[MAX_VOLUME - 1] = 0;
> -    pvd.system[MAX_SYSTEM - 1] = 0;
> -    pvd.publisher[MAX_PUBLISHER - 1] = 0;
> +    return ret;
> +}
>  
> -    if (pvd.volume[0] && (pvd.system[0] == 0 && pvd.publisher[0] == 0)) {
> -        g_set_error(error,
> -                    OSINFO_MEDIA_ERROR,
> -                    OSINFO_MEDIA_ERROR_INSUFFIENT_METADATA,
> -                    "Insufficient metadata on installation media");
> +static void on_svd_read (GObject *source,
> +                         GAsyncResult *res,
> +                         gpointer user_data)
> +{
> +    OsinfoMedia *ret = NULL;
> +    GInputStream *stream = G_INPUT_STREAM(source);
> +    gchar *uri;
> +    GError *error = NULL;
> +    CreateFromLocationAsyncData *data;
>  
> -        goto EXIT;
> -    }
> +    data = (CreateFromLocationAsyncData *)user_data;
>  
> -    memset(&svd, 0, sizeof(svd));
> -    if (g_input_stream_read(G_INPUT_STREAM(stream),
> -                            &svd,
> -                            sizeof(svd),
> -                            cancellable,
> -                            error) < sizeof(svd)) {
> -        if (*error)
> -            g_prefix_error(error,
> +    if (g_input_stream_read_finish(stream,
> +                                   res,
> +                                   &error) < sizeof(data->svd)) {
> +        if (error)
> +            g_prefix_error(&error,
>                             "Failed to read supplementary volume descriptor");
>          else
> -            g_set_error(error,
> +            g_set_error(&error,
>                          OSINFO_MEDIA_ERROR,
>                          OSINFO_MEDIA_ERROR_NO_SVD,
>                          "Supplementary volume descriptor unavailable");
>  
> +
>          goto EXIT;
>      }
>  
> -    svd.system[MAX_SYSTEM - 1] = 0;
> +    data->svd.system[MAX_SYSTEM - 1] = 0;
>  
> -    if (strncmp(BOOTABLE_TAG, svd.system, sizeof(BOOTABLE_TAG) != 0)) {
> -        g_set_error(error,
> +    if (strncmp(BOOTABLE_TAG, data->svd.system, sizeof(BOOTABLE_TAG) != 0)) {
> +        g_set_error(&error,
>                      OSINFO_MEDIA_ERROR,
>                      OSINFO_MEDIA_ERROR_NOT_BOOTABLE,
>                      "Install media is not bootable");
> @@ -235,7 +254,7 @@ OsinfoMedia *osinfo_media_create_from_location(const gchar *location,
>          goto EXIT;
>      }
>  
> -    uri = g_file_get_uri(file);
> +    uri = g_file_get_uri(data->file);
>      ret = g_object_new(OSINFO_TYPE_MEDIA,
>                         "id", uri,
>                         NULL);
> @@ -243,24 +262,202 @@ OsinfoMedia *osinfo_media_create_from_location(const gchar *location,
>                              OSINFO_MEDIA_PROP_URL,
>                              uri);
>      g_free(uri);
> -    if (pvd.volume[0] != 0)
> +    if (data->pvd.volume[0] != 0)
>          osinfo_entity_set_param(OSINFO_ENTITY(ret),
>                                  OSINFO_MEDIA_PROP_VOLUME_ID,
> -                                pvd.volume);
> -    if (pvd.system[0] != 0)
> +                                data->pvd.volume);
> +    if (data->pvd.system[0] != 0)
>          osinfo_entity_set_param(OSINFO_ENTITY(ret),
>                                  OSINFO_MEDIA_PROP_SYSTEM_ID,
> -                                pvd.system);
> -    if (pvd.publisher[0] != 0)
> +                                data->pvd.system);
> +    if (data->pvd.publisher[0] != 0)
>          osinfo_entity_set_param(OSINFO_ENTITY(ret),
>                                  OSINFO_MEDIA_PROP_PUBLISHER_ID,
> -                                pvd.publisher);
> +                                data->pvd.publisher);
>  
>  EXIT:
> +    if (error != NULL)
> +        g_simple_async_result_take_error(data->res, error);
> +    else
> +        g_simple_async_result_set_op_res_gpointer(data->res, ret, NULL);
> +    g_simple_async_result_complete (data->res);
> +
>      g_object_unref(stream);
> -    g_object_unref(file);
> +    create_from_location_async_data_free(data);
> +}
>  
> -    return ret;
> +static void on_pvd_read (GObject *source,
> +                         GAsyncResult *res,
> +                         gpointer user_data)
> +{
> +    GInputStream *stream = G_INPUT_STREAM(source);
> +    CreateFromLocationAsyncData *data;
> +    GError *error = NULL;
> +
> +    data = (CreateFromLocationAsyncData *)user_data;
> +
> +    if (g_input_stream_read_finish(stream,
> +                                   res,
> +                                   &error) < sizeof(data->pvd)) {
> +        if (error)
> +            g_prefix_error(&error, "Failed to read primary volume descriptor");
> +        else
> +            g_set_error(&error,
> +                        OSINFO_MEDIA_ERROR,
> +                        OSINFO_MEDIA_ERROR_NO_PVD,
> +                        "Primary volume descriptor unavailable");
> +
> +        goto ON_ERROR;
> +    }
> +
> +    data->pvd.volume[MAX_VOLUME - 1] = 0;
> +    data->pvd.system[MAX_SYSTEM - 1] = 0;
> +    data->pvd.publisher[MAX_PUBLISHER - 1] = 0;
> +
> +    if (data->pvd.volume[0] &&
> +        (data->pvd.system[0] == 0 && data->pvd.publisher[0] == 0)) {
> +        g_set_error(&error,
> +                    OSINFO_MEDIA_ERROR,
> +                    OSINFO_MEDIA_ERROR_INSUFFIENT_METADATA,
> +                    "Insufficient metadata on installation media");
> +
> +        goto ON_ERROR;
> +    }
> +
> +    g_input_stream_read_async(stream,
> +                              &data->svd,
> +                              sizeof(data->svd),
> +                              data->priority,
> +                              data->cancellable,
> +                              on_svd_read,
> +                              data);
> +    return;
> +
> +ON_ERROR:
> +    g_simple_async_result_take_error(data->res, error);
> +    g_simple_async_result_complete (data->res);
> +    create_from_location_async_data_free(data);
> +}
> +
> +static void on_location_skipped(GObject *source,
> +                                GAsyncResult *res,
> +                                gpointer user_data)
> +{
> +    GInputStream *stream = G_INPUT_STREAM(source);
> +    CreateFromLocationAsyncData *data;
> +    GError *error = NULL;
> +
> +    data = (CreateFromLocationAsyncData *)user_data;
> +
> +    if (g_input_stream_skip_finish(stream, res, &error) < PVD_OFFSET) {
> +        if (error)
> +            g_prefix_error(&error, "Failed to skip %d bytes", PVD_OFFSET);
> +        else
> +            g_set_error(&error,
> +                         OSINFO_MEDIA_ERROR,
> +                         OSINFO_MEDIA_ERROR_NO_DESCRIPTORS,
> +                         "No volume descriptors");
> +        g_simple_async_result_take_error(data->res, error);
> +        g_simple_async_result_complete (data->res);
> +        create_from_location_async_data_free(data);
> +
> +        return;
> +    }
> +
> +    g_input_stream_read_async(stream,
> +                              &data->pvd,
> +                              sizeof(data->pvd),
> +                              data->priority,
> +                              data->cancellable,
> +                              on_pvd_read,
> +                              data);
> +}
> +
> +static void on_location_read(GObject *source,
> +                             GAsyncResult *res,
> +                             gpointer user_data)
> +{
> +    GFileInputStream *stream;
> +    CreateFromLocationAsyncData *data;
> +    GError *error = NULL;
> +
> +    data = (CreateFromLocationAsyncData *)user_data;
> +
> +    stream = g_file_read_finish(G_FILE(source), res, &error);
> +    if (error != NULL) {
> +        g_prefix_error(&error, "Failed to open file");
> +        g_simple_async_result_take_error(data->res, error);
> +        g_simple_async_result_complete (data->res);
> +        create_from_location_async_data_free(data);
> +
> +        return;
> +    }
> +
> +    g_input_stream_skip_async(G_INPUT_STREAM(stream),
> +                              PVD_OFFSET,
> +                              data->priority,
> +                              data->cancellable,
> +                              on_location_skipped,
> +                              data);
> +}
> +
> +/**
> + * osinfo_media_create_from_location_async:
> + * @location: the location of an installation media
> + * @priority: the I/O priority of the request
> + * @cancellable (allow-none): a #GCancellable, or %NULL
> + * @callback: Function to call when result of this call is ready
> + * @user_data: The user data to pass to @callback, or %NULL
> + *
> + * Asynchronous variant of #osinfo_media_create_from_location.
> + */
> +void osinfo_media_create_from_location_async(const gchar *location,
> +                                             gint priority,
> +                                             GCancellable *cancellable,
> +                                             GAsyncReadyCallback callback,
> +                                             gpointer user_data)
> +{
> +    CreateFromLocationAsyncData *data;
> +
> +    g_return_if_fail(location != NULL);
> +
> +    data = g_slice_new0(CreateFromLocationAsyncData);
> +    data->res = g_simple_async_result_new
> +                                (NULL,
> +                                 callback,
> +                                 user_data,
> +                                 osinfo_media_create_from_location_async);
> +    data->file = g_file_new_for_commandline_arg(location);
> +    data->priority = priority;
> +    data->cancellable = cancellable;
> +    g_file_read_async(data->file,
> +                      priority,
> +                      cancellable,
> +                      on_location_read,
> +                      data);
> +}
> +
> +/**
> + * osinfo_media_create_from_location_finish:
> + * @res: a #GAsyncResult
> + * @error: The location where to store any error, or %NULL
> + *
> + * Finishes an asynchronous media object creation process started with
> + * #osinfo_media_create_from_location_async.
> + *
> + * Returns: (transfer full): a new #OsinfoMedia , or NULL on error
> + */
> +OsinfoMedia *osinfo_media_create_from_location_finish(GAsyncResult *res,
> +                                                      GError **error)
> +{
> +    GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
> +
> +    g_return_val_if_fail(error == NULL || *error == NULL, NULL);
> +
> +    if (g_simple_async_result_propagate_error(simple, error))
> +        return NULL;
> +
> +    return g_simple_async_result_get_op_res_gpointer(simple);
>  }
>  
>  /**
> diff --git a/osinfo/osinfo_media.h b/osinfo/osinfo_media.h
> index 89eb1f6..9ecf0b1 100644
> --- a/osinfo/osinfo_media.h
> +++ b/osinfo/osinfo_media.h
> @@ -101,6 +101,13 @@ OsinfoMedia *osinfo_media_new(const gchar *id, const gchar *architecture);
>  OsinfoMedia *osinfo_media_create_from_location(const gchar *location,
>                                                 GCancellable *cancellable,
>                                                 GError **error);
> +void osinfo_media_create_from_location_async(const gchar *location,
> +                                             gint priority,
> +                                             GCancellable *cancellable,
> +                                             GAsyncReadyCallback callback,
> +                                             gpointer user_data);
> +OsinfoMedia *osinfo_media_create_from_location_finish(GAsyncResult *res,
> +                                                      GError **error);
>  
>  const gchar *osinfo_media_get_architecture(OsinfoMedia *media);
>  const gchar *osinfo_media_get_url(OsinfoMedia *media);

The async code is rather complicated, and would perhaps be better by
just using g_simple_async_result_run_in_thread.  This does not impact
on the API design though, so we can go for the proposed code in this
mail, and revisit this in the future if we feel the need.

ACK


Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the virt-tools-list mailing list