After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 559209 - Gst::Element::get_factory() - Gst::ElementFactory refcount problem
Gst::Element::get_factory() - Gst::ElementFactory refcount problem
Status: RESOLVED FIXED
Product: gstreamermm
Classification: Bindings
Component: general
git master
Other All
: Normal normal
: ---
Assigned To: gtkmm-forge
gtkmm-forge
Depends on:
Blocks:
 
 
Reported: 2008-11-04 00:09 UTC by RichardF
Modified: 2011-01-16 23:37 UTC
See Also:
GNOME target: ---
GNOME version: 2.23/2.24



Description RichardF 2008-11-04 00:09:08 UTC
Please describe the problem:
Gst::Element::get_factory() appears to currently be coded under the assumption that callers of gst_element_get_factory() must unref the returned GstElementFactory* when they are done with it. 

However, this is not the case (according to the gstreamer gst_element_get_factory() doco: "no refcounting is needed").

As a result, calling Gst::Element::get_factory() twice on the same element causes the element's factory to be prematurely and incorrectly disposed.

Seen in gstreamermm 0.9.7 (and earlier).


Steps to reproduce:
Glib::RefPtr<Gst::Element> element = Gst::ElementFactory::create_element("fakesrc");
{
  std::cout << "Getting factory";
  Glib::RefPtr<Gst::ElementFactory> factory = element->get_factory();
  std::cout << "Factory name: " << factory->get_name();
}
std::cout << "Factory out of scope";
{
  std::cout << "Getting factory again";
  Glib::RefPtr<Gst::ElementFactory> factory = element->get_factory();
  std::cout << "Factory name: " << factory->get_name();
}
std::cout << "Factory out of scope again";



Actual results:
Run with Debug logging enabled, and you should see the element's factory incorrectly get disposed before the last out of scope message.

Not surprisingly, any subsequent calls to get_factory() on the element, incorrectly return NULL;


Expected results:
The factory not to get incorrectly/prematurely disposed.

Does this happen every time?
Yes.

Other information:
I tried the a workaround that passes "true" as the take_copy parameter to Glib::wrap(GstElementFactory*, bool) in Gst::Element::get_factory(), but for some reason I'm not sure about, the refcount on the factory, after all the RefPtr's to it were destructed, ends up being set to 2, rather than 1 like I would expect (in the logging it shows that the first get_factory() call causes 2 refs to take place rather than just 1 - something to do with floating references??).

I'm not sure what the correct solution is: Does there need to be a similar approach to wrapping in get_factory() that is used for the get_structure() call in Gst::Message? Or, something similar to the get_source() call in Gst::Message??
Comment 1 Murray Cumming 2008-11-04 22:15:47 UTC
The fix was simple:

2008-11-04  Murray Cumming  <murrayc@murrayc.com>

	* gstreamer/src/element.hg: get_factory(): Use refreturn.
	refresult is not a valid _WRAP_METHOD() parameter.

Most GTK+ functions needs this. Most gstreamer functions don't, because they return a reference.

Please try to check that this fixes your problem. If not, please try to provide a simple test case for us to compile.
Comment 2 RichardF 2008-11-04 23:55:22 UTC
Thanks Murray. The code that is generated with your change to the .hg file is exactly the (manual) workaround that I settled on while waiting for feedback on this bug.

That said, I'm still confused about the fact that the first call to get_factory() appears to be causing the factory to get ref'ed twice, rather than once?

Simple test:

#include <gstreamermm.h>
#include <iostream>

int main(int argc, char* argv[])
{
    try {
        Gst::init(argc, argv);

        Glib::RefPtr<Gst::Element> element = Gst::ElementFactory::create_element("fakesrc");

        {
            std::cout << "Getting factory" << std::endl;
            Glib::RefPtr<Gst::ElementFactory> factory = element->get_factory();
        }
        std::cout << "Factory RefPtr out of scope" << std::endl;
        {
            std::cout << "Getting factory again" << std::endl;
            Glib::RefPtr<Gst::ElementFactory> factory = element->get_factory();
            if (!factory) throw std::runtime_error("factory is NULL");
        }
        std::cout << "Second factory RefPtr out of scope" << std::endl;
        {
            std::cout << "Getting factory again" << std::endl;
            Glib::RefPtr<Gst::ElementFactory> factory = element->get_factory();
            if (!factory) throw std::runtime_error("factory is NULL");
        }
        std::cout << "Third factory RefPtr out of scope" << std::endl;
    }
    catch (...) {
        std::cout << "Uncaught exception\n";
        return 1;
    }
    std::cout << "Element RefPtr out of scope" << std::endl;

    return 0;
}


Running this prior to the bug fix will cause a failure, running with the bug fix (and GST_DEBUG=*:5) gives me:

Getting factory
0:00:25.098267539 30319      0x1716b50 LOG        GST_REFCOUNTING gstobject.c:326:gst_object_ref:<fakesrc> 0x1875360 ref 1->2
0:00:25.098279077 30319      0x1716b50 LOG        GST_REFCOUNTING gstobject.c:380:gst_object_sink:<fakesrc> sink
0:00:25.098293597 30319      0x1716b50 LOG        GST_REFCOUNTING gstobject.c:326:gst_object_ref:<fakesrc> 0x1875360 ref 2->3
0:00:25.098306008 30319      0x1716b50 LOG        GST_REFCOUNTING gstobject.c:353:gst_object_unref:<fakesrc> 0x1875360 unref 3->2
Factory RefPtr out of scope
Getting factory again
0:00:25.098322418 30319      0x1716b50 LOG        GST_REFCOUNTING gstobject.c:326:gst_object_ref:<fakesrc> 0x1875360 ref 2->3
0:00:25.098332729 30319      0x1716b50 LOG        GST_REFCOUNTING gstobject.c:353:gst_object_unref:<fakesrc> 0x1875360 unref 3->2
Second factory RefPtr out of scope
Getting factory again
0:00:25.098347459 30319      0x1716b50 LOG        GST_REFCOUNTING gstobject.c:326:gst_object_ref:<fakesrc> 0x1875360 ref 2->3
0:00:25.098357584 30319      0x1716b50 LOG        GST_REFCOUNTING gstobject.c:353:gst_object_unref:<fakesrc> 0x1875360 unref 3->2
Third factory RefPtr out of scope

ie before the first get_factory() call the factory refcount is 1, and after the first RefPtr<ElementFactory> goes out of scope the factory refcount is 2 rather than 1. 

All subsequent get_factory calls result in the refcount being increased by 1 only and decreased by 1, as expected, when the RefPtr goes out of scope. 

I'm relatively new to Glib style programming (and to Gstreamer for that matter), so this different first-time code path (with the call the gst_object_sink() - should be a no-op given the factory is owned by the registry isn't it?) and the resulting initial double reference may be expected behaviour, but it does seem strange to me at first glance.

My apologies if there is something basic that I'm missing???


Comment 3 Murray Cumming 2008-11-05 00:21:25 UTC
That debug output is not at all clear to me. I am not convinced that it means what you think it means.

The function is incredibly simple:

GstElementFactory *
gst_element_get_factory (GstElement * element)
{
  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);

  return GST_ELEMENT_GET_CLASS (element)->elementfactory;
}

It does no referencing, so we must take a reference when creating a RefPtr from its result. We then release that refernce in the RefPtr destructor. I don't see how any different refcounting could happen the first or second time that this function is called.

If you are not convinced, you should probably try creating a C test case.
Comment 4 José Alburquerque 2008-11-05 04:28:28 UTC
(In reply to comment #3)
> The function is incredibly simple:
> 
> GstElementFactory *
> gst_element_get_factory (GstElement * element)
> {
>   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
> 
>   return GST_ELEMENT_GET_CLASS (element)->elementfactory;
> }
> 
> It does no referencing, so we must take a reference when creating a RefPtr from
> its result. We then release that refernce in the RefPtr destructor. I don't see
> how any different refcounting could happen the first or second time that this
> function is called.

Exactly.  There is no problem with how get_factory() is written.  What you're seeing is probably a side effect of the floating reference functionality of GstObject (see Description section of GstObject[1]).  We've opted not to include this in gstreamermm at this point because we have automatic referencing, but it's tricky thus the side effect.  Could you please try with the following checked in change?

2008-11-04  José Alburquerque  <jaalburqu@svn.gnome.org>

	* gstreamer/gstreamermm/object.cc: Fix the floating reference aversion
	code so that it checks that the GstObject is floating before
	referencing it and then sinking it.

Also, please try to create attachments in the report; they're easier to use.  Thanks.

[1] http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstObject.html
Comment 5 RichardF 2008-11-05 04:44:06 UTC
José, as it turns out I just came to the same conclusion as you about the use of gst_object_ref() and gst_object_sink() in the Gst::Object constructor, and made the same change as your patch - albeit with a G_LIKELY around the GST_OBJECT_IS_FLOATING, as I guessed that is the most common case ;) - and was about to add the details to this bug report when I saw your post. :)

Your change (along with Murray's previous change) works as desired, and the test code I posted above now results in the underlying GstElementFactory only having the required one remaining reference after all the RefPtrs have destructed.

Thanks for your prompt attention to this problem.

Comment 6 José Alburquerque 2008-11-05 04:58:49 UTC
(In reply to comment #5)
> José, as it turns out I just came to the same conclusion as you about the use
> of gst_object_ref() and gst_object_sink() in the Gst::Object constructor, and
> made the same change as your patch - albeit with a G_LIKELY around the
> GST_OBJECT_IS_FLOATING, as I guessed that is the most common case ;) - and was
> about to add the details to this bug report when I saw your post. :)

I've done the same but will not check it in until a little later with some other things I'm working on.  Thanks for the report.