wireplumber/lib/wp/event-hook.c
Julian Bouzas b80a0975c7 event-dispatcher: Register hooks for defined events in a hash table
Since all the current hooks are defined specifically for a particular event
type, we can register the hooks in a hash table using the event type as key
for faster event hook collection.

Also, hooks that are not specific to a particular event type, like constraints
such as 'event.type=*', will be registered in both the undefined hook list,
and also in all the hash table defined hook lists so they are always evaluated.

Even though 'wp_event_dispatcher_new_hooks_iterator()' can still be used, it is
now marked as deprecated because it is slower. The event hook collection uses
'wp_event_dispatcher_new_hooks_for_event_type_iterator()' now because it is
much faster.

Previously, the more hooks we were registering, the slower WirePlumber would
process events as all hooks needed to be evaluated for all events constantly.
This is not the case anymore with this patch. We can register thousands of
hooks, and if only 1 of those runs for a particular event, only 1 will be
evaluated instead of all of them.

See #824
2025-11-24 08:01:00 -05:00

804 lines
24 KiB
C

/* WirePlumber
*
* Copyright © 2022 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include "event-hook.h"
#include "event-dispatcher.h"
#include "transition.h"
#include "log.h"
#include "wpenums.h"
WP_DEFINE_LOCAL_LOG_TOPIC ("wp-event-hook")
typedef struct _WpEventHookPrivate WpEventHookPrivate;
struct _WpEventHookPrivate
{
GWeakRef dispatcher;
gchar *name;
gchar **before;
gchar **after;
};
enum {
PROP_0,
PROP_NAME,
PROP_RUNS_BEFORE_HOOKS,
PROP_RUNS_AFTER_HOOKS,
PROP_DISPATCHER,
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpEventHook, wp_event_hook, G_TYPE_OBJECT)
static void
wp_event_hook_init (WpEventHook * self)
{
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
g_weak_ref_init (&priv->dispatcher, NULL);
}
static void
wp_event_hook_finalize (GObject * object)
{
WpEventHook *self = WP_EVENT_HOOK (object);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
g_weak_ref_clear (&priv->dispatcher);
g_strfreev (priv->before);
g_strfreev (priv->after);
g_free (priv->name);
G_OBJECT_CLASS (wp_event_hook_parent_class)->finalize (object);
}
static void
wp_event_hook_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpEventHook *self = WP_EVENT_HOOK (object);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
switch (property_id) {
case PROP_NAME:
priv->name = g_value_dup_string (value);
break;
case PROP_RUNS_BEFORE_HOOKS:
priv->before = g_value_dup_boxed (value);
break;
case PROP_RUNS_AFTER_HOOKS:
priv->after = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_event_hook_get_property (GObject * object, guint property_id, GValue * value,
GParamSpec * pspec)
{
WpEventHook *self = WP_EVENT_HOOK (object);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
switch (property_id) {
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_RUNS_BEFORE_HOOKS:
g_value_set_boxed (value, priv->before);
break;
case PROP_RUNS_AFTER_HOOKS:
g_value_set_boxed (value, priv->after);
break;
case PROP_DISPATCHER:
g_value_take_object (value, wp_event_hook_get_dispatcher (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_event_hook_class_init (WpEventHookClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
object_class->finalize = wp_event_hook_finalize;
object_class->set_property = wp_event_hook_set_property;
object_class->get_property = wp_event_hook_get_property;
g_object_class_install_property (object_class, PROP_NAME,
g_param_spec_string ("name", "name", "The hook name", "",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_RUNS_BEFORE_HOOKS,
g_param_spec_boxed ("runs-before-hooks", "runs-before-hooks",
"runs-before-hooks", G_TYPE_STRV,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_RUNS_AFTER_HOOKS,
g_param_spec_boxed ("runs-after-hooks", "runs-after-hooks",
"runs-after-hooks", G_TYPE_STRV,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_DISPATCHER,
g_param_spec_object ("dispatcher", "dispatcher",
"The associated event dispatcher", WP_TYPE_EVENT_DISPATCHER,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
/*!
* \brief Returns the name of the hook
*
* \ingroup wpeventhook
* \param self the event hook
* \return the event hook name
*/
const gchar *
wp_event_hook_get_name (WpEventHook * self)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), 0);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
return priv->name;
}
/*!
* \brief Returns the names of the hooks that should run after this hook,
* or in other words, this hook should run before them
*
* \ingroup wpeventhook
* \param self the event hook
* \return (array zero-terminated=1)(element-type utf8)(transfer none):
* a NULL-terminated array of hook names
*/
const gchar * const *
wp_event_hook_get_runs_before_hooks (WpEventHook * self)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), NULL);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
return (const gchar * const *) priv->before;
}
/*!
* \brief Returns the names of the hooks that should run before this hook,
* or in other words, this hook should run after them
*
* \ingroup wpeventhook
* \param self the event hook
* \return (array zero-terminated=1)(element-type utf8)(transfer none):
* a NULL-terminated array of hook names
*/
const gchar * const *
wp_event_hook_get_runs_after_hooks (WpEventHook * self)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), NULL);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
return (const gchar * const *) priv->after;
}
/*!
* \brief Returns the associated event dispatcher
*
* \private
* \ingroup wpeventhook
* \param self the event hook
* \return (transfer full)(nullable): the event dispatcher on which this hook
* is registered, or NULL if the hook is not registered
*/
WpEventDispatcher *
wp_event_hook_get_dispatcher (WpEventHook * self)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), NULL);
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
return g_weak_ref_get (&priv->dispatcher);
}
/*!
* \brief Sets the associated event dispatcher
*
* \private
* \ingroup wpeventhook
* \param self the event hook
* \param dispatcher (transfer none): the event dispatcher on which
* this hook is registered
*/
void
wp_event_hook_set_dispatcher (WpEventHook * self, WpEventDispatcher * dispatcher)
{
WpEventHookPrivate *priv = wp_event_hook_get_instance_private (self);
wp_trace_object (dispatcher, "hook (%s) registered", priv->name);
g_weak_ref_set (&priv->dispatcher, dispatcher);
}
/*!
* \brief Checks if the hook should be executed for a given event
*
* \ingroup wpeventhook
* \param self the event hook
* \param event the event
* \return TRUE if the hook should be executed for the given event,
* FALSE otherwise
*/
gboolean
wp_event_hook_runs_for_event (WpEventHook * self, WpEvent * event)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), FALSE);
g_return_val_if_fail (WP_EVENT_HOOK_GET_CLASS (self)->runs_for_event, FALSE);
return WP_EVENT_HOOK_GET_CLASS (self)->runs_for_event (self, event);
}
/*!
* \brief Runs the hook on the given event
*
* \ingroup wpeventhook
* \param self the event hook
* \param event the event that triggered the hook
* \param cancellable (nullable): a GCancellable to cancel the async operation
* \param callback (scope async)(closure callback_data): a callback to fire after execution of the hook
* has completed
* \param callback_data data for the callback
*/
void
wp_event_hook_run (WpEventHook * self,
WpEvent * event, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
g_return_if_fail (WP_IS_EVENT_HOOK (self));
g_return_if_fail (WP_EVENT_HOOK_GET_CLASS (self)->run);
WP_EVENT_HOOK_GET_CLASS (self)->run (self, event, cancellable, callback,
callback_data);
}
/*!
* \brief Gets all the matching event types for this hook if any.
*
* \ingroup wpeventhook
* \param self the event hook
* \returns (element-type gchar*) (transfer full) (nullable): the matching
* event types for this hook if any.
* \since 0.5.13
*/
GPtrArray *
wp_event_hook_get_matching_event_types (WpEventHook * self)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), NULL);
g_return_val_if_fail (
WP_EVENT_HOOK_GET_CLASS (self)->get_matching_event_types, NULL);
return WP_EVENT_HOOK_GET_CLASS (self)->get_matching_event_types (self);
}
/*!
* \brief Finishes the async operation that was started by wp_event_hook_run()
*
* \ingroup wpeventhook
* \param self the event hook
* \param res the async operation result
* \param error (out) (optional): the error of the operation, if any
* \returns FALSE if there was an error, TRUE otherwise
*/
gboolean
wp_event_hook_finish (WpEventHook * self, GAsyncResult * res, GError ** error)
{
g_return_val_if_fail (WP_IS_EVENT_HOOK (self), FALSE);
g_return_val_if_fail (WP_EVENT_HOOK_GET_CLASS (self)->finish, FALSE);
return WP_EVENT_HOOK_GET_CLASS (self)->finish (self, res, error);
}
/*!
* \struct WpInterestEventHook
*
* An event hook that declares interest in specific events. This subclass
* implements the WpEventHook.runs_for_event() vmethod and returns TRUE from
* that method if the given event has properties that match one of the declared
* interests.
*/
typedef struct _WpInterestEventHookPrivate WpInterestEventHookPrivate;
struct _WpInterestEventHookPrivate
{
/* element-type: WpObjectInterest* */
GPtrArray *interests;
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpInterestEventHook,
wp_interest_event_hook, WP_TYPE_EVENT_HOOK)
static void
wp_interest_event_hook_init (WpInterestEventHook * self)
{
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
priv->interests = g_ptr_array_new_with_free_func (
(GDestroyNotify) wp_object_interest_unref);
}
static void
wp_interest_event_hook_finalize (GObject * object)
{
WpInterestEventHook *self = WP_INTEREST_EVENT_HOOK (object);
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
g_clear_pointer (&priv->interests, g_ptr_array_unref);
G_OBJECT_CLASS (wp_interest_event_hook_parent_class)->finalize (object);
}
static gboolean
wp_interest_event_hook_runs_for_event (WpEventHook * hook, WpEvent * event)
{
WpInterestEventHook *self = WP_INTEREST_EVENT_HOOK (hook);
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
g_autoptr (WpProperties) properties = wp_event_get_properties (event);
g_autoptr (GObject) subject = wp_event_get_subject (event);
guint i;
WpObjectInterest *interest = NULL;
for (i = 0; i < priv->interests->len; i++) {
interest = g_ptr_array_index (priv->interests, i);
if (wp_object_interest_matches_full (interest,
WP_INTEREST_MATCH_FLAGS_NONE,
WP_TYPE_EVENT, subject, properties, properties) == WP_INTEREST_MATCH_ALL)
return TRUE;
}
return FALSE;
}
static void
add_unique (GPtrArray *array, const gchar * lookup)
{
for (guint i = 0; i < array->len; i++)
if (g_str_equal (g_ptr_array_index (array, i), lookup))
return;
g_ptr_array_add (array, g_strdup (lookup));
}
static GPtrArray *
wp_interest_event_hook_get_matching_event_types (WpEventHook * hook)
{
WpInterestEventHook *self = WP_INTEREST_EVENT_HOOK (hook);
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
GPtrArray *res = g_ptr_array_new_with_free_func (g_free);
guint i;
for (i = 0; i < priv->interests->len; i++) {
WpObjectInterest *interest = g_ptr_array_index (priv->interests, i);
if (wp_object_interest_matches_full (interest, WP_INTEREST_MATCH_FLAGS_NONE,
WP_TYPE_EVENT, NULL, NULL, NULL) & WP_INTEREST_MATCH_GTYPE) {
g_autoptr (GPtrArray) values =
wp_object_interest_find_defined_constraint_values (interest,
WP_CONSTRAINT_TYPE_NONE, "event.type");
if (!values || values->len == 0) {
/* We always consider the hook undefined if it has at least one interest
* without a defined 'event.type' constraint */
return NULL;
} else {
for (guint j = 0; j < values->len; j++) {
GVariant *v = g_ptr_array_index (values, j);
if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) {
const gchar *v_str = g_variant_get_string (v, NULL);
add_unique (res, v_str);
}
}
}
}
}
return res;
}
static void
wp_interest_event_hook_class_init (WpInterestEventHookClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpEventHookClass *hook_class = (WpEventHookClass *) klass;
object_class->finalize = wp_interest_event_hook_finalize;
hook_class->runs_for_event = wp_interest_event_hook_runs_for_event;
hook_class->get_matching_event_types =
wp_interest_event_hook_get_matching_event_types;
}
/*!
* \brief Equivalent to:
* \code
* WpObjectInterest *i = wp_object_interest_new (WP_TYPE_EVENT, ...);
* wp_interest_event_hook_add_interest_full (self, i);
* \endcode
*
* The constraints specified in the variable arguments must follow the rules
* documented in wp_object_interest_new().
*
* \ingroup wpeventhook
* \param self the event hook
* \param ... a list of constraints, terminated by NULL
*/
void
wp_interest_event_hook_add_interest (WpInterestEventHook * self, ...)
{
WpObjectInterest *interest;
va_list args;
g_return_if_fail (WP_IS_INTEREST_EVENT_HOOK (self));
va_start (args, self);
interest = wp_object_interest_new_valist (WP_TYPE_EVENT, &args);
wp_interest_event_hook_add_interest_full (self, interest);
va_end (args);
}
/*!
* \brief Declares interest on events. The interest must be constructed to
* match WP_TYPE_EVENT objects and it is going to be matched against
* the WpEvent's properties.
*
* \param self the event hook
* \param interest (transfer full): the event interest
*/
void
wp_interest_event_hook_add_interest_full (WpInterestEventHook * self,
WpObjectInterest * interest)
{
g_autoptr (GError) error = NULL;
g_return_if_fail (WP_IS_INTEREST_EVENT_HOOK (self));
if (G_UNLIKELY (!wp_object_interest_validate (interest, &error))) {
wp_critical_object (self, "interest validation failed: %s",
error->message);
wp_object_interest_unref (interest);
return;
}
WpInterestEventHookPrivate *priv =
wp_interest_event_hook_get_instance_private (self);
g_ptr_array_add (priv->interests, interest);
}
/*!
* \struct WpSimpleEventHook
*
* An event hook that runs a GClosure, synchronously.
*/
struct _WpSimpleEventHook
{
WpInterestEventHook parent;
GClosure *closure;
};
enum {
PROP_SIMPLE_0,
PROP_SIMPLE_CLOSURE,
};
G_DEFINE_TYPE (WpSimpleEventHook, wp_simple_event_hook,
WP_TYPE_INTEREST_EVENT_HOOK)
static void
wp_simple_event_hook_init (WpSimpleEventHook * self)
{
}
static void
wp_simple_event_hook_finalize (GObject * object)
{
WpSimpleEventHook *self = WP_SIMPLE_EVENT_HOOK (object);
g_clear_pointer (&self->closure, g_closure_unref);
G_OBJECT_CLASS (wp_simple_event_hook_parent_class)->finalize (object);
}
static void
wp_simple_event_hook_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpSimpleEventHook *self = WP_SIMPLE_EVENT_HOOK (object);
switch (property_id) {
case PROP_SIMPLE_CLOSURE:
self->closure = g_value_dup_boxed (value);
g_closure_sink (self->closure);
if (G_CLOSURE_NEEDS_MARSHAL (self->closure))
g_closure_set_marshal (self->closure, g_cclosure_marshal_generic);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_simple_event_hook_run (WpEventHook * hook,
WpEvent * event, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
WpSimpleEventHook *self = WP_SIMPLE_EVENT_HOOK (hook);
GValue values[2] = { G_VALUE_INIT };
g_value_init (&values[0], WP_TYPE_EVENT);
g_value_set_boxed (&values[0], event);
g_closure_invoke (self->closure, NULL, 1, values, NULL);
g_value_unset (&values[0]);
callback ((GObject *) self, NULL, callback_data);
}
static gboolean
wp_simple_event_hook_finish (WpEventHook * hook, GAsyncResult * res,
GError ** error)
{
return TRUE;
}
static void
wp_simple_event_hook_class_init (WpSimpleEventHookClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpEventHookClass *hook_class = (WpEventHookClass *) klass;
object_class->finalize = wp_simple_event_hook_finalize;
object_class->set_property = wp_simple_event_hook_set_property;
hook_class->run = wp_simple_event_hook_run;
hook_class->finish = wp_simple_event_hook_finish;
g_object_class_install_property (object_class, PROP_SIMPLE_CLOSURE,
g_param_spec_boxed ("closure", "closure", "The closure", G_TYPE_CLOSURE,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
/*!
* \brief Constructs a new simple event hook
*
* \param name the name of the hook
* \param before (array zero-terminated=1)(element-type utf8)(transfer none)(nullable):
* an array of hook names that should run after this hook
* \param after (array zero-terminated=1)(element-type utf8)(transfer none)(nullable):
* an array of hook names that should run before this hook
* \param closure the closure to invoke when the hook is executed; the closure
* should accept two parameters: the event dispatcher and the event, returning
* nothing
* \return a new simple event hook
*/
WpEventHook *
wp_simple_event_hook_new (const gchar *name,
const gchar * before[], const gchar * after[], GClosure * closure)
{
g_return_val_if_fail (closure != NULL, NULL);
return g_object_new (WP_TYPE_SIMPLE_EVENT_HOOK,
"name", name,
"runs-before-hooks", before,
"runs-after-hooks", after,
"closure", closure,
NULL);
}
/* WpAsyncEventHookTransition */
#define WP_TYPE_ASYNC_EVENT_HOOK_TRANSITION \
(wp_async_event_hook_transition_get_type ())
G_DECLARE_FINAL_TYPE (WpAsyncEventHookTransition,
wp_async_event_hook_transition,
WP, ASYNC_EVENT_HOOK_TRANSITION, WpTransition)
/*!
* \struct WpAsyncEventHook
*
* An event hook that runs a WpTransition, implemented with closures.
*/
struct _WpAsyncEventHook
{
WpInterestEventHook parent;
GClosure *get_next_step;
GClosure *execute_step;
};
enum {
PROP_ASYNC_0,
PROP_ASYNC_GET_NEXT_STEP,
PROP_ASYNC_EXECUTE_STEP,
};
G_DEFINE_TYPE (WpAsyncEventHook, wp_async_event_hook,
WP_TYPE_INTEREST_EVENT_HOOK)
static void
wp_async_event_hook_init (WpAsyncEventHook * self)
{
}
static void
wp_async_event_hook_finalize (GObject * object)
{
WpAsyncEventHook *self = WP_ASYNC_EVENT_HOOK (object);
g_clear_pointer (&self->get_next_step, g_closure_unref);
g_clear_pointer (&self->execute_step, g_closure_unref);
G_OBJECT_CLASS (wp_async_event_hook_parent_class)->finalize (object);
}
static void
wp_async_event_hook_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpAsyncEventHook *self = WP_ASYNC_EVENT_HOOK (object);
switch (property_id) {
case PROP_ASYNC_GET_NEXT_STEP:
self->get_next_step = g_value_dup_boxed (value);
g_closure_sink (self->get_next_step);
if (G_CLOSURE_NEEDS_MARSHAL (self->get_next_step))
g_closure_set_marshal (self->get_next_step, g_cclosure_marshal_generic);
break;
case PROP_ASYNC_EXECUTE_STEP:
self->execute_step = g_value_dup_boxed (value);
g_closure_sink (self->execute_step);
if (G_CLOSURE_NEEDS_MARSHAL (self->execute_step))
g_closure_set_marshal (self->execute_step, g_cclosure_marshal_VOID__UINT);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_async_event_hook_run (WpEventHook * hook,
WpEvent * event, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
WpTransition *transition = wp_transition_new (
WP_TYPE_ASYNC_EVENT_HOOK_TRANSITION,
hook, cancellable, callback, callback_data);
wp_transition_set_data (transition, wp_event_ref (event),
(GDestroyNotify) wp_event_unref);
wp_transition_set_source_tag (transition, wp_async_event_hook_run);
wp_transition_advance (transition);
}
static gboolean
wp_async_event_hook_finish (WpEventHook * hook, GAsyncResult * res,
GError ** error)
{
g_return_val_if_fail (g_async_result_is_tagged (res, wp_async_event_hook_run),
FALSE);
return wp_transition_finish (res, error);
}
static void
wp_async_event_hook_class_init (WpAsyncEventHookClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpEventHookClass *hook_class = (WpEventHookClass *) klass;
object_class->finalize = wp_async_event_hook_finalize;
object_class->set_property = wp_async_event_hook_set_property;
hook_class->run = wp_async_event_hook_run;
hook_class->finish = wp_async_event_hook_finish;
g_object_class_install_property (object_class, PROP_ASYNC_GET_NEXT_STEP,
g_param_spec_boxed ("get-next-step", "get-next-step",
"The get-next-step closure", G_TYPE_CLOSURE,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_ASYNC_EXECUTE_STEP,
g_param_spec_boxed ("execute-step", "execute-step",
"The execute-step closure", G_TYPE_CLOSURE,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
/*!
* \brief Constructs a new async event hook
*
* \param name the name of the hook
* \param before (array zero-terminated=1)(element-type utf8)(transfer none)(nullable):
* an array of hook names that should run after this hook
* \param after (array zero-terminated=1)(element-type utf8)(transfer none)(nullable):
* an array of hook names that should run before this hook
* \param get_next_step the closure to invoke to get the next step
* \param execute_step the closure to invoke to execute the step
* \return a new async event hook
*/
WpEventHook *
wp_async_event_hook_new (const gchar *name,
const gchar * before[], const gchar * after[],
GClosure * get_next_step, GClosure * execute_step)
{
g_return_val_if_fail (get_next_step != NULL, NULL);
g_return_val_if_fail (execute_step != NULL, NULL);
return g_object_new (WP_TYPE_ASYNC_EVENT_HOOK,
"name", name,
"runs-before-hooks", before,
"runs-after-hooks", after,
"get-next-step", get_next_step,
"execute-step", execute_step,
NULL);
}
/* WpAsyncEventHookTransition - implementation */
struct _WpAsyncEventHookTransition
{
WpTransition parent;
};
G_DEFINE_TYPE (WpAsyncEventHookTransition,
wp_async_event_hook_transition,
WP_TYPE_TRANSITION)
static void
wp_async_event_hook_transition_init (
WpAsyncEventHookTransition * transition)
{
}
static guint
wp_async_event_hook_transition_get_next_step (
WpTransition * transition, guint step)
{
WpAsyncEventHook *hook =
WP_ASYNC_EVENT_HOOK (wp_transition_get_source_object (transition));
guint next_step = WP_TRANSITION_STEP_ERROR;
GValue ret = G_VALUE_INIT;
GValue values[2] = { G_VALUE_INIT, G_VALUE_INIT };
g_value_init (&ret, G_TYPE_UINT);
g_value_init (&values[0], G_TYPE_OBJECT);
g_value_init (&values[1], G_TYPE_UINT);
g_value_set_object (&values[0], transition);
g_value_set_uint (&values[1], step);
g_closure_invoke (hook->get_next_step, &ret, 2, values, NULL);
g_value_unset (&values[0]);
g_value_unset (&values[1]);
next_step = g_value_get_uint (&ret);
g_value_unset (&ret);
return next_step;
}
static void
wp_async_event_hook_transition_execute_step (
WpTransition * transition, guint step)
{
WpAsyncEventHook *hook =
WP_ASYNC_EVENT_HOOK (wp_transition_get_source_object (transition));
GValue values[2] = { G_VALUE_INIT, G_VALUE_INIT };
g_value_init (&values[0], G_TYPE_OBJECT);
g_value_init (&values[1], G_TYPE_UINT);
g_value_set_object (&values[0], transition);
g_value_set_uint (&values[1], step);
g_closure_invoke (hook->execute_step, NULL, 2, values, NULL);
g_value_unset (&values[0]);
g_value_unset (&values[1]);
}
static void
wp_async_event_hook_transition_class_init (
WpAsyncEventHookTransitionClass * klass)
{
WpTransitionClass *transition_class = (WpTransitionClass *) klass;
transition_class->get_next_step =
wp_async_event_hook_transition_get_next_step;
transition_class->execute_step =
wp_async_event_hook_transition_execute_step;
}