sysroot: Add a try_lock() API
The blocking locking API wasn't sufficient for use in the rpm-ostree daemon; it really wants to know if the lock is held, then continue to do other things (like service DBus requests), and get notification when the lock is available. We also add an async variant that can be called if the lock is not available. Implement a higher level "loop until lock is available" method in the `ostree admin` commandline.
This commit is contained in:
parent
d0edf63346
commit
2dfe24632a
|
|
@ -368,6 +368,9 @@ ostree_sysroot_new_default
|
|||
ostree_sysroot_get_path
|
||||
ostree_sysroot_load
|
||||
ostree_sysroot_lock
|
||||
ostree_sysroot_try_lock
|
||||
ostree_sysroot_lock_async
|
||||
ostree_sysroot_lock_finish
|
||||
ostree_sysroot_unlock
|
||||
ostree_sysroot_get_fd
|
||||
ostree_sysroot_ensure_initialized
|
||||
|
|
|
|||
2
libglnx
2
libglnx
|
|
@ -1 +1 @@
|
|||
Subproject commit cf8ae27bab717621f1edf8ab6ae3dd89da4d8d4f
|
||||
Subproject commit 900b25f7018878ab64bd04751d8f15c6d83ba823
|
||||
|
|
@ -1170,6 +1170,55 @@ ostree_sysroot_lock (OstreeSysroot *self,
|
|||
LOCK_EX, &self->lock, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_try_lock:
|
||||
* @self: Self
|
||||
* @out_acquired: (out): Whether or not the lock has been acquired
|
||||
* @error: Error
|
||||
*
|
||||
* Try to acquire an exclusive multi-process write lock for @self. If
|
||||
* another process holds the lock, this function will return
|
||||
* immediately, setting @out_acquired to %FALSE, and returning %TRUE
|
||||
* (and no error).
|
||||
*
|
||||
* Release the lock with ostree_sysroot_unlock(). The lock will also
|
||||
* be released if @self is deallocated.
|
||||
*/
|
||||
gboolean
|
||||
ostree_sysroot_try_lock (OstreeSysroot *self,
|
||||
gboolean *out_acquired,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GError *local_error = NULL;
|
||||
|
||||
if (!ensure_sysroot_fd (self, error))
|
||||
goto out;
|
||||
|
||||
/* Note use of LOCK_NB */
|
||||
if (!glnx_make_lock_file (self->sysroot_fd, OSTREE_SYSROOT_LOCKFILE,
|
||||
LOCK_EX | LOCK_NB, &self->lock, &local_error))
|
||||
{
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
|
||||
{
|
||||
*out_acquired = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, local_error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*out_acquired = TRUE;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_unlock:
|
||||
* @self: Self
|
||||
|
|
@ -1184,6 +1233,64 @@ ostree_sysroot_unlock (OstreeSysroot *self)
|
|||
glnx_release_lock_file (&self->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
lock_in_thread (GTask *task,
|
||||
gpointer source,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GError *local_error = NULL;
|
||||
OstreeSysroot *self = source;
|
||||
|
||||
if (!ostree_sysroot_lock (self, &local_error))
|
||||
goto out;
|
||||
|
||||
if (g_cancellable_set_error_if_cancelled (cancellable, &local_error))
|
||||
ostree_sysroot_unlock (self);
|
||||
|
||||
out:
|
||||
if (local_error)
|
||||
g_task_return_error (task, local_error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_lock_async:
|
||||
* @self: Self
|
||||
* @cancellable: Cancellable
|
||||
* @callback: Callback
|
||||
* @user_data: User data
|
||||
*
|
||||
* An asynchronous version of ostree_sysroot_lock().
|
||||
*/
|
||||
void
|
||||
ostree_sysroot_lock_async (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
|
||||
g_task_run_in_thread (task, lock_in_thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_lock_finish:
|
||||
* @self: Self
|
||||
* @result: Result
|
||||
* @error: Error
|
||||
*
|
||||
* Call when ostree_sysroot_lock_async() is ready.
|
||||
*/
|
||||
gboolean
|
||||
ostree_sysroot_lock_finish (OstreeSysroot *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
|
||||
return g_task_propagate_boolean ((GTask*)result, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_simple_write_deployment:
|
||||
* @sysroot: Sysroot
|
||||
|
|
|
|||
|
|
@ -63,6 +63,16 @@ char *ostree_sysroot_get_deployment_dirpath (OstreeSysroot *self,
|
|||
GFile * ostree_sysroot_get_deployment_origin_path (GFile *deployment_path);
|
||||
|
||||
gboolean ostree_sysroot_lock (OstreeSysroot *self, GError **error);
|
||||
gboolean ostree_sysroot_try_lock (OstreeSysroot *self,
|
||||
gboolean *out_acquired,
|
||||
GError **error);
|
||||
void ostree_sysroot_lock_async (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean ostree_sysroot_lock_finish (OstreeSysroot *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
void ostree_sysroot_unlock (OstreeSysroot *self);
|
||||
|
||||
gboolean ostree_sysroot_cleanup (OstreeSysroot *self,
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro
|
|||
|
||||
refspec = argv[1];
|
||||
|
||||
if (!ostree_sysroot_lock (sysroot, error))
|
||||
if (!ot_admin_sysroot_lock (sysroot, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
|
|
|
|||
|
|
@ -96,3 +96,62 @@ ot_admin_get_indexed_deployment (OstreeSysroot *sysroot,
|
|||
|
||||
return g_object_ref (current_deployments->pdata[index]);
|
||||
}
|
||||
|
||||
struct ContextState {
|
||||
GMainContext *mainctx;
|
||||
gboolean running;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
on_sysroot_lock_timeout (gpointer user_data)
|
||||
{
|
||||
g_print ("Waiting for sysroot lock...\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_sysroot_lock_acquired (OstreeSysroot *sysroot,
|
||||
GAsyncResult *result,
|
||||
struct ContextState *state)
|
||||
{
|
||||
state->running = FALSE;
|
||||
g_main_context_wakeup (state->mainctx);
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_admin_sysroot_lock (OstreeSysroot *sysroot,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gboolean acquired;
|
||||
struct ContextState state = {
|
||||
.mainctx = g_main_context_new (),
|
||||
.running = TRUE,
|
||||
};
|
||||
|
||||
g_main_context_push_thread_default (state.mainctx);
|
||||
|
||||
if (!ostree_sysroot_try_lock (sysroot, &acquired, error))
|
||||
goto out;
|
||||
|
||||
if (!acquired)
|
||||
{
|
||||
GSource *timeout_src = g_timeout_source_new_seconds (3);
|
||||
g_source_set_callback (timeout_src, (GSourceFunc)on_sysroot_lock_timeout, &state, NULL);
|
||||
g_source_attach (timeout_src, state.mainctx);
|
||||
g_source_unref (timeout_src);
|
||||
|
||||
on_sysroot_lock_timeout (&state);
|
||||
|
||||
ostree_sysroot_lock_async (sysroot, NULL, (GAsyncReadyCallback)on_sysroot_lock_acquired, &state);
|
||||
|
||||
while (state.running)
|
||||
g_main_context_iteration (state.mainctx, TRUE);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
g_main_context_pop_thread_default (state.mainctx);
|
||||
g_main_context_unref (state.mainctx);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,5 +41,9 @@ ot_admin_get_indexed_deployment (OstreeSysroot *sysroot,
|
|||
int index,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
ot_admin_sysroot_lock (OstreeSysroot *sysroot,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
|||
Loading…
Reference in New Issue