deltas: Drop async content writes
This caused deadlocks and/or EMFILE due to the interaction between threads and fds. What we really want here is a better pull-based model for parsing content objects. Another idea would be to change static deltas so that content objects have a special opcode that includes their metadata first, and then do rollsums etc. only over actual content.
This commit is contained in:
parent
d49fc876bb
commit
6d1de23f87
|
|
@ -25,6 +25,7 @@
|
||||||
#include <glib-unix.h>
|
#include <glib-unix.h>
|
||||||
#include <gio/gunixinputstream.h>
|
#include <gio/gunixinputstream.h>
|
||||||
#include <gio/gunixoutputstream.h>
|
#include <gio/gunixoutputstream.h>
|
||||||
|
#include <gio/gfiledescriptorbased.h>
|
||||||
|
|
||||||
#include "ostree-repo-private.h"
|
#include "ostree-repo-private.h"
|
||||||
#include "ostree-repo-static-delta-private.h"
|
#include "ostree-repo-static-delta-private.h"
|
||||||
|
|
@ -45,8 +46,6 @@ typedef struct {
|
||||||
guint oplen;
|
guint oplen;
|
||||||
|
|
||||||
gboolean object_start;
|
gboolean object_start;
|
||||||
guint outstanding_content_writes;
|
|
||||||
GMainContext *content_writing_context;
|
|
||||||
gboolean caught_error;
|
gboolean caught_error;
|
||||||
GError **async_error;
|
GError **async_error;
|
||||||
|
|
||||||
|
|
@ -93,11 +92,6 @@ static OstreeStaticDeltaOperation op_dispatch_table[] = {
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
|
||||||
on_content_written (GObject *src,
|
|
||||||
GAsyncResult *result,
|
|
||||||
gpointer user_data);
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
read_varuint64 (StaticDeltaExecutionState *state,
|
read_varuint64 (StaticDeltaExecutionState *state,
|
||||||
guint64 *out_value,
|
guint64 *out_value,
|
||||||
|
|
@ -146,43 +140,10 @@ open_output_target (StaticDeltaExecutionState *state,
|
||||||
if (!read_varuint64 (state, &object_size, error))
|
if (!read_varuint64 (state, &object_size, error))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (OSTREE_OBJECT_TYPE_IS_META (state->output_objtype))
|
if (!gs_file_open_in_tmpdir_at (state->repo->tmp_dir_fd, 0644,
|
||||||
{
|
&state->output_tmp_path, &state->output_tmp_stream,
|
||||||
if (!gs_file_open_in_tmpdir_at (state->repo->tmp_dir_fd, 0644,
|
cancellable, error))
|
||||||
&state->output_tmp_path, &state->output_tmp_stream,
|
goto out;
|
||||||
cancellable, error))
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int pipefds[2];
|
|
||||||
|
|
||||||
if (!g_unix_open_pipe (pipefds, FD_CLOEXEC, error))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
content_in_stream = g_unix_input_stream_new (pipefds[0], TRUE);
|
|
||||||
state->output_tmp_stream = g_unix_output_stream_new (pipefds[1], TRUE);
|
|
||||||
|
|
||||||
if (!state->content_writing_context)
|
|
||||||
state->content_writing_context = g_main_context_new();
|
|
||||||
|
|
||||||
g_main_context_push_thread_default (state->content_writing_context);
|
|
||||||
|
|
||||||
{
|
|
||||||
StaticDeltaContentWrite *writedata = g_new0 (StaticDeltaContentWrite, 1);
|
|
||||||
writedata->state = state;
|
|
||||||
memcpy (writedata->checksum, checksum, sizeof (writedata->checksum));
|
|
||||||
ostree_repo_write_content_async (state->repo, checksum,
|
|
||||||
content_in_stream,
|
|
||||||
object_size,
|
|
||||||
cancellable,
|
|
||||||
on_content_written,
|
|
||||||
writedata);
|
|
||||||
}
|
|
||||||
state->outstanding_content_writes++;
|
|
||||||
|
|
||||||
g_main_context_pop_thread_default (state->content_writing_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
out:
|
out:
|
||||||
|
|
@ -265,11 +226,6 @@ _ostree_static_delta_part_execute_raw (OstreeRepo *repo,
|
||||||
guint8 opcode;
|
guint8 opcode;
|
||||||
OstreeStaticDeltaOperation *op;
|
OstreeStaticDeltaOperation *op;
|
||||||
|
|
||||||
/* Limit the number of outstanding writes to 1 to prevent too many open files
|
|
||||||
at the same time. */
|
|
||||||
while (state->outstanding_content_writes > 1)
|
|
||||||
g_main_context_iteration (state->content_writing_context, TRUE);
|
|
||||||
|
|
||||||
if (state->object_start)
|
if (state->object_start)
|
||||||
{
|
{
|
||||||
if (!open_output_target (state, cancellable, error))
|
if (!open_output_target (state, cancellable, error))
|
||||||
|
|
@ -294,15 +250,11 @@ _ostree_static_delta_part_execute_raw (OstreeRepo *repo,
|
||||||
n_executed++;
|
n_executed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (state->outstanding_content_writes > 0)
|
|
||||||
g_main_context_iteration (state->content_writing_context, TRUE);
|
|
||||||
|
|
||||||
if (state->caught_error)
|
if (state->caught_error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
out:
|
out:
|
||||||
g_clear_pointer (&state->content_writing_context, g_main_context_unref);
|
|
||||||
g_clear_pointer (&state->output_tmp_path, g_free);
|
g_clear_pointer (&state->output_tmp_path, g_free);
|
||||||
g_clear_object (&state->output_tmp_stream);
|
g_clear_object (&state->output_tmp_stream);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -496,38 +448,6 @@ validate_ofs (StaticDeltaExecutionState *state,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
on_content_written (GObject *src,
|
|
||||||
GAsyncResult *result,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
StaticDeltaContentWrite *writedata = user_data;
|
|
||||||
StaticDeltaExecutionState *state = writedata->state;
|
|
||||||
GError *local_error = NULL;
|
|
||||||
GError **error = &local_error;
|
|
||||||
|
|
||||||
if (!ostree_repo_write_content_finish ((OstreeRepo*)src, result, NULL, error))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
out:
|
|
||||||
state->outstanding_content_writes--;
|
|
||||||
if (state->outstanding_content_writes == 0)
|
|
||||||
g_main_context_wakeup (state->content_writing_context);
|
|
||||||
if (local_error)
|
|
||||||
{
|
|
||||||
if (!state->caught_error)
|
|
||||||
{
|
|
||||||
state->caught_error = TRUE;
|
|
||||||
g_main_context_wakeup (state->content_writing_context);
|
|
||||||
g_propagate_error (state->async_error, local_error);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_error_free (local_error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
dispatch_write (OstreeRepo *repo,
|
dispatch_write (OstreeRepo *repo,
|
||||||
StaticDeltaExecutionState *state,
|
StaticDeltaExecutionState *state,
|
||||||
|
|
@ -663,9 +583,29 @@ dispatch_close (OstreeRepo *repo,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* We already have an async write going, the close() above will
|
gs_unref_object GInputStream *instream = NULL;
|
||||||
* ensure it completes.
|
int fd;
|
||||||
*/
|
struct stat stbuf;
|
||||||
|
|
||||||
|
if (!ot_openat_read_stream (state->repo->tmp_dir_fd,
|
||||||
|
state->output_tmp_path, FALSE,
|
||||||
|
&instream, cancellable, error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
fd = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (instream));
|
||||||
|
if (fstat (fd, &stbuf) == -1)
|
||||||
|
{
|
||||||
|
gs_set_error_from_errno (error, errno);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now get rid of the temporary */
|
||||||
|
(void) unlinkat (state->repo->tmp_dir_fd, state->output_tmp_path, 0);
|
||||||
|
|
||||||
|
if (!ostree_repo_write_content (repo, tmp_checksum,
|
||||||
|
instream, stbuf.st_size,
|
||||||
|
NULL, cancellable, error))
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->output_target = NULL;
|
state->output_target = NULL;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue