2020-03-10 13:41:34 +02:00
|
|
|
|
/* WirePlumber
|
|
|
|
|
|
*
|
|
|
|
|
|
* Copyright © 2020 Collabora Ltd.
|
|
|
|
|
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
|
|
|
|
*
|
|
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2021-05-21 18:40:43 +03:00
|
|
|
|
#define G_LOG_DOMAIN "wp-transition"
|
|
|
|
|
|
|
|
|
|
|
|
#include "transition.h"
|
|
|
|
|
|
#include "log.h"
|
|
|
|
|
|
#include "error.h"
|
2021-05-13 17:54:58 +03:00
|
|
|
|
|
2021-05-21 18:40:43 +03:00
|
|
|
|
/*! \defgroup wptransition Transitions */
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \struct WpTransition
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* A transition is an asynchronous operation, like GTask, that contains an
|
|
|
|
|
|
* internal state machine, where a series of 'steps' are executed in order to
|
|
|
|
|
|
* complete the operation.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* For every step, _WpTransitionClass::get_next_step()
|
2021-05-13 17:54:58 +03:00
|
|
|
|
* is called in order to determine the next step to execute. Afterwards,
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* _WpTransitionClass::execute_step() is called
|
2020-03-10 13:41:34 +02:00
|
|
|
|
* to perform any actions necessary to complete this step. When execution
|
|
|
|
|
|
* of the step is done, the operation's code must call wp_transition_advance()
|
|
|
|
|
|
* in order to continue to the next step. If an error occurs, the operation's
|
|
|
|
|
|
* code must call wp_transition_return_error() instead, in which case the
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* transition completes immediately and wp_transition_had_error() returns TRUE.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
|
|
|
|
|
* Typically, every step will start an asynchronous operation. Although is is
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* possible, the WpTransition base class does not expect
|
|
|
|
|
|
* _WpTransitionClass::execute_step() to call wp_transition_advance() directly.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
* Instead, it is expected that wp_transition_advance() will be called from
|
|
|
|
|
|
* the callback that the step's asyncrhonous operation will call when it is
|
|
|
|
|
|
* completed.
|
2021-05-13 17:54:58 +03:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \gproperties
|
|
|
|
|
|
|
|
|
|
|
|
* \gproperty{completed, gboolean, G_PARAM_READABLE,
|
|
|
|
|
|
* Whether the transition has completed\, meaning its callback (if set)
|
|
|
|
|
|
* has been invoked. This can only happen after the final step has been
|
|
|
|
|
|
* reached or wp_transition_return_error() has been called.
|
|
|
|
|
|
* \n
|
|
|
|
|
|
* This property is guaranteed to change from FALSE to TRUE exactly once.
|
|
|
|
|
|
* \n
|
|
|
|
|
|
* The GObject \c notify signal for this change is emitted in the same context
|
|
|
|
|
|
* as the transition’s callback\, immediately after that callback is invoked.}
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct _WpTransitionPrivate WpTransitionPrivate;
|
|
|
|
|
|
struct _WpTransitionPrivate
|
|
|
|
|
|
{
|
|
|
|
|
|
/* source obj & callback */
|
|
|
|
|
|
GObject *source_object;
|
|
|
|
|
|
GCancellable *cancellable;
|
2020-05-08 19:23:23 +03:00
|
|
|
|
GClosure *closure;
|
2020-03-10 13:41:34 +02:00
|
|
|
|
|
|
|
|
|
|
/* GAsyncResult tag */
|
|
|
|
|
|
gpointer tag;
|
|
|
|
|
|
|
|
|
|
|
|
/* task data */
|
|
|
|
|
|
gpointer data;
|
|
|
|
|
|
GDestroyNotify data_destroy;
|
|
|
|
|
|
|
|
|
|
|
|
/* state machine */
|
|
|
|
|
|
guint step;
|
|
|
|
|
|
GError *error;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
|
PROP_0,
|
|
|
|
|
|
PROP_COMPLETED,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void wp_transition_async_result_init (GAsyncResultIface *iface);
|
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (WpTransition, wp_transition, G_TYPE_OBJECT,
|
|
|
|
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, wp_transition_async_result_init)
|
|
|
|
|
|
G_ADD_PRIVATE (WpTransition))
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wp_transition_init (WpTransition * self)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wp_transition_finalize (GObject * object)
|
|
|
|
|
|
{
|
|
|
|
|
|
WpTransition * self = WP_TRANSITION (object);
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
|
|
|
|
|
|
if (priv->data && priv->data_destroy)
|
|
|
|
|
|
priv->data_destroy (priv->data);
|
|
|
|
|
|
|
|
|
|
|
|
g_clear_error (&priv->error);
|
2020-05-08 19:23:23 +03:00
|
|
|
|
g_clear_pointer (&priv->closure, g_closure_unref);
|
2020-03-10 13:41:34 +02:00
|
|
|
|
g_clear_object (&priv->cancellable);
|
|
|
|
|
|
g_clear_object (&priv->source_object);
|
|
|
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (wp_transition_parent_class)->finalize (object);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wp_transition_get_property (GObject * object, guint property_id,
|
|
|
|
|
|
GValue * value, GParamSpec * pspec)
|
|
|
|
|
|
{
|
|
|
|
|
|
WpTransition * self = WP_TRANSITION (object);
|
|
|
|
|
|
|
|
|
|
|
|
switch (property_id) {
|
|
|
|
|
|
case PROP_COMPLETED:
|
|
|
|
|
|
g_value_set_boolean (value, wp_transition_get_completed (self));
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wp_transition_class_init (WpTransitionClass * klass)
|
|
|
|
|
|
{
|
|
|
|
|
|
GObjectClass *object_class = (GObjectClass *) klass;
|
|
|
|
|
|
|
|
|
|
|
|
object_class->finalize = wp_transition_finalize;
|
|
|
|
|
|
object_class->get_property = wp_transition_get_property;
|
|
|
|
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class, PROP_COMPLETED,
|
|
|
|
|
|
g_param_spec_boolean ("completed", "completed",
|
|
|
|
|
|
"Whether the transition has completed", FALSE,
|
|
|
|
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static GObject *
|
|
|
|
|
|
get_source_object (GAsyncResult * res)
|
|
|
|
|
|
{
|
|
|
|
|
|
WpTransitionPrivate *priv =
|
|
|
|
|
|
wp_transition_get_instance_private (WP_TRANSITION (res));
|
|
|
|
|
|
return priv->source_object ? g_object_ref (priv->source_object) : NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wp_transition_async_result_init (GAsyncResultIface * iface)
|
|
|
|
|
|
{
|
|
|
|
|
|
iface->get_source_object = get_source_object;
|
|
|
|
|
|
iface->get_user_data =
|
|
|
|
|
|
(gpointer (*)(GAsyncResult *)) wp_transition_get_data;
|
|
|
|
|
|
iface->is_tagged =
|
|
|
|
|
|
(gboolean (*)(GAsyncResult *, gpointer)) wp_transition_is_tagged;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Creates a WpTransition acting on \a source_object.
|
2021-05-13 17:54:58 +03:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* When the transition is done, \a callback will be invoked.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
|
|
|
|
|
* The transition does not automatically start executing steps. You must
|
|
|
|
|
|
* call wp_transition_advance() after creating it in order to start it.
|
|
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \note The transition is automatically unref'ed after the \a callback
|
2020-03-10 13:41:34 +02:00
|
|
|
|
* has been executed. If you wish to keep an additional reference on it,
|
|
|
|
|
|
* you need to ref it explicitly.
|
|
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param type the GType of the WpTransition subclass to instantiate
|
|
|
|
|
|
* \param source_object (nullable) (type GObject): the GObject that owns this
|
|
|
|
|
|
* task, or NULL
|
|
|
|
|
|
* \param cancellable (nullable): optional GCancellable
|
|
|
|
|
|
* \param callback (scope async): a GAsyncReadyCallback
|
|
|
|
|
|
* \param callback_data (closure): user data passed to \a callback
|
|
|
|
|
|
* \returns (transfer none): the new transition
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
WpTransition *
|
|
|
|
|
|
wp_transition_new (GType type,
|
|
|
|
|
|
gpointer source_object, GCancellable * cancellable,
|
|
|
|
|
|
GAsyncReadyCallback callback, gpointer callback_data)
|
|
|
|
|
|
{
|
2020-05-08 19:23:23 +03:00
|
|
|
|
return wp_transition_new_closure (type, source_object, cancellable,
|
|
|
|
|
|
g_cclosure_new (G_CALLBACK (callback), callback_data, NULL));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Creates a WpTransition acting on \a source_object.
|
|
|
|
|
|
* When the transition is done, \a closure will be invoked.
|
2020-05-08 19:23:23 +03:00
|
|
|
|
*
|
|
|
|
|
|
* The transition does not automatically start executing steps. You must
|
|
|
|
|
|
* call wp_transition_advance() after creating it in order to start it.
|
|
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* Note that the transition is automatically unref'ed after the \a closure
|
2020-05-08 19:23:23 +03:00
|
|
|
|
* has been executed. If you wish to keep an additional reference on it,
|
|
|
|
|
|
* you need to ref it explicitly.
|
|
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param type the GType of the WpTransition subclass to instantiate
|
|
|
|
|
|
* \param source_object (nullable) (type GObject): the GObject that owns this
|
|
|
|
|
|
* task, or NULL
|
|
|
|
|
|
* \param cancellable (nullable): optional GCancellable
|
|
|
|
|
|
* \param closure (nullable): a GAsyncReadyCallback wrapped in a GClosure
|
|
|
|
|
|
* \returns (transfer none): the new transition
|
2020-05-08 19:23:23 +03:00
|
|
|
|
*/
|
|
|
|
|
|
WpTransition *
|
|
|
|
|
|
wp_transition_new_closure (GType type, gpointer source_object,
|
|
|
|
|
|
GCancellable * cancellable, GClosure * closure)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_val_if_fail (g_type_is_a (type, WP_TYPE_TRANSITION), NULL);
|
2020-03-10 13:41:34 +02:00
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
|
|
|
|
|
|
|
|
|
|
|
|
WpTransition *self = g_object_new (type, NULL);
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
|
|
|
|
|
|
priv->source_object = source_object ? g_object_ref (source_object) : NULL;
|
|
|
|
|
|
priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
|
2020-05-08 19:23:23 +03:00
|
|
|
|
|
|
|
|
|
|
if (closure) {
|
|
|
|
|
|
priv->closure = g_closure_ref (closure);
|
|
|
|
|
|
g_closure_sink (closure);
|
|
|
|
|
|
if (G_CLOSURE_NEEDS_MARSHAL (closure))
|
|
|
|
|
|
g_closure_set_marshal (closure, g_cclosure_marshal_VOID__OBJECT);
|
|
|
|
|
|
}
|
2020-03-10 13:41:34 +02:00
|
|
|
|
|
|
|
|
|
|
return self;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Gets the source object from the transition.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
|
|
|
|
|
* Like g_async_result_get_source_object(), but does not ref the object.
|
|
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \returns (transfer none) (type GObject): the source object
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
gpointer
|
|
|
|
|
|
wp_transition_get_source_object (WpTransition * self)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_TRANSITION (self), NULL);
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
return priv->source_object;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Checks if \a self has the given \a tag (generally a function pointer
|
|
|
|
|
|
* indicating the function \a self was created by).
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \param tag a tag
|
|
|
|
|
|
* \returns TRUE if \a self has the indicated \a tag , FALSE if not.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
gboolean
|
|
|
|
|
|
wp_transition_is_tagged (WpTransition * self, gpointer tag)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_TRANSITION (self), FALSE);
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
return (priv->tag == tag);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Gets \a self 's source tag.
|
|
|
|
|
|
* \see wp_transition_set_source_tag().
|
|
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \returns (transfer none): the transition's source tag
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
gpointer
|
|
|
|
|
|
wp_transition_get_source_tag (WpTransition * self)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_TRANSITION (self), NULL);
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
return priv->tag;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Sets \a self 's source tag.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* You can use this to tag a transition's return
|
2020-03-10 13:41:34 +02:00
|
|
|
|
* value with a particular pointer (usually a pointer to the function doing
|
|
|
|
|
|
* the tagging) and then later check it using wp_transition_get_source_tag()
|
|
|
|
|
|
* (or g_async_result_is_tagged()) in the transition's "finish" function,
|
|
|
|
|
|
* to figure out if the response came from a particular place.
|
2021-05-13 17:54:58 +03:00
|
|
|
|
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \param tag an opaque pointer indicating the source of this transition
|
|
|
|
|
|
*/
|
2020-03-10 13:41:34 +02:00
|
|
|
|
void
|
|
|
|
|
|
wp_transition_set_source_tag (WpTransition * self, gpointer tag)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_if_fail (WP_IS_TRANSITION (self));
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
priv->tag = tag;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Gets \a self 's data.
|
|
|
|
|
|
* \see wp_transition_set_data().
|
|
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \returns (transfer none): the transition's data
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
gpointer
|
|
|
|
|
|
wp_transition_get_data (WpTransition * self)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_TRANSITION (self), NULL);
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
return priv->data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Sets \a self 's data (freeing the existing data, if any). This can be an
|
2020-03-10 13:41:34 +02:00
|
|
|
|
* arbitrary user structure that holds data associated with this transition.
|
2021-05-21 18:40:43 +03:00
|
|
|
|
*
|
|
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \param data (nullable): transition-specific user data
|
|
|
|
|
|
* \param data_destroy (nullable): GDestroyNotify for \a data
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
wp_transition_set_data (WpTransition * self, gpointer data,
|
|
|
|
|
|
GDestroyNotify data_destroy)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_if_fail (WP_IS_TRANSITION (self));
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
if (priv->data && priv->data_destroy)
|
|
|
|
|
|
priv->data_destroy (priv->data);
|
|
|
|
|
|
priv->data = data;
|
|
|
|
|
|
priv->data_destroy = data_destroy;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \returns TRUE if the transition has completed (with or without an error),
|
|
|
|
|
|
* FALSE otherwise
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
gboolean
|
|
|
|
|
|
wp_transition_get_completed (WpTransition * self)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_TRANSITION (self), FALSE);
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
return priv->step == WP_TRANSITION_STEP_NONE ||
|
|
|
|
|
|
priv->step == WP_TRANSITION_STEP_ERROR;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \returns TRUE if the transition completed with an error, FALSE otherwise
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
gboolean
|
|
|
|
|
|
wp_transition_had_error (WpTransition * self)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_TRANSITION (self), FALSE);
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
return priv->step == WP_TRANSITION_STEP_ERROR;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wp_transition_return (WpTransition * self, WpTransitionPrivate *priv)
|
|
|
|
|
|
{
|
2020-05-08 19:23:23 +03:00
|
|
|
|
if (priv->closure) {
|
|
|
|
|
|
GValue values[2] = { G_VALUE_INIT, G_VALUE_INIT };
|
|
|
|
|
|
g_value_init (&values[0], G_TYPE_OBJECT);
|
|
|
|
|
|
g_value_init (&values[1], G_TYPE_OBJECT);
|
|
|
|
|
|
g_value_set_object (&values[0], priv->source_object);
|
|
|
|
|
|
g_value_set_object (&values[1], self);
|
|
|
|
|
|
g_closure_invoke (priv->closure, NULL, 2, values, NULL);
|
|
|
|
|
|
g_value_unset (&values[0]);
|
|
|
|
|
|
g_value_unset (&values[1]);
|
2020-03-10 13:41:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
g_object_notify (G_OBJECT (self), "completed");
|
|
|
|
|
|
|
|
|
|
|
|
/* WARNING */
|
|
|
|
|
|
g_object_unref (self);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Advances the transition to the next step.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* This initially calls _WpTransitionClass::get_next_step()
|
2021-05-13 17:54:58 +03:00
|
|
|
|
* in order to determine what the next step is.
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* If _WpTransitionClass::get_next_step() returns a step
|
2021-05-13 17:54:58 +03:00
|
|
|
|
* different than the previous one, it calls
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* _WpTransitionClass::execute_step() to execute it.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* The very first time that _WpTransitionClass::get_next_step()
|
|
|
|
|
|
* is called, its \a step parameter equals WP_TRANSITION_STEP_NONE.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* When _WpTransitionClass::get_next_step() returns
|
|
|
|
|
|
* WP_TRANSITION_STEP_NONE this function completes the transition,
|
|
|
|
|
|
* calling the transition's callback and then unref-ing the transition.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* When _WpTransitionClass::get_next_step() returns
|
|
|
|
|
|
* WP_TRANSITION_STEP_ERROR, this function calls wp_transition_return_error(),
|
2021-05-13 17:54:58 +03:00
|
|
|
|
* unless it has already been called directly by
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* _WpTransitionClass::get_next_step().
|
2020-03-29 12:36:19 +03:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* In error conditions, _WpTransitionClass::execute_step()
|
|
|
|
|
|
* is called once with \a step being WP_TRANSITION_STEP_ERROR, allowing the
|
|
|
|
|
|
* implementation to rollback any changes or cancel underlying jobs, if necessary.
|
|
|
|
|
|
*
|
|
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
wp_transition_advance (WpTransition * self)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_if_fail (WP_IS_TRANSITION (self));
|
|
|
|
|
|
|
|
|
|
|
|
/* keep a reference to avoid issues when wp_transition_return_error() is
|
|
|
|
|
|
called from within get_next_step() */
|
|
|
|
|
|
g_autoptr (WpTransition) self_ref = g_object_ref (self);
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
guint next_step;
|
2020-10-22 13:27:29 +03:00
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (priv->cancellable, &error)) {
|
|
|
|
|
|
wp_transition_return_error (self, error);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2020-03-10 13:41:34 +02:00
|
|
|
|
|
|
|
|
|
|
/* find the next step */
|
|
|
|
|
|
next_step = WP_TRANSITION_GET_CLASS (self)->get_next_step (self, priv->step);
|
|
|
|
|
|
|
lib: refactor WpProxy
This is an attempt to unclutter the API of WpProxy and
split functionality into smaller pieces, making it easier
to work with.
In this new class layout, we have the following classes:
- WpObject: base class for everything; handles activating
| and deactivating "features"
|- WpProxy: base class for anything that wraps a pw_proxy;
| handles events from pw_proxy and nothing more
|- WpGlobalProxy: handles integration with the registry
All the other classes derive from WpGlobalProxy. The reason
for separating WpGlobalProxy from WpProxy, though, is that
classes such as WpImplNode / WpSpaDevice can also derive from
WpProxy now, without interfacing with the registry.
All objects that come with an "info" structure and have properties
and/or params also implement the WpPipewireObject interface. This
provides the API to query properties and get/set params. Essentially,
this is implemented by all classes except WpMetadata (pw_metadata
does not have info)
This interface is implemented on each object separately, using
a private "mixin", which is a set of vfunc implementations and helper
functions (and macros) to facilitate the implementation of this interface.
A notable difference to the old WpProxy is that now features can be
deactivated, so it is possible to enable something and later disable
it again.
This commit disables modules, tests, tools, etc, to avoid growing the
patch more, while ensuring that the project compiles.
2020-11-10 19:17:02 +02:00
|
|
|
|
wp_trace_object (priv->source_object, "transition: %d -> %d", priv->step,
|
|
|
|
|
|
next_step);
|
|
|
|
|
|
|
2020-03-10 13:41:34 +02:00
|
|
|
|
if (next_step == WP_TRANSITION_STEP_ERROR) {
|
|
|
|
|
|
/* return error if the callback didn't do it already */
|
|
|
|
|
|
if (G_UNLIKELY (!priv->error)) {
|
|
|
|
|
|
wp_transition_return_error (self, g_error_new (WP_DOMAIN_LIBRARY,
|
|
|
|
|
|
WP_LIBRARY_ERROR_INVARIANT, "state machine error"));
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* if we reached STEP_NONE again, that means we reached the next state */
|
|
|
|
|
|
if (next_step == WP_TRANSITION_STEP_NONE) {
|
|
|
|
|
|
/* complete the transition */
|
|
|
|
|
|
priv->step = next_step;
|
|
|
|
|
|
wp_transition_return (self, priv);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* still at the same step, this means we are waiting for something */
|
|
|
|
|
|
if (next_step == priv->step)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
lib: refactor WpProxy
This is an attempt to unclutter the API of WpProxy and
split functionality into smaller pieces, making it easier
to work with.
In this new class layout, we have the following classes:
- WpObject: base class for everything; handles activating
| and deactivating "features"
|- WpProxy: base class for anything that wraps a pw_proxy;
| handles events from pw_proxy and nothing more
|- WpGlobalProxy: handles integration with the registry
All the other classes derive from WpGlobalProxy. The reason
for separating WpGlobalProxy from WpProxy, though, is that
classes such as WpImplNode / WpSpaDevice can also derive from
WpProxy now, without interfacing with the registry.
All objects that come with an "info" structure and have properties
and/or params also implement the WpPipewireObject interface. This
provides the API to query properties and get/set params. Essentially,
this is implemented by all classes except WpMetadata (pw_metadata
does not have info)
This interface is implemented on each object separately, using
a private "mixin", which is a set of vfunc implementations and helper
functions (and macros) to facilitate the implementation of this interface.
A notable difference to the old WpProxy is that now features can be
deactivated, so it is possible to enable something and later disable
it again.
This commit disables modules, tests, tools, etc, to avoid growing the
patch more, while ensuring that the project compiles.
2020-11-10 19:17:02 +02:00
|
|
|
|
wp_trace_object (priv->source_object, "transition: execute %d", next_step);
|
|
|
|
|
|
|
2020-03-10 13:41:34 +02:00
|
|
|
|
/* execute the next step */
|
|
|
|
|
|
priv->step = next_step;
|
|
|
|
|
|
WP_TRANSITION_GET_CLASS (self)->execute_step (self, priv->step);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Completes the transition with an error.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* This can be called anytime from within any virtual function or an async
|
|
|
|
|
|
* job handler.
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \note In most cases this will also unref the transition, so it is
|
2020-03-10 13:41:34 +02:00
|
|
|
|
* not safe to access it after this function has been called.
|
2021-05-21 18:40:43 +03:00
|
|
|
|
*
|
|
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param self the transition
|
|
|
|
|
|
* \param error (transfer full): a GError
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
wp_transition_return_error (WpTransition * self, GError * error)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_if_fail (WP_IS_TRANSITION (self));
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv = wp_transition_get_instance_private (self);
|
|
|
|
|
|
|
|
|
|
|
|
if (G_UNLIKELY (priv->error)) {
|
2020-04-14 18:31:17 +03:00
|
|
|
|
wp_warning_object (self, "transition bailing out multiple times; "
|
|
|
|
|
|
"old error was: %s", priv->error->message);
|
2020-03-10 13:41:34 +02:00
|
|
|
|
g_clear_error (&priv->error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
priv->step = WP_TRANSITION_STEP_ERROR;
|
|
|
|
|
|
priv->error = error;
|
2020-03-29 12:36:19 +03:00
|
|
|
|
|
|
|
|
|
|
/* allow the implementation to rollback changes */
|
|
|
|
|
|
if (WP_TRANSITION_GET_CLASS (self)->execute_step)
|
|
|
|
|
|
WP_TRANSITION_GET_CLASS (self)->execute_step (self, priv->step);
|
|
|
|
|
|
|
2020-03-10 13:41:34 +02:00
|
|
|
|
wp_transition_return (self, priv);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-13 17:54:58 +03:00
|
|
|
|
/*!
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \brief Returns the final return status of the transition and its error,
|
|
|
|
|
|
* if there was one.
|
2021-05-13 17:54:58 +03:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* This is meant to be called from within the GAsyncReadyCallback
|
|
|
|
|
|
* that was specified in wp_transition_new().
|
2020-03-10 13:41:34 +02:00
|
|
|
|
*
|
2021-05-21 18:40:43 +03:00
|
|
|
|
* \ingroup wptransition
|
|
|
|
|
|
* \param res a transition, as a GAsyncResult
|
|
|
|
|
|
* \param error (out) (optional): a location to return the transition's error,
|
|
|
|
|
|
* if any
|
|
|
|
|
|
* \returns TRUE if the transition completed successfully, FALSE if there
|
2020-03-10 13:41:34 +02:00
|
|
|
|
* was an error
|
|
|
|
|
|
*/
|
|
|
|
|
|
gboolean
|
|
|
|
|
|
wp_transition_finish (GAsyncResult * res, GError ** error)
|
|
|
|
|
|
{
|
|
|
|
|
|
g_return_val_if_fail (WP_IS_TRANSITION (res), FALSE);
|
|
|
|
|
|
|
|
|
|
|
|
WpTransitionPrivate *priv =
|
|
|
|
|
|
wp_transition_get_instance_private (WP_TRANSITION (res));
|
|
|
|
|
|
if (priv->error) {
|
|
|
|
|
|
g_propagate_error (error, priv->error);
|
|
|
|
|
|
priv->error = NULL;
|
|
|
|
|
|
}
|
lib: refactor WpProxy
This is an attempt to unclutter the API of WpProxy and
split functionality into smaller pieces, making it easier
to work with.
In this new class layout, we have the following classes:
- WpObject: base class for everything; handles activating
| and deactivating "features"
|- WpProxy: base class for anything that wraps a pw_proxy;
| handles events from pw_proxy and nothing more
|- WpGlobalProxy: handles integration with the registry
All the other classes derive from WpGlobalProxy. The reason
for separating WpGlobalProxy from WpProxy, though, is that
classes such as WpImplNode / WpSpaDevice can also derive from
WpProxy now, without interfacing with the registry.
All objects that come with an "info" structure and have properties
and/or params also implement the WpPipewireObject interface. This
provides the API to query properties and get/set params. Essentially,
this is implemented by all classes except WpMetadata (pw_metadata
does not have info)
This interface is implemented on each object separately, using
a private "mixin", which is a set of vfunc implementations and helper
functions (and macros) to facilitate the implementation of this interface.
A notable difference to the old WpProxy is that now features can be
deactivated, so it is possible to enable something and later disable
it again.
This commit disables modules, tests, tools, etc, to avoid growing the
patch more, while ensuring that the project compiles.
2020-11-10 19:17:02 +02:00
|
|
|
|
|
|
|
|
|
|
wp_trace_object (priv->source_object, "transition: finished %s",
|
|
|
|
|
|
(priv->step == WP_TRANSITION_STEP_NONE) ? "ok" : "with error");
|
|
|
|
|
|
|
2020-03-10 13:41:34 +02:00
|
|
|
|
return (priv->step == WP_TRANSITION_STEP_NONE);
|
2021-05-21 18:40:43 +03:00
|
|
|
|
}
|