registry: allow plugin and feature filter funcs to call registry API

Don't keep the registry locked whilst iterating over the plugins
or features with a filter function. This would deadlock if the
callback tried to access the registry from the function. Instead,
make a copy of the feature/plugin list and then filter it without
holding the registry lock. This is still considerably faster than
the alternative which would be to use a GstIterator.

https://bugzilla.gnome.org/show_bug.cgi?id=756738
This commit is contained in:
Tim-Philipp Müller 2015-10-17 18:01:47 +01:00
parent 2a023cdc14
commit f02d09c362

View file

@ -148,6 +148,7 @@ struct _GstRegistryPrivate
GList *plugins; GList *plugins;
GList *features; GList *features;
guint n_plugins;
#if 0 #if 0
GList *paths; GList *paths;
#endif #endif
@ -271,6 +272,7 @@ gst_registry_finalize (GObject * object)
plugins = registry->priv->plugins; plugins = registry->priv->plugins;
registry->priv->plugins = NULL; registry->priv->plugins = NULL;
registry->priv->n_plugins = 0;
GST_DEBUG_OBJECT (registry, "registry finalize"); GST_DEBUG_OBJECT (registry, "registry finalize");
p = plugins; p = plugins;
@ -465,6 +467,7 @@ gst_registry_add_plugin (GstRegistry * registry, GstPlugin * plugin)
} }
registry->priv->plugins = registry->priv->plugins =
g_list_remove (registry->priv->plugins, existing_plugin); g_list_remove (registry->priv->plugins, existing_plugin);
--registry->priv->n_plugins;
if (G_LIKELY (existing_plugin->basename)) if (G_LIKELY (existing_plugin->basename))
g_hash_table_remove (registry->priv->basename_hash, g_hash_table_remove (registry->priv->basename_hash,
existing_plugin->basename); existing_plugin->basename);
@ -476,6 +479,8 @@ gst_registry_add_plugin (GstRegistry * registry, GstPlugin * plugin)
plugin, GST_STR_NULL (plugin->filename)); plugin, GST_STR_NULL (plugin->filename));
registry->priv->plugins = g_list_prepend (registry->priv->plugins, plugin); registry->priv->plugins = g_list_prepend (registry->priv->plugins, plugin);
++registry->priv->n_plugins;
if (G_LIKELY (plugin->basename)) if (G_LIKELY (plugin->basename))
g_hash_table_replace (registry->priv->basename_hash, plugin->basename, g_hash_table_replace (registry->priv->basename_hash, plugin->basename,
plugin); plugin);
@ -541,6 +546,7 @@ gst_registry_remove_plugin (GstRegistry * registry, GstPlugin * plugin)
GST_OBJECT_LOCK (registry); GST_OBJECT_LOCK (registry);
registry->priv->plugins = g_list_remove (registry->priv->plugins, plugin); registry->priv->plugins = g_list_remove (registry->priv->plugins, plugin);
--registry->priv->n_plugins;
if (G_LIKELY (plugin->basename)) if (G_LIKELY (plugin->basename))
g_hash_table_remove (registry->priv->basename_hash, plugin->basename); g_hash_table_remove (registry->priv->basename_hash, plugin->basename);
gst_registry_remove_features_for_plugin_unlocked (registry, plugin); gst_registry_remove_features_for_plugin_unlocked (registry, plugin);
@ -657,26 +663,30 @@ GList *
gst_registry_plugin_filter (GstRegistry * registry, gst_registry_plugin_filter (GstRegistry * registry,
GstPluginFilter filter, gboolean first, gpointer user_data) GstPluginFilter filter, gboolean first, gpointer user_data)
{ {
GList *list = NULL; GstPlugin **plugins;
GList *walk, *list = NULL;
guint n_plugins, i;
g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL); g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
GST_OBJECT_LOCK (registry); GST_OBJECT_LOCK (registry);
{ n_plugins = registry->priv->n_plugins;
const GList *walk; plugins = g_newa (GstPlugin *, n_plugins + 1);
for (walk = registry->priv->plugins, i = 0; walk != NULL; walk = walk->next)
plugins[i++] = gst_object_ref (walk->data);
GST_OBJECT_UNLOCK (registry);
for (walk = registry->priv->plugins; walk != NULL; walk = walk->next) { for (i = 0; i < n_plugins; ++i) {
GstPlugin *plugin = walk->data; if (filter == NULL || filter (plugins[i], user_data)) {
list = g_list_prepend (list, gst_object_ref (plugins[i]));
if (filter == NULL || filter (plugin, user_data)) {
list = g_list_prepend (list, gst_object_ref (plugin));
if (first) if (first)
break; break;
} }
} }
}
GST_OBJECT_UNLOCK (registry); for (i = 0; i < n_plugins; ++i)
gst_object_unref (plugins[i]);
return list; return list;
} }
@ -830,26 +840,30 @@ GList *
gst_registry_feature_filter (GstRegistry * registry, gst_registry_feature_filter (GstRegistry * registry,
GstPluginFeatureFilter filter, gboolean first, gpointer user_data) GstPluginFeatureFilter filter, gboolean first, gpointer user_data)
{ {
GList *list = NULL; GstPluginFeature **features;
GList *walk, *list = NULL;
guint n_features, i;
g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL); g_return_val_if_fail (GST_IS_REGISTRY (registry), NULL);
GST_OBJECT_LOCK (registry); GST_OBJECT_LOCK (registry);
{ n_features = g_hash_table_size (registry->priv->feature_hash);
const GList *walk; features = g_newa (GstPluginFeature *, n_features + 1);
for (walk = registry->priv->features, i = 0; walk != NULL; walk = walk->next)
features[i++] = gst_object_ref (walk->data);
GST_OBJECT_UNLOCK (registry);
for (walk = registry->priv->features; walk != NULL; walk = walk->next) { for (i = 0; i < n_features; ++i) {
GstPluginFeature *feature = walk->data; if (filter == NULL || filter (features[i], user_data)) {
list = g_list_prepend (list, gst_object_ref (features[i]));
if (filter == NULL || filter (feature, user_data)) {
list = g_list_prepend (list, gst_object_ref (feature));
if (first) if (first)
break; break;
} }
} }
}
GST_OBJECT_UNLOCK (registry); for (i = 0; i < n_features; ++i)
gst_object_unref (features[i]);
return list; return list;
} }
@ -1533,6 +1547,7 @@ gst_registry_remove_cache_plugins (GstRegistry * registry)
GST_DEBUG_OBJECT (registry, "removing cached plugin \"%s\"", GST_DEBUG_OBJECT (registry, "removing cached plugin \"%s\"",
GST_STR_NULL (plugin->filename)); GST_STR_NULL (plugin->filename));
registry->priv->plugins = g_list_delete_link (registry->priv->plugins, g); registry->priv->plugins = g_list_delete_link (registry->priv->plugins, g);
--registry->priv->n_plugins;
if (G_LIKELY (plugin->basename)) if (G_LIKELY (plugin->basename))
g_hash_table_remove (registry->priv->basename_hash, plugin->basename); g_hash_table_remove (registry->priv->basename_hash, plugin->basename);
gst_registry_remove_features_for_plugin_unlocked (registry, plugin); gst_registry_remove_features_for_plugin_unlocked (registry, plugin);