registry: don't recreate features on first use. Fixes #584389
authorStefan Kost <ensonic@users.sf.net>
Sun, 7 Jun 2009 19:09:14 +0000 (22:09 +0300)
committerStefan Kost <ensonic@users.sf.net>
Sun, 7 Jun 2009 20:48:59 +0000 (23:48 +0300)
The first time one calls gst_element_factory_make(), gst recreates the plugin
feature and the element factory. As a side effect we ref the class to fill
in detail we already have filled from the registry cache. This patch changes
the behaviour to just update the existing entries. The factory is now attached
to the type and set in gst_element_base_class_init().

gst/gstelement.c
gst/gstelementfactory.c
gst/gstplugin.c
win32/common/libgstreamer.def

index be848b2..2a55d87 100644 (file)
@@ -145,6 +145,9 @@ static void gst_element_restore_thyself (GstObject * parent, xmlNodePtr self);
 static GstObjectClass *parent_class = NULL;
 static guint gst_element_signals[LAST_SIGNAL] = { 0 };
 
+/* this is used in gstelementfactory.c:gst_element_register() */
+GQuark _gst_elementclass_factory = 0;
+
 GType
 gst_element_get_type (void)
 {
@@ -167,6 +170,9 @@ gst_element_get_type (void)
 
     _type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
         &element_info, G_TYPE_FLAG_ABSTRACT);
+
+    _gst_elementclass_factory =
+        g_quark_from_static_string ("GST_ELEMENTCLASS_FACTORY");
     g_once_init_leave (&gst_element_type, _type);
   }
   return gst_element_type;
@@ -252,6 +258,13 @@ gst_element_base_class_init (gpointer g_class)
    */
   memset (&element_class->details, 0, sizeof (GstElementDetails));
   element_class->padtemplates = NULL;
+
+  /* set the factory, see gst_element_register() */
+  element_class->elementfactory =
+      g_type_get_qdata (G_TYPE_FROM_CLASS (element_class),
+      _gst_elementclass_factory);
+  GST_DEBUG ("type %s : factory %p", G_OBJECT_CLASS_NAME (element_class),
+      element_class->elementfactory);
 }
 
 static void
index be8f765..123e3e1 100644 (file)
@@ -79,6 +79,9 @@ static GstPluginFeatureClass *parent_class = NULL;
 
 /* static guint gst_element_factory_signals[LAST_SIGNAL] = { 0 }; */
 
+/* this is defined in gstelement.c */
+extern GQuark _gst_elementclass_factory;
+
 #define _do_init \
 { \
   GST_DEBUG_CATEGORY_INIT (element_factory_debug, "GST_ELEMENT_FACTORY", \
@@ -193,8 +196,7 @@ gst_element_factory_cleanup (GstElementFactory * factory)
 
   __gst_element_details_clear (&factory->details);
   if (factory->type) {
-    g_type_class_unref (g_type_class_peek (factory->type));
-    factory->type = 0;
+    factory->type = G_TYPE_INVALID;
   }
 
   for (item = factory->staticpadtemplates; item; item = item->next) {
@@ -249,6 +251,8 @@ gboolean
 gst_element_register (GstPlugin * plugin, const gchar * name, guint rank,
     GType type)
 {
+  GstPluginFeature *existing_feature;
+  GstRegistry *registry;
   GstElementFactory *factory;
   GType *interfaces;
   guint n_interfaces, i;
@@ -258,6 +262,24 @@ gst_element_register (GstPlugin * plugin, const gchar * name, guint rank,
   g_return_val_if_fail (name != NULL, FALSE);
   g_return_val_if_fail (g_type_is_a (type, GST_TYPE_ELEMENT), FALSE);
 
+  registry = gst_registry_get_default ();
+
+  /* check if feature already exists, if it exists there is no need to update it
+   * when the registry is getting updated, outdated plugins and all there
+   * feature are removed and readded.
+   */
+  existing_feature = gst_registry_lookup_feature (registry, name);
+  if (existing_feature) {
+    GST_DEBUG_OBJECT (registry, "update existing feature %p (%s)",
+        existing_feature, name);
+    factory = GST_ELEMENT_FACTORY_CAST (existing_feature);
+    factory->type = type;
+    existing_feature->loaded = TRUE;
+    g_type_set_qdata (type, _gst_elementclass_factory, factory);
+    gst_object_unref (existing_feature);
+    return TRUE;
+  }
+
   factory =
       GST_ELEMENT_FACTORY_CAST (g_object_new (GST_TYPE_ELEMENT_FACTORY, NULL));
   gst_plugin_feature_set_name (GST_PLUGIN_FEATURE_CAST (factory), name);
@@ -284,7 +306,7 @@ gst_element_register (GstPlugin * plugin, const gchar * name, guint rank,
         g_list_append (factory->staticpadtemplates, newt);
   }
   factory->numpadtemplates = klass->numpadtemplates;
-  klass->elementfactory = factory;
+  g_type_set_qdata (type, _gst_elementclass_factory, factory);
 
   /* special stuff for URI handling */
   if (g_type_is_a (type, GST_TYPE_URI_HANDLER)) {
@@ -322,8 +344,7 @@ gst_element_register (GstPlugin * plugin, const gchar * name, guint rank,
   gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE_CAST (factory), rank);
   GST_PLUGIN_FEATURE_CAST (factory)->loaded = TRUE;
 
-  gst_registry_add_feature (gst_registry_get_default (),
-      GST_PLUGIN_FEATURE_CAST (factory));
+  gst_registry_add_feature (registry, GST_PLUGIN_FEATURE_CAST (factory));
 
   return TRUE;
 
@@ -389,12 +410,12 @@ gst_element_factory_create (GstElementFactory * factory, const gchar * name)
 
   /* fill in the pointer to the factory in the element class. The
    * class will not be unreffed currently. 
-   * FIXME: This isn't safe and may leak a refcount on the factory if 2 threads
-   * create the first instance of an element at the same moment */
+   * Be thread safe as there might be 2 threads creating the first instance of
+   * an element at the same moment
+   */
   oclass = GST_ELEMENT_GET_CLASS (element);
-  if (G_UNLIKELY (oclass->elementfactory == NULL))
-    oclass->elementfactory = factory;
-  else
+  if (!g_atomic_pointer_compare_and_exchange (
+          (gpointer *) & oclass->elementfactory, NULL, factory))
     gst_object_unref (factory);
 
   if (name)
@@ -407,7 +428,8 @@ gst_element_factory_create (GstElementFactory * factory, const gchar * name)
   /* ERRORS */
 load_failed:
   {
-    GST_WARNING_OBJECT (factory, "loading plugin returned NULL!");
+    GST_WARNING_OBJECT (factory,
+        "loading plugin containing feature %s returned NULL!", name);
     return NULL;
   }
 no_type:
index f80ddac..a27ef69 100644 (file)
@@ -535,6 +535,7 @@ gst_plugin_load_file (const gchar * filename, GError ** error)
   gpointer ptr;
   struct stat file_status;
   GstRegistry *registry;
+  gboolean new_plugin = TRUE;
 
   g_return_val_if_fail (filename != NULL, NULL);
 
@@ -544,11 +545,12 @@ gst_plugin_load_file (const gchar * filename, GError ** error)
   plugin = gst_registry_lookup (registry, filename);
   if (plugin) {
     if (plugin->module) {
+      /* already loaded */
       g_static_mutex_unlock (&gst_plugin_loading_mutex);
       return plugin;
     } else {
-      gst_object_unref (plugin);
-      plugin = NULL;
+      /* load plugin and update fields */
+      new_plugin = FALSE;
     }
   }
 
@@ -586,13 +588,14 @@ gst_plugin_load_file (const gchar * filename, GError ** error)
     goto return_error;
   }
 
-  plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
-
+  if (new_plugin) {
+    plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
+    plugin->file_mtime = file_status.st_mtime;
+    plugin->file_size = file_status.st_size;
+  }
   plugin->module = module;
   plugin->filename = g_strdup (filename);
   plugin->basename = g_path_get_basename (filename);
-  plugin->file_mtime = file_status.st_mtime;
-  plugin->file_size = file_status.st_size;
 
   ret = g_module_symbol (module, "gst_plugin_desc", &ptr);
   if (!ret) {
@@ -606,15 +609,17 @@ gst_plugin_load_file (const gchar * filename, GError ** error)
   }
   plugin->orig_desc = (GstPluginDesc *) ptr;
 
-  /* check plugin description: complain about bad values but accept them, to
-   * maintain backwards compatibility (FIXME: 0.11) */
-  CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, name, filename);
-  CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, description, filename);
-  CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, version, filename);
-  CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, license, filename);
-  CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, source, filename);
-  CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, package, filename);
-  CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, origin, filename);
+  if (new_plugin) {
+    /* check plugin description: complain about bad values but accept them, to
+     * maintain backwards compatibility (FIXME: 0.11) */
+    CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, name, filename);
+    CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, description, filename);
+    CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, version, filename);
+    CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, license, filename);
+    CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, source, filename);
+    CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, package, filename);
+    CHECK_PLUGIN_DESC_FIELD (plugin->orig_desc, origin, filename);
+  }
 
   GST_LOG ("Plugin %p for file \"%s\" prepared, calling entry function...",
       plugin, filename);
@@ -645,8 +650,10 @@ gst_plugin_load_file (const gchar * filename, GError ** error)
   _gst_plugin_fault_handler_filename = NULL;
   GST_INFO ("plugin \"%s\" loaded", plugin->filename);
 
-  gst_object_ref (plugin);
-  gst_default_registry_add_plugin (plugin);
+  if (new_plugin) {
+    gst_object_ref (plugin);
+    gst_default_registry_add_plugin (plugin);
+  }
 
   g_static_mutex_unlock (&gst_plugin_loading_mutex);
   return plugin;
index ac0c3b8..7cb014c 100644 (file)
@@ -41,6 +41,7 @@ EXPORTS
        _gst_debug_nameof_funcptr
        _gst_debug_register_funcptr
        _gst_element_error_printf
+       _gst_elementclass_factory DATA
        _gst_plugin_register_static
        _gst_trace_add_entry
        _gst_trace_on DATA