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:
Colin Walters 2015-05-06 22:19:05 -04:00
parent d0edf63346
commit 2dfe24632a
7 changed files with 185 additions and 2 deletions

View File

@ -368,6 +368,9 @@ ostree_sysroot_new_default
ostree_sysroot_get_path ostree_sysroot_get_path
ostree_sysroot_load ostree_sysroot_load
ostree_sysroot_lock ostree_sysroot_lock
ostree_sysroot_try_lock
ostree_sysroot_lock_async
ostree_sysroot_lock_finish
ostree_sysroot_unlock ostree_sysroot_unlock
ostree_sysroot_get_fd ostree_sysroot_get_fd
ostree_sysroot_ensure_initialized ostree_sysroot_ensure_initialized

@ -1 +1 @@
Subproject commit cf8ae27bab717621f1edf8ab6ae3dd89da4d8d4f Subproject commit 900b25f7018878ab64bd04751d8f15c6d83ba823

View File

@ -1170,6 +1170,55 @@ ostree_sysroot_lock (OstreeSysroot *self,
LOCK_EX, &self->lock, error); 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: * ostree_sysroot_unlock:
* @self: Self * @self: Self
@ -1184,6 +1233,64 @@ ostree_sysroot_unlock (OstreeSysroot *self)
glnx_release_lock_file (&self->lock); 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: * ostree_sysroot_simple_write_deployment:
* @sysroot: Sysroot * @sysroot: Sysroot

View File

@ -63,6 +63,16 @@ char *ostree_sysroot_get_deployment_dirpath (OstreeSysroot *self,
GFile * ostree_sysroot_get_deployment_origin_path (GFile *deployment_path); GFile * ostree_sysroot_get_deployment_origin_path (GFile *deployment_path);
gboolean ostree_sysroot_lock (OstreeSysroot *self, GError **error); 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); void ostree_sysroot_unlock (OstreeSysroot *self);
gboolean ostree_sysroot_cleanup (OstreeSysroot *self, gboolean ostree_sysroot_cleanup (OstreeSysroot *self,

View File

@ -79,7 +79,7 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro
refspec = argv[1]; refspec = argv[1];
if (!ostree_sysroot_lock (sysroot, error)) if (!ot_admin_sysroot_lock (sysroot, error))
goto out; goto out;
if (!ostree_sysroot_load (sysroot, cancellable, error)) if (!ostree_sysroot_load (sysroot, cancellable, error))

View File

@ -96,3 +96,62 @@ ot_admin_get_indexed_deployment (OstreeSysroot *sysroot,
return g_object_ref (current_deployments->pdata[index]); 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;
}

View File

@ -41,5 +41,9 @@ ot_admin_get_indexed_deployment (OstreeSysroot *sysroot,
int index, int index,
GError **error); GError **error);
gboolean
ot_admin_sysroot_lock (OstreeSysroot *sysroot,
GError **error);
G_END_DECLS G_END_DECLS