mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2025-12-20 04:10:03 +01:00
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
This commit is contained in:
parent
7ca21699a9
commit
b80a0975c7
7 changed files with 271 additions and 10 deletions
|
|
@ -49,7 +49,8 @@ struct _WpEventDispatcher
|
|||
GObject parent;
|
||||
|
||||
GWeakRef core;
|
||||
GPtrArray *hooks; /* registered hooks */
|
||||
GHashTable *defined_hooks; /* registered hooks for defined events */
|
||||
GPtrArray *undefined_hooks; /* registered hooks for undefined events */
|
||||
GSource *source; /* the event loop source */
|
||||
GList *events; /* the events stack */
|
||||
struct spa_system *system;
|
||||
|
|
@ -160,7 +161,9 @@ static void
|
|||
wp_event_dispatcher_init (WpEventDispatcher * self)
|
||||
{
|
||||
g_weak_ref_init (&self->core, NULL);
|
||||
self->hooks = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
self->defined_hooks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
||||
(GDestroyNotify)g_ptr_array_unref);
|
||||
self->undefined_hooks = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
self->source = g_source_new (&source_funcs, sizeof (WpEventSource));
|
||||
((WpEventSource *) self->source)->dispatcher = self;
|
||||
|
|
@ -184,7 +187,8 @@ wp_event_dispatcher_finalize (GObject * object)
|
|||
|
||||
close (self->eventfd);
|
||||
|
||||
g_clear_pointer (&self->hooks, g_ptr_array_unref);
|
||||
g_clear_pointer (&self->defined_hooks, g_hash_table_unref);
|
||||
g_clear_pointer (&self->undefined_hooks, g_ptr_array_unref);
|
||||
g_weak_ref_clear (&self->core);
|
||||
|
||||
G_OBJECT_CLASS (wp_event_dispatcher_parent_class)->finalize (object);
|
||||
|
|
@ -284,6 +288,10 @@ void
|
|||
wp_event_dispatcher_register_hook (WpEventDispatcher * self,
|
||||
WpEventHook * hook)
|
||||
{
|
||||
g_autoptr (GPtrArray) event_types = NULL;
|
||||
gboolean is_defined = FALSE;
|
||||
const gchar *hook_name;
|
||||
|
||||
g_return_if_fail (WP_IS_EVENT_DISPATCHER (self));
|
||||
g_return_if_fail (WP_IS_EVENT_HOOK (hook));
|
||||
|
||||
|
|
@ -292,7 +300,57 @@ wp_event_dispatcher_register_hook (WpEventDispatcher * self,
|
|||
g_return_if_fail (already_registered_dispatcher == NULL);
|
||||
|
||||
wp_event_hook_set_dispatcher (hook, self);
|
||||
g_ptr_array_add (self->hooks, g_object_ref (hook));
|
||||
|
||||
/* Register the event hook in the defined hooks table if it is defined */
|
||||
hook_name = wp_event_hook_get_name (hook);
|
||||
event_types = wp_event_hook_get_matching_event_types (hook);
|
||||
if (event_types) {
|
||||
for (guint i = 0; i < event_types->len; i++) {
|
||||
const gchar *event_type = g_ptr_array_index (event_types, i);
|
||||
GPtrArray *hooks;
|
||||
|
||||
wp_debug_object (self, "Registering hook %s for defined event type %s",
|
||||
hook_name, event_type);
|
||||
|
||||
/* Check if the event type was registered in the hash table */
|
||||
hooks = g_hash_table_lookup (self->defined_hooks, event_type);
|
||||
if (hooks) {
|
||||
g_ptr_array_add (hooks, g_object_ref (hook));
|
||||
} else {
|
||||
GPtrArray *new_hooks = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
/* Add undefined hooks */
|
||||
for (guint i = 0; i < self->undefined_hooks->len; i++) {
|
||||
WpEventHook *uh = g_ptr_array_index (self->undefined_hooks, i);
|
||||
g_ptr_array_add (new_hooks, g_object_ref (uh));
|
||||
}
|
||||
/* Add current hook */
|
||||
g_ptr_array_add (new_hooks, g_object_ref (hook));
|
||||
g_hash_table_insert (self->defined_hooks, g_strdup (event_type),
|
||||
new_hooks);
|
||||
}
|
||||
|
||||
is_defined = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise just register it as undefined hook */
|
||||
if (!is_defined) {
|
||||
GHashTableIter iter;
|
||||
gpointer value;
|
||||
|
||||
wp_debug_object (self, "Registering hook %s for undefined event types",
|
||||
hook_name);
|
||||
|
||||
/* Add it to the defined hooks table */
|
||||
g_hash_table_iter_init (&iter, self->defined_hooks);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &value)) {
|
||||
GPtrArray *defined_hooks = value;
|
||||
g_ptr_array_add (defined_hooks, g_object_ref (hook));
|
||||
}
|
||||
|
||||
/* Add it to the undefined hooks */
|
||||
g_ptr_array_add (self->undefined_hooks, g_object_ref (hook));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -306,6 +364,9 @@ void
|
|||
wp_event_dispatcher_unregister_hook (WpEventDispatcher * self,
|
||||
WpEventHook * hook)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer value;
|
||||
|
||||
g_return_if_fail (WP_IS_EVENT_DISPATCHER (self));
|
||||
g_return_if_fail (WP_IS_EVENT_HOOK (hook));
|
||||
|
||||
|
|
@ -314,11 +375,29 @@ wp_event_dispatcher_unregister_hook (WpEventDispatcher * self,
|
|||
g_return_if_fail (already_registered_dispatcher == self);
|
||||
|
||||
wp_event_hook_set_dispatcher (hook, NULL);
|
||||
g_ptr_array_remove_fast (self->hooks, hook);
|
||||
|
||||
/* Remove hook from defined table and undefined list */
|
||||
g_hash_table_iter_init (&iter, self->defined_hooks);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &value)) {
|
||||
GPtrArray *defined_hooks = value;
|
||||
g_ptr_array_remove (defined_hooks, hook);
|
||||
}
|
||||
g_ptr_array_remove (self->undefined_hooks, hook);
|
||||
}
|
||||
|
||||
static void
|
||||
add_unique (GPtrArray *array, WpEventHook * hook)
|
||||
{
|
||||
for (guint i = 0; i < array->len; i++)
|
||||
if (g_ptr_array_index (array, i) == hook)
|
||||
return;
|
||||
g_ptr_array_add (array, g_object_ref (hook));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns an iterator to iterate over all the registered hooks
|
||||
* \deprecated Use \ref wp_event_dispatcher_new_hooks_for_event_type_iterator
|
||||
* instead.
|
||||
* \ingroup wpeventdispatcher
|
||||
*
|
||||
* \param self the event dispatcher
|
||||
|
|
@ -327,7 +406,56 @@ wp_event_dispatcher_unregister_hook (WpEventDispatcher * self,
|
|||
WpIterator *
|
||||
wp_event_dispatcher_new_hooks_iterator (WpEventDispatcher * self)
|
||||
{
|
||||
GPtrArray *items =
|
||||
g_ptr_array_copy (self->hooks, (GCopyFunc) g_object_ref, NULL);
|
||||
GPtrArray *items = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
GHashTableIter iter;
|
||||
gpointer value;
|
||||
|
||||
/* Add all defined hooks */
|
||||
g_hash_table_iter_init (&iter, self->defined_hooks);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &value)) {
|
||||
GPtrArray *hooks = value;
|
||||
for (guint i = 0; i < hooks->len; i++) {
|
||||
WpEventHook *hook = g_ptr_array_index (hooks, i);
|
||||
add_unique (items, hook);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add all undefined hooks */
|
||||
for (guint i = 0; i < self->undefined_hooks->len; i++) {
|
||||
WpEventHook *hook = g_ptr_array_index (self->undefined_hooks, i);
|
||||
add_unique (items, hook);
|
||||
}
|
||||
|
||||
return wp_iterator_new_ptr_array (items, WP_TYPE_EVENT_HOOK);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns an iterator to iterate over the registered hooks for a
|
||||
* particular event type.
|
||||
* \ingroup wpeventdispatcher
|
||||
*
|
||||
* \param self the event dispatcher
|
||||
* \param event_type the event type
|
||||
* \return (transfer full): a new iterator
|
||||
* \since 0.5.13
|
||||
*/
|
||||
WpIterator *
|
||||
wp_event_dispatcher_new_hooks_for_event_type_iterator (
|
||||
WpEventDispatcher * self, const gchar *event_type)
|
||||
{
|
||||
GPtrArray *items;
|
||||
GPtrArray *hooks;
|
||||
|
||||
hooks = g_hash_table_lookup (self->defined_hooks, event_type);
|
||||
if (hooks) {
|
||||
wp_debug_object (self, "Using %d defined hooks for event type %s",
|
||||
hooks->len, event_type);
|
||||
} else {
|
||||
hooks = self->undefined_hooks;
|
||||
wp_debug_object (self, "Using %d undefined hooks for event type %s",
|
||||
hooks->len, event_type);
|
||||
}
|
||||
|
||||
items = g_ptr_array_copy (hooks, (GCopyFunc) g_object_ref, NULL);
|
||||
return wp_iterator_new_ptr_array (items, WP_TYPE_EVENT_HOOK);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,12 @@ void wp_event_dispatcher_unregister_hook (WpEventDispatcher * self,
|
|||
WpEventHook * hook);
|
||||
|
||||
WP_API
|
||||
WpIterator * wp_event_dispatcher_new_hooks_iterator (WpEventDispatcher * self);
|
||||
WpIterator * wp_event_dispatcher_new_hooks_iterator (WpEventDispatcher * self)
|
||||
G_GNUC_DEPRECATED_FOR (wp_event_dispatcher_new_hooks_for_event_type_iterator);
|
||||
|
||||
WP_API
|
||||
WpIterator * wp_event_dispatcher_new_hooks_for_event_type_iterator (
|
||||
WpEventDispatcher * self, const gchar *event_type);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
|||
|
|
@ -254,6 +254,24 @@ wp_event_hook_run (WpEventHook * self,
|
|||
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()
|
||||
*
|
||||
|
|
@ -334,6 +352,50 @@ wp_interest_event_hook_runs_for_event (WpEventHook * hook, WpEvent * event)
|
|||
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)
|
||||
{
|
||||
|
|
@ -342,6 +404,8 @@ wp_interest_event_hook_class_init (WpInterestEventHookClass * 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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -39,8 +39,10 @@ struct _WpEventHookClass
|
|||
|
||||
gboolean (*finish) (WpEventHook * self, GAsyncResult * res, GError ** error);
|
||||
|
||||
GPtrArray * (*get_matching_event_types) (WpEventHook *self);
|
||||
|
||||
/*< private >*/
|
||||
WP_PADDING(5)
|
||||
WP_PADDING(4)
|
||||
};
|
||||
|
||||
WP_API
|
||||
|
|
@ -67,6 +69,9 @@ void wp_event_hook_run (WpEventHook * self,
|
|||
WpEvent * event, GCancellable * cancellable,
|
||||
GAsyncReadyCallback callback, gpointer callback_data);
|
||||
|
||||
WP_API
|
||||
GPtrArray * wp_event_hook_get_matching_event_types (WpEventHook * self);
|
||||
|
||||
WP_API
|
||||
gboolean wp_event_hook_finish (WpEventHook * self, GAsyncResult * res,
|
||||
GError ** error);
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ wp_event_collect_hooks (WpEvent * event, WpEventDispatcher * dispatcher)
|
|||
struct spa_list collected, result, remaining;
|
||||
g_autoptr (WpIterator) all_hooks = NULL;
|
||||
g_auto (GValue) value = G_VALUE_INIT;
|
||||
const gchar *event_type = NULL;
|
||||
|
||||
g_return_val_if_fail (event != NULL, FALSE);
|
||||
g_return_val_if_fail (WP_IS_EVENT_DISPATCHER (dispatcher), FALSE);
|
||||
|
|
@ -370,8 +371,14 @@ wp_event_collect_hooks (WpEvent * event, WpEventDispatcher * dispatcher)
|
|||
spa_list_init (&result);
|
||||
spa_list_init (&remaining);
|
||||
|
||||
/* Get the event type */
|
||||
event_type = wp_properties_get (event->properties, "event.type");
|
||||
wp_debug_object (dispatcher, "Collecting hooks for event %s with type %s",
|
||||
event->name, event_type);
|
||||
|
||||
/* collect hooks that run for this event */
|
||||
all_hooks = wp_event_dispatcher_new_hooks_iterator (dispatcher);
|
||||
all_hooks = wp_event_dispatcher_new_hooks_for_event_type_iterator (dispatcher,
|
||||
event_type);
|
||||
while (wp_iterator_next (all_hooks, &value)) {
|
||||
WpEventHook *hook = g_value_get_object (&value);
|
||||
|
||||
|
|
|
|||
|
|
@ -881,3 +881,51 @@ wp_object_interest_matches_full (WpObjectInterest * self,
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Finds all the defined constraint values for a subject in \a self.
|
||||
*
|
||||
* A defined constraint value is the value of a constraint with the 'equal' or
|
||||
* 'in-list' verb, because the full value must be defined with those verbs. This
|
||||
* can be useful for cases where we want to enumerate interests that are
|
||||
* interested in specific subjects.
|
||||
*
|
||||
* \ingroup wpobjectinterest
|
||||
* \param self the object interest
|
||||
* \param type the constraint type
|
||||
* \param subject the subject that the constraint applies to
|
||||
* \returns (element-type GVariant) (transfer full) (nullable): the defined
|
||||
* constraint values for this object interest.
|
||||
* \since 0.5.13
|
||||
*/
|
||||
GPtrArray *
|
||||
wp_object_interest_find_defined_constraint_values (WpObjectInterest * self,
|
||||
WpConstraintType type, const gchar * subject)
|
||||
{
|
||||
GPtrArray *res = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify)g_variant_unref);
|
||||
struct constraint *c;
|
||||
|
||||
pw_array_for_each (c, &self->constraints) {
|
||||
if ((c->type == type || WP_CONSTRAINT_TYPE_NONE == type) &&
|
||||
g_str_equal (c->subject, subject)) {
|
||||
switch (c->verb) {
|
||||
case WP_CONSTRAINT_VERB_EQUALS:
|
||||
g_ptr_array_add (res, g_variant_ref (c->value));
|
||||
break;
|
||||
case WP_CONSTRAINT_VERB_IN_LIST: {
|
||||
GVariantIter iter;
|
||||
GVariant *child;
|
||||
g_variant_iter_init (&iter, c->value);
|
||||
while ((child = g_variant_iter_next_value (&iter)))
|
||||
g_ptr_array_add (res, child);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,10 @@ WpInterestMatch wp_object_interest_matches_full (WpObjectInterest * self,
|
|||
WpInterestMatchFlags flags, GType object_type, gpointer object,
|
||||
WpProperties * pw_props, WpProperties * pw_global_props);
|
||||
|
||||
WP_API
|
||||
GPtrArray * wp_object_interest_find_defined_constraint_values (
|
||||
WpObjectInterest * self, WpConstraintType type, const gchar * subject);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpObjectInterest, wp_object_interest_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue