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 683470 - Unix socket fd leaks and memory leaks when use pipeline
Unix socket fd leaks and memory leaks when use pipeline
Status: RESOLVED NOTABUG
Product: GStreamer
Classification: Platform
Component: gstreamer (core)
0.11.x
Other Linux
: Normal critical
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2012-09-06 09:07 UTC by zhangyanping
Modified: 2012-09-06 12:00 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description zhangyanping 2012-09-06 09:07:20 UTC
We usually use the code as below. 
//=====================================
    GMainLoop *loop;
    GstBus *bus;
    GstElement *pipeline,*source,*typefind,*sink;

    gst_init(NULL, NULL);
    loop = g_main_loop_new(NULL, FALSE);

    //create element
    pipeline   = gst_pipeline_new("my_pipeline");
    typefind  = gst_element_factory_make("typefind","typefind1");
    sink        = gst_element_factory_make("fakesink","sink1");
    source    = gst_element_factory_make("souphttpsrc","source1");

    if (!pipeline || !source || !typefind || !sink)
    {
        return;
    }

    g_object_set (G_OBJECT(source), "location", _cUrl, NULL);

    bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));  //here will ref bus
    gst_bus_add_watch(bus, bus_watch, loop);                 //here will ref bus 
    gst_object_unref(bus);                                               //but only unref bus once

    gst_bin_add_many(GST_BIN(pipeline), source, typefind, sink, NULL);
    gst_element_link(source,typefind);
    gst_element_link(typefind,sink);

    g_main_loop_run(loop);

    gst_element_set_state(pipeline,GST_STATE_NULL);
    gst_object_unref(GST_OBJECT(pipeline));

//=====================================

Gstreamer example code is the same usage.
Note this:

    bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));  //here will ref bus
    gst_bus_add_watch(bus, bus_watch, loop);                 //here will ref bus 
    gst_object_unref(bus);                                               //but only unref bus once

(gst_pipeline_get_bus --> gst_element_get_bus)  will ref bus.
(gst_bus_add_watch ---> gst_bus_add_watch_full_unlocked---> gst_bus_create_watch) will ref bus.
Then will ref bus twice, and only unref once.  So when we  call  "gst_object_unref(GST_OBJECT(pipeline));" at end of the application, the pipeline bus will not freed. 

When the pipeline created, it will create bus, and bus will create gstpoll. look at gstpoll codes,

GstPoll *
gst_poll_new (gboolean controllable)
{
    GstPoll *nset;

    GST_DEBUG ("controllable : %d", controllable);

    nset = g_slice_new0 (GstPoll);
    g_mutex_init (&nset->lock);
#ifndef G_OS_WIN32
    nset->mode = GST_POLL_MODE_AUTO;
    nset->fds = g_array_new (FALSE, FALSE, sizeof (struct pollfd));
    nset->active_fds = g_array_new (FALSE, FALSE, sizeof (struct pollfd));
    nset->control_read_fd.fd = -1;
    nset->control_write_fd.fd = -1;
    {
        gint control_sock[2];

        if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
            goto no_socket_pair;

//=================================

It will use socketpair to create two sockets, but not closed when pipeline destroyed because the bus ref count is 2. 

You can use lsof to check fd number
Comment 1 zhangyanping 2012-09-06 09:09:33 UTC
That's to say the bus of the pipeline is not destroyed, because the ref count of the bus is not 1
Comment 2 Tim-Philipp Müller 2012-09-06 09:16:08 UTC
The bus watch probably keeps a ref to the bus as well. Try:

 id = gst_bus_add_watch (..);
 ...
 g_source_remove (id);
Comment 3 zhangyanping 2012-09-06 09:35:24 UTC
If I use
    gst_element_set_state(pipeline,GST_STATE_NULL);
    gst_object_unref(GST_OBJECT(pipeline));
    g_source_remove(id);

It will be ok. Thank you Tim. I think you should add this to the example code.
Comment 4 Wim Taymans 2012-09-06 09:39:43 UTC
The problem really is because the mainloop is not unreffed.
Comment 5 zhangyanping 2012-09-06 09:43:55 UTC
Then how we can avoid it. Is it a gstreamer inner bug or a programmer bug ?
Comment 6 Wim Taymans 2012-09-06 09:45:18 UTC
programmer bug. Do g_main_loop_unref (loop) after unreffing the pipeline.
Comment 7 Tim-Philipp Müller 2012-09-06 09:47:29 UTC
> The problem really is because the mainloop is not unreffed.

But the watch is hooked up on the default main context (which I believe is never destroyed once created), so you can do multiple g_main_loop_run()/quit()/run(), so I think the fd leak is really due to the source not having been removed.
Comment 8 zhangyanping 2012-09-06 09:51:32 UTC
I use
    gst_element_set_state(pipeline,GST_STATE_NULL);
    gst_object_unref(GST_OBJECT(pipeline));
    g_main_loop_unref(loop);

still have fd leak. Every time leak two fds.
Comment 9 zhangyanping 2012-09-06 09:59:15 UTC
Acossing to my debuging, I think the source is removed.   


  • #0 socketpair
    from /opt/host_libc_arm1176jzf_s/arm-avl-linux-gnueabi/arm-avl-linux-gnueabi/sysroot/lib/libc.so.6
  • #1 gst_poll_new
    at gstpoll.c line 604
  • #2 gst_poll_new_timer
    at gstpoll.c line 667
  • #3 gst_bus_constructed
    at gstbus.c line 150
  • #4 g_object_newv
    from /home/zhangyp/rootfs_for_hunter/usr/lib/libgobject-2.0.so.0
  • #5 gst_bus_new
    at gstbus.c line 270
  • #6 gst_pipeline_init
    at gstpipeline.c line 220
  • #7 g_type_create_instance
    from /home/zhangyp/rootfs_for_hunter/usr/lib/libgobject-2.0.so.0
  • #8 g_object_constructor
    from /home/zhangyp/rootfs_for_hunter/usr/lib/libgobject-2.0.so.0
  • #9 g_object_newv
    from /home/zhangyp/rootfs_for_hunter/usr/lib/libgobject-2.0.so.0
  • #10 g_object_new_valist
    from /home/zhangyp/rootfs_for_hunter/usr/lib/libgobject-2.0.so.0
  • #11 g_object_new
    from /home/zhangyp/rootfs_for_hunter/usr/lib/libgobject-2.0.so.0
  • #12 gst_element_factory_create
    at gstelementfactory.c line 382
  • #13 gst_element_factory_make
    at gstelementfactory.c line 453
  • #14 gst_pipeline_new
    at gstpipeline.c line 314


The leaked fd is created by socketpair. It belongs to the pipeline element, not belongs to src
Comment 10 Wim Taymans 2012-09-06 10:00:19 UTC
Ah, indeed. Then you need to explicitly remove the source or make a custom context. It's not so nice..

Does the source really need a ref to the bus? If removing the last ref on the bus would also remove the source, it would perhaps be nicer..
Comment 11 Wim Taymans 2012-09-06 10:03:02 UTC
(In reply to comment #9)
> Acossing to my debuging, I think the source is removed.   
> 
> The leaked fd is created by socketpair. It belongs to the pipeline element, not
> belongs to src

The socketpair is from the bus object, which is not freed because the source has a ref to it and is not freed.
Comment 12 zhangyanping 2012-09-06 10:04:54 UTC
Hi Wim,
    Then How to write my code. Do you mean

explicitly  call gst_object_unref(GST_OBJECT(src));

or  call gst_object_unref(bus) twice. 

Calling  gst_object_unref(bus) twice  will cause segment failed.
Comment 13 zhangyanping 2012-09-06 10:08:59 UTC
(In reply to comment #11)
> (In reply to comment #9)
> > Acossing to my debuging, I think the source is removed.   
> > 
> > The leaked fd is created by socketpair. It belongs to the pipeline element, not
> > belongs to src
> 
> The socketpair is from the bus object, which is not freed because the source
> has a ref to it and is not freed.


The only way to close the socket is when bus is destroyed. When the bus destroyed, it call gst_poll_free. In gst_poll_free, it will close the socket.

static void
gst_bus_dispose (GObject * object)
{
  GstBus *bus = GST_BUS (object);

  if (bus->priv->queue) {
    GstMessage *message;

    g_mutex_lock (&bus->priv->queue_lock);
    do {
      message = gst_atomic_queue_pop (bus->priv->queue);
      if (message)
        gst_message_unref (message);
    } while (message != NULL);
    gst_atomic_queue_unref (bus->priv->queue);
    bus->priv->queue = NULL;
    g_mutex_unlock (&bus->priv->queue_lock);
    g_mutex_clear (&bus->priv->queue_lock);

    if (bus->priv->poll)
      gst_poll_free (bus->priv->poll);
    bus->priv->poll = NULL;
  }

  G_OBJECT_CLASS (parent_class)->dispose (object);
}
Comment 14 zhangyanping 2012-09-06 10:11:36 UTC
I think the cause is that the bus is not freed. How to free the bus?
Comment 15 Wim Taymans 2012-09-06 10:13:00 UTC
(In reply to comment #14)
> I think the cause is that the bus is not freed. How to free the bus?

What Tim said:

 id = gst_bus_add_watch (..);
 ...
 g_source_remove (id);
Comment 16 zhangyanping 2012-09-06 10:15:57 UTC
(In reply to comment #15)
> (In reply to comment #14)
> > I think the cause is that the bus is not freed. How to free the bus?
> 
> What Tim said:
> 
>  id = gst_bus_add_watch (..);
>  ...
>  g_source_remove (id);

Yes , this way is ok as I had replied in comment 3.
Comment 17 zhangyanping 2012-09-06 11:55:43 UTC
Hello All,
        Thank you for your replay with patience.  I think gst example codes should also add this code. I will mark it NOTABUG.
Comment 18 Tim-Philipp Müller 2012-09-06 12:00:19 UTC
We'll fix the example, thanks.