pull: Stop using GMainLoop
First of all, what we were doing with having GMainLoop in the internal APIs is wrong. Synchronous APIs should always create their own main context and not iterate the caller's. Doing the latter creates potential for evil reentrancy issues. Sync API should block, async API is for not blocking. Now that's out of the way, fix the pull code to do the clean ``` while (termination_condition (state)) g_main_context_iteration (mainctx, TRUE); ``` model for looping. This is a lot easier to understand and ultimately more reliable than having other code call `g_main_loop_quit()`, as the loop condition is in exactly one place. We can also remove the idle source which only fired once. Note we have to add a hack here to discard the synchronous session and create a new one which we only use async. https://bugzilla.gnome.org/show_bug.cgi?id=753336
This commit is contained in:
1 parent
5c20ea920e
commit
9f3d586993
6 files changed
+133
-243
No files matched your search
+87
-183
@@ -56,7 +56,7 @@ typedef struct
|
||||
{
|
||||
OstreeMetalink *metalink;
|
||||
|
||||
GTask *task;
|
||||
GCancellable *cancellable;
|
||||
GMarkupParseContext *parser;
|
||||
|
||||
guint passthrough_depth;
|
||||
@@ -110,8 +110,7 @@ metalink_parser_start (GMarkupParseContext *context,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
GTask *task = user_data;
|
||||
OstreeMetalinkRequest *self = g_task_get_task_data (task);
|
||||
OstreeMetalinkRequest *self = user_data;
|
||||
|
||||
switch (self->state)
|
||||
{
|
||||
@@ -273,8 +272,7 @@ metalink_parser_end (GMarkupParseContext *context,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
GTask *task = user_data;
|
||||
OstreeMetalinkRequest *self = g_task_get_task_data (task);
|
||||
OstreeMetalinkRequest *self = user_data;
|
||||
|
||||
switch (self->state)
|
||||
{
|
||||
@@ -316,8 +314,7 @@ metalink_parser_text (GMarkupParseContext *context,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
GTask *task = user_data;
|
||||
OstreeMetalinkRequest *self = g_task_get_task_data (task);
|
||||
OstreeMetalinkRequest *self = user_data;
|
||||
|
||||
switch (self->state)
|
||||
{
|
||||
@@ -414,9 +411,6 @@ _ostree_metalink_new (OstreeFetcher *fetcher,
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
try_next_url (OstreeMetalinkRequest *self);
|
||||
|
||||
static gboolean
|
||||
valid_hex_checksum (const char *s, gsize expected_len)
|
||||
{
|
||||
@@ -425,47 +419,30 @@ valid_hex_checksum (const char *s, gsize expected_len)
|
||||
return len == expected_len && s[len] == '\0';
|
||||
}
|
||||
|
||||
static void
|
||||
on_fetched_url (GObject *src,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
static gboolean
|
||||
try_one_url (OstreeMetalinkRequest *self,
|
||||
SoupURI *uri,
|
||||
GBytes **out_data,
|
||||
GError **error)
|
||||
{
|
||||
GTask *task = user_data;
|
||||
GCancellable *cancellable;
|
||||
OstreeMetalinkRequest *self = g_task_get_task_data (task);
|
||||
GError *local_error = NULL;
|
||||
int parent_dfd = _ostree_fetcher_get_dfd (self->metalink->fetcher);
|
||||
g_autoptr(GInputStream) instream = NULL;
|
||||
g_autoptr(GOutputStream) outstream = NULL;
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GBytes) bytes = NULL;
|
||||
g_autofree char *path = NULL;
|
||||
gssize n_bytes;
|
||||
|
||||
path = _ostree_fetcher_request_uri_with_partial_finish ((OstreeFetcher*)src, res, &local_error);
|
||||
if (!path)
|
||||
if (!_ostree_fetcher_request_uri_to_membuf (self->metalink->fetcher,
|
||||
uri,
|
||||
FALSE,
|
||||
FALSE,
|
||||
&bytes,
|
||||
self->metalink->max_size,
|
||||
self->cancellable,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
cancellable = g_task_get_cancellable (task);
|
||||
|
||||
if (!ot_openat_read_stream (parent_dfd, path, FALSE, &instream,
|
||||
cancellable, &local_error))
|
||||
goto out;
|
||||
|
||||
outstream = g_memory_output_stream_new_resizable ();
|
||||
|
||||
n_bytes = g_output_stream_splice (outstream, instream,
|
||||
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
|
||||
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
|
||||
cancellable, &local_error);
|
||||
|
||||
if (n_bytes < 0)
|
||||
goto out;
|
||||
|
||||
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (outstream));
|
||||
|
||||
n_bytes = g_bytes_get_size (bytes);
|
||||
if (n_bytes != self->size)
|
||||
{
|
||||
g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expected size is %" G_GUINT64_FORMAT " bytes but content is %" G_GSSIZE_FORMAT " bytes",
|
||||
self->size, n_bytes);
|
||||
goto out;
|
||||
@@ -479,7 +456,7 @@ on_fetched_url (GObject *src,
|
||||
|
||||
if (strcmp (self->verification_sha512, actual) != 0)
|
||||
{
|
||||
g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expected checksum is %s but actual is %s",
|
||||
self->verification_sha512, actual);
|
||||
goto out;
|
||||
@@ -493,57 +470,28 @@ on_fetched_url (GObject *src,
|
||||
|
||||
if (strcmp (self->verification_sha256, actual) != 0)
|
||||
{
|
||||
g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expected checksum is %s but actual is %s",
|
||||
self->verification_sha256, actual);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
if (out_data)
|
||||
*out_data = g_bytes_ref (bytes);
|
||||
out:
|
||||
if (local_error)
|
||||
{
|
||||
g_free (self->last_metalink_error);
|
||||
self->last_metalink_error = g_strdup (local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
|
||||
/* And here we iterate on the next one if we hit an error */
|
||||
self->current_url_index++;
|
||||
try_next_url (self);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->result = g_bytes_ref (bytes);
|
||||
g_task_return_boolean (self->task, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
try_next_url (OstreeMetalinkRequest *self)
|
||||
{
|
||||
if (self->current_url_index >= self->urls->len)
|
||||
{
|
||||
g_task_return_new_error (self->task, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Exhausted %u metalink targets, last error: %s",
|
||||
self->urls->len, self->last_metalink_error);
|
||||
}
|
||||
else
|
||||
{
|
||||
SoupURI *next = self->urls->pdata[self->current_url_index];
|
||||
|
||||
_ostree_fetcher_request_uri_with_partial_async (self->metalink->fetcher, next,
|
||||
self->metalink->max_size,
|
||||
OSTREE_FETCHER_DEFAULT_PRIORITY,
|
||||
g_task_get_cancellable (self->task),
|
||||
on_fetched_url, self->task);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
start_target_request_phase (OstreeMetalinkRequest *self,
|
||||
GError **error)
|
||||
try_metalink_targets (OstreeMetalinkRequest *self,
|
||||
SoupURI **out_target_uri,
|
||||
GBytes **out_data,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
SoupURI *target_uri;
|
||||
|
||||
if (!self->found_a_file_element)
|
||||
{
|
||||
@@ -590,24 +538,40 @@ start_target_request_phase (OstreeMetalinkRequest *self,
|
||||
goto out;
|
||||
}
|
||||
|
||||
try_next_url (self);
|
||||
for (self->current_url_index = 0;
|
||||
self->current_url_index < self->urls->len;
|
||||
self->current_url_index++)
|
||||
{
|
||||
GError *temp_error = NULL;
|
||||
|
||||
target_uri = self->urls->pdata[self->current_url_index];
|
||||
|
||||
if (try_one_url (self, target_uri, out_data, &temp_error))
|
||||
break;
|
||||
else
|
||||
{
|
||||
g_free (self->last_metalink_error);
|
||||
self->last_metalink_error = g_strdup (temp_error->message);
|
||||
g_clear_error (&temp_error);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->current_url_index >= self->urls->len)
|
||||
{
|
||||
g_assert (self->last_metalink_error != NULL);
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Exhausted %u metalink targets, last error: %s",
|
||||
self->urls->len, self->last_metalink_error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
if (out_target_uri)
|
||||
*out_target_uri = soup_uri_copy (target_uri);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_metalink_request_unref (gpointer data)
|
||||
{
|
||||
OstreeMetalinkRequest *request = data;
|
||||
g_object_unref (request->metalink);
|
||||
g_clear_pointer (&request->result, g_bytes_unref);
|
||||
g_free (request->last_metalink_error);
|
||||
g_ptr_array_unref (request->urls);
|
||||
g_free (request);
|
||||
}
|
||||
|
||||
static const GMarkupParser metalink_parser = {
|
||||
metalink_parser_start,
|
||||
metalink_parser_end,
|
||||
@@ -625,115 +589,55 @@ typedef struct
|
||||
GMainLoop *loop;
|
||||
} FetchMetalinkSyncData;
|
||||
|
||||
static gboolean
|
||||
ostree_metalink_request_finish (OstreeMetalink *self,
|
||||
GAsyncResult *result,
|
||||
SoupURI **out_target_uri,
|
||||
GBytes **out_data,
|
||||
GError **error)
|
||||
{
|
||||
OstreeMetalinkRequest *request;
|
||||
|
||||
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
|
||||
|
||||
request = g_task_get_task_data ((GTask*)result);
|
||||
|
||||
if (g_task_propagate_boolean ((GTask*)result, error))
|
||||
{
|
||||
g_assert_cmpint (request->current_url_index, <, request->urls->len);
|
||||
if (out_target_uri != NULL)
|
||||
*out_target_uri = request->urls->pdata[request->current_url_index];
|
||||
if (out_data != NULL)
|
||||
*out_data = g_bytes_ref (request->result);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_metalink_fetched (GObject *src,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
FetchMetalinkSyncData *data = user_data;
|
||||
|
||||
data->success = ostree_metalink_request_finish ((OstreeMetalink*)src,
|
||||
result,
|
||||
data->out_target_uri,
|
||||
data->out_data,
|
||||
data->error);
|
||||
g_main_loop_quit (data->loop);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_metalink_bytes_read (OstreeMetalinkRequest *self,
|
||||
OstreeMetalinkRequest *request,
|
||||
FetchMetalinkSyncData *sync_data,
|
||||
GBytes *bytes,
|
||||
GError **error)
|
||||
{
|
||||
gsize len;
|
||||
const guint8 *data = g_bytes_get_data (bytes, &len);
|
||||
if (!g_markup_parse_context_parse (self->parser, (const char*)data, len, error))
|
||||
return FALSE;
|
||||
|
||||
if (!start_target_request_phase (self, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ostree_metalink_request_sync (OstreeMetalink *self,
|
||||
GMainLoop *loop,
|
||||
SoupURI **out_target_uri,
|
||||
GBytes **out_data,
|
||||
SoupURI **fetching_sync_uri,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
OstreeMetalinkRequest *request = g_new0 (OstreeMetalinkRequest, 1);
|
||||
FetchMetalinkSyncData data = { 0, };
|
||||
GTask *task = g_task_new (self, cancellable, on_metalink_fetched, &data);
|
||||
GBytes *out_contents = NULL;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
data.out_target_uri = out_target_uri;
|
||||
data.out_data = out_data;
|
||||
data.loop = loop;
|
||||
data.error = error;
|
||||
OstreeMetalinkRequest request = { 0, };
|
||||
g_autoptr(GMainContext) mainctx = NULL;
|
||||
GBytes *out_contents = NULL;
|
||||
gsize len;
|
||||
const guint8 *data;
|
||||
|
||||
if (fetching_sync_uri != NULL)
|
||||
*fetching_sync_uri = _ostree_metalink_get_uri (self);
|
||||
|
||||
request->metalink = g_object_ref (self);
|
||||
request->urls = g_ptr_array_new_with_free_func ((GDestroyNotify) soup_uri_free);
|
||||
request->task = task; /* Unowned */
|
||||
mainctx = g_main_context_new ();
|
||||
g_main_context_push_thread_default (mainctx);
|
||||
|
||||
request->parser = g_markup_parse_context_new (&metalink_parser, G_MARKUP_PREFIX_ERROR_POSITION, task, NULL);
|
||||
request.metalink = g_object_ref (self);
|
||||
request.urls = g_ptr_array_new_with_free_func ((GDestroyNotify) soup_uri_free);
|
||||
request.parser = g_markup_parse_context_new (&metalink_parser, G_MARKUP_PREFIX_ERROR_POSITION, &request, NULL);
|
||||
|
||||
g_task_set_task_data (task, request, ostree_metalink_request_unref);
|
||||
|
||||
if (! _ostree_fetcher_request_uri_to_membuf (self->fetcher,
|
||||
self->uri,
|
||||
FALSE,
|
||||
FALSE,
|
||||
&out_contents,
|
||||
loop,
|
||||
self->max_size,
|
||||
cancellable,
|
||||
error))
|
||||
if (!_ostree_fetcher_request_uri_to_membuf (self->fetcher,
|
||||
self->uri,
|
||||
FALSE,
|
||||
FALSE,
|
||||
&out_contents,
|
||||
self->max_size,
|
||||
cancellable,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
if (! on_metalink_bytes_read (request, request, &data, out_contents, error))
|
||||
data = g_bytes_get_data (out_contents, &len);
|
||||
if (!g_markup_parse_context_parse (request.parser, (const char*)data, len, error))
|
||||
goto out;
|
||||
|
||||
g_main_loop_run (data.loop);
|
||||
|
||||
ret = data.success;
|
||||
if (!try_metalink_targets (&request, out_target_uri, out_data, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (mainctx)
|
||||
g_main_context_pop_thread_default (mainctx);
|
||||
g_clear_object (&request.metalink);
|
||||
g_clear_pointer (&request.urls, g_ptr_array_unref);
|
||||
g_clear_pointer (&request.parser, g_markup_parse_context_free);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user