Signal handling causes memory leaks

I reported this as bug #1724347, but it seems like the bug-list is less
active than the forum list, so I’ll repost it here just in case someone
has an idea to help me.

I have a ruby defined GObject that is a singleton and is “alive” as long
as
the Application is running.
This GObject represents the state of a machine. I use several views
defined
in glade so the user can get different views of the machine depending on
their interest.The views are loaded with libglade
then “filled” with several controls programatically.
This controls have to update their values when the GObject emits
signals.So
far so good, I’ve set this up and it works well.

The problem is that when I destroy a view, the GRClosure for the signal
keeps around since it is attached to the main GObject (the one
representing
the state of the machine) and even doing a signal_disconnect_handler
will
not free it. Since the callback has to reference the view, that means
that
the view doesn’t get garbage collected leading to a seious memory leak
wich
I see no way to avoid. The reasonable behaviour would be to dettach the
callback from the GObject once it has been disconnected.

Attached to the bug report is a simple test that reproduces the bug.

Cheers, (and thanks for your incredible work)
V. Segui

Sorry, I forgot to mention that I can reproduce the bug with
ruby-gnome-0.16 and ruby-gnome-0.14. Ruby versions tested are ruby-1.8.4
and ruby-1.8.5.

Cheers
V. Segui

Hi,

In [email protected]
“[ruby-gnome2-devel-en] Signal handling causes memory leaks” on Tue,
19 Jun 2007 08:27:09 +0200,
Vicent S. [email protected] wrote:

callback from the GObject once it has been disconnected.

Attached to the bug report is a simple test that reproduces the bug.

I don’t think it’s not a memory leak. Could you add the
following code to your sample script and try again?

p :finished

leaked_objects = Array.new
11.times{ |i|
hash_objects=Hash.new{|h,k|h[k]=0}

GC.start
ObjectSpace.each_object{|obj| hash_objects[obj.class]+=1 }
 leaked_objects<<hash_objects if i==0 or i==10

}
leaked_objects.each_with_index{|h,i|
puts “LEAKED OBJECTS #{i}”
h2 = leaked_objects[i+1]
h2 = Hash.new{|h,k|h[k]=0} if not h2

 h.each_pair{|k,v|
   puts "Leaked #{k}: #{h2[k]-v}" if (h2[k]-v)>0
 }
 h2.each_pair{|k,v|
    puts "Leakdes #{k}: #{v-h[k]}" if (h[k] == 0) && (v != 0)
 }

}

Thanks,

kou


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Hi,

In [email protected]
“Re: [ruby-gnome2-devel-en] Signal handling causes memory leaks” on
Tue, 19 Jun 2007 13:09:13 +0200,
Vicent S. [email protected] wrote:

Thanks for your quick reply. I’m a little confused at what you are
trying here, so where do you want me to add the above code? Should I
delete my own code? I actually just added it yo my own code so correct
me if I’m wrong. Here are my results:

I wanted to show your test script doesn’t show what you want
to show. Could you rewrite a test script? The test script
will have a GObject and some GtkWidgets. And the GtkWidgets
will create and destroy many times. And then (or each time)
your test script will report alive objects.

Thanks,

kou


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Hikou,

I just noticed the following when using the test. If I create a
Gtk::Button and assign it to a var, it will “leak”, even if i don’t add
it to the box. If I just create the GtkButton but don’t assign it it wil
not “leak”. The only reason I can see for this behaviour is because the
GRClosure we create below for the signal captures it in its lexical
scope.

    box = Gtk::VBox.new(2)
    button = Gtk::Button.new()
    @window.add box
    id_signal = nil
    #when we create this closure we capture the button
    # and it leaks since the closure is not released even
    # when disconnected
    id_signal = self.signal_connect("test") { |gobj|
       box.add Gtk::Label.new("Test")
    }

Cheers,
V. Segui

I wanted to show your test script doesn’t show what you want
to show.

Thanks,

kou

Hi Kou,
I think the script does show a problem, the point is that if I comment
the signal_connection code , that is if the following code is commented:

    #if we comment this we won't get the leakage
      id_signal = self.signal_connect("test") { |gobj|
         box.add Gtk::Label.new("Test")
      }
     #now disconnect the signals if not needed
     @window.signal_connect("destroy") { |win|
        self.signal_handler_disconnect(id_signal)if id_signal
     }

then there is no “memory leak”:

LEAKED OBJECTS 0
Leaked Hash: 2
Leaked Proc: 2
LEAKED OBJECTS 1
:finished
LEAKED OBJECTS 0
Leaked Proc: 1
LEAKED OBJECTS 1

I assume you mean that my script isn’t accurate measuring the memory
leak, which is true, but I have observed the “leak” acting in my app
and it disappears if I comment the signal connection code.

Could you rewrite a test script? The test script
will have a GObject and some GtkWidgets. And the GtkWidgets
will create and destroy many times. And then (or each time)
your test script will report alive objects.

This is basically what I’m doing, I create the view every time, and then
record the alive objects. At the end, I calculate the difference between
the live objects at the beginning and those at the end of the iterations
(the code is horrible, I know, but then I was in a hurry) and print
that. The only relevant objects are those of the Gtk hierarchy of
course, since ruby may be doing it’s own stuff in the meantime.
If you still need another test, please let me know, and I will see what
I can bake up…

Cheers,
V. Segui

Hi kou,

Thanks for your quick reply. I’m a little confused at what you are
trying here, so where do you want me to add the above code? Should I
delete my own code? I actually just added it yo my own code so correct
me if I’m wrong. Here are my results:

LEAKED OBJECTS 0
Leaked Hash: 12
Leaked Gtk::VBox: 10
Leaked String: 19
Leaked Proc: 12
Leaked Data: 10
Leaked Array: 10
LEAKED OBJECTS 1
:finished
LEAKED OBJECTS 0
Leaked Proc: 1
LEAKED OBJECTS 1

Again, thanks for your quick reply,

Cheers,
V. Segui

Hi Vicent,

I don’t know what is the correct result by your
test_signal_disconnection.rb.

My result was:

LEAKED OBJECTS 0
Leaked Hash: 2
Leaked Proc: 2
Leaked Data: 10
Leaked Array: 1
LEAKED OBJECTS 1

Where is the point ?
Do you mean there are 15(2+2+10+1) objects leaked?
Should all lines be 0 ?

On Tue, 19 Jun 2007 08:27:09 +0200
Vicent S. [email protected] wrote:

their interest.The views are loaded with libglade
not free it. Since the callback has to reference the view, that means

[email protected]
ruby-gnome2-devel-en List Signup and Options


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Where is the point ?
Do you mean there are 15(2+2+10+1) objects leaked?
Should all lines be 0 ?

Hi Masao,
All lines need not be 0 (we have to consider for the “leak detection”
algorithim’s own memory use), but I should not have Gtk objects printed
(which I do) there since they should be created and destroyed properly.
Unfortunately, after I run test_signal_connection.rb, with the signal
connection code uncommented, I get 10 Gtk::Boxes or whatever Gtk object
I create and assign to a variable in the lexical scope of the signal
closure. The only reason for this is that

a) we are not properly releasing the GRClosure
b) We do release the GRClosure but this one somehow does not release
it’s “links” to the variables
c) There is something wrong in my “leak” detection algorithm wich only
manifests itself when I do the signal_connect stuff. However, please
note that running that code enough times will actually see a real
increment in memory use as measured by top.

Thank you for your time, cheers,

V. Segui

Hi,

On Tue, 19 Jun 2007 23:12:31 +0200
Guest [email protected] wrote:

I create and assign to a variable in the lexical scope of the signal
closure. The only reason for this is that

a) we are not properly releasing the GRClosure
b) We do release the GRClosure but this one somehow does not release
it’s “links” to the variables
c) There is something wrong in my “leak” detection algorithm wich only
manifests itself when I do the signal_connect stuff. However, please
note that running that code enough times will actually see a real
increment in memory use as measured by top.

For understanding, I need to try to reproduce your problem easier.
So I wrote 2 small samples.

(1)

require ‘gtk2’
def test
a = Gtk::Button.new
handler_id = a.signal_connect(“clicked”){1 + 1}

a.signal_handler_disconnect(handler_id)

a.destroy

end

loop {
test
GC.start
}

(2)

require ‘gtk2’
def test
a = Gtk::Button.new
a.signal_connect(“clicked”){ 1 + 1 }

a.signal_handler_disconnect(handler_id)

a.destroy

end

10000.times {
test
}
GC.start
objs = []
ObjectSpace.each_object(GLib::Object){|obj|
objs << obj
}
p objs

I test (1) with “top” command, but it doesn’t seem to
leak memory.

Then I test (2), the result was:
[#<Gtk::Button:0x2aaaafdad2a0 ptr=0xd79800>]

Actually there is an object which is not released yet,
but I think this is not your problem.

% ruby -v -rgtk2 -W0 -e ‘p Gtk::BINDING_VERSION’
ruby 1.8.6 (2007-03-13 patchlevel 0) [x86_64-linux]
[0, 16, 0]


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Hi Masao M.,

Actually there is an object which is not released yet,
but I think this is not your problem.

Thank you for your reply. Actually the problem is related, try with
this code:
1)
require ‘gtk2’

def test(obj)
a = Gtk::Button.new
handler_id = obj.signal_connect(“clicked”){ 1 + 1 }
obj.signal_handler_disconnect(handler_id)
a.destroy
end
obj = Gtk::Button.new
10000.times {
test(obj)
}
GC.start
objs = []
ObjectSpace.each_object(GLib::Object){|obj|
objs << obj
}
p objs

In my machine it will print thousands of Gtk::Buttons that are
Glib::destroyed but not GC. My guess is that this is probably because
the closure created with obj.signal_connect still has a reference to the
Buttons and it isn’t realeasing it, even if we disconnect it. You can
test it by commenting the signal_comment code like this:

def test(obj)
a = Gtk::Button.new

handler_id = obj.signal_connect(“clicked”){ 1 + 1 }

obj.signal_handler_disconnect(handler_id)

a.destroy
end
obj = Gtk::Button.new
10000.times {
test(obj)
}
GC.start
objs = []
ObjectSpace.each_object(GLib::Object){|obj|
objs << obj
}
p objs

and it will print:

[#<Gtk::Button:0xb6a9b904 destroyed>, #<Gtk::Button:0xb6b0780c
ptr=0x8280510>]
instead of the thousand instances of #<Gtk::Button:0xb6a9b904
destroyed> it prints when 1) is run. I hope this clarifies my issue
better.

thanks for your time, cheers,
V. Segui

Hi,

From: Vicent S. [email protected]
Date: Thu, 21 Jun 2007 08:47:08 +0200

In my machine it will print thousands of Gtk::Buttons that are
Glib::destroyed but not GC. My guess is that this is probably because
the closure created with obj.signal_connect still has a reference to the
Buttons and it isn’t realeasing it, even if we disconnect it.

Your guess is correct.
obj still has a reference to the signal handler even after we
disconnect it. Then the Button is also refered by obj via the signal
handler’s Proc object.

Here is a makeshift patch against ruby-gnome2-0.16 that fix this
problem partially.

— rbgobj_closure.c.orig 2006-12-29 22:17:29.000000000 +0900
+++ rbgobj_closure.c 2007-06-21 22:55:47.000000000 +0900
@@ -243,8 +243,10 @@ rclosure_unref(GRClosure *rclosure)
if (!rclosure_alive_p(rclosure)) {
GList *next;
for (next = rclosure->objects; next; next = next->next) {

  •        GObject *object;
    
  •        object = G_OBJECT(next->data);
    
  •        GObject *object = G_OBJECT(next->data);
    
  •        VALUE obj = rbgobj_ruby_object_from_instance2(object, 
    

FALSE);

  •        if (!NIL_P(rclosure->rb_holder) && !NIL_P(obj))
    
  •            G_CHILD_REMOVE(obj, rclosure->rb_holder);
           g_object_weak_unref(object, rclosure_weak_notify, 
    

rclosure);
}
g_list_free(rclosure->objects);

– Masahiro S.


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Hi Vicent,

On Thu, 21 Jun 2007 16:54:39 +0200
Vicent S. [email protected] wrote:

@@ -243,8 +243,10 @@ rclosure_unref(GRClosure *rclosure)
g_object_weak_unref(object, rclosure_weak_notify,
http://sourceforge.net/powerbar/db2/

Hi Masao,

Oops, Unfortunately, thie patch is Masahiro S., not me.

Thanks a lot for the patch. I know things have changed a lot since
ruby-gnome 0.14 but will this patch also apply there? At the moment I
can’t upgrade to 0.16 since I’m still getting hit ocasionally (which is
why I haven’t been able to reproduce a test case to report) with the
“GRClosure already destroyed” (like in Bug #1637290) issue.

Ruby-GNOME2 0.14.0 has other memory leaks problems and many bugs.
I highly recommand to upgrade it to 0.16.


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Masahiro S. wrote:

Hi,

From: Vicent S. [email protected]
Date: Thu, 21 Jun 2007 08:47:08 +0200

In my machine it will print thousands of Gtk::Buttons that are
Glib::destroyed but not GC. My guess is that this is probably because
the closure created with obj.signal_connect still has a reference to the
Buttons and it isn’t realeasing it, even if we disconnect it.

Your guess is correct.
obj still has a reference to the signal handler even after we
disconnect it. Then the Button is also refered by obj via the signal
handler’s Proc object.

Here is a makeshift patch against ruby-gnome2-0.16 that fix this
problem partially.

— rbgobj_closure.c.orig 2006-12-29 22:17:29.000000000 +0900
+++ rbgobj_closure.c 2007-06-21 22:55:47.000000000 +0900
@@ -243,8 +243,10 @@ rclosure_unref(GRClosure *rclosure)
if (!rclosure_alive_p(rclosure)) {
GList *next;
for (next = rclosure->objects; next; next = next->next) {

  •        GObject *object;
    
  •        object = G_OBJECT(next->data);
    
  •        GObject *object = G_OBJECT(next->data);
    
  •        VALUE obj = rbgobj_ruby_object_from_instance2(object, 
    

FALSE);

  •        if (!NIL_P(rclosure->rb_holder) && !NIL_P(obj))
    
  •            G_CHILD_REMOVE(obj, rclosure->rb_holder);
           g_object_weak_unref(object, rclosure_weak_notify, 
    

rclosure);
}
g_list_free(rclosure->objects);

– Masahiro S.


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Hi Masao,
Thanks a lot for the patch. I know things have changed a lot since
ruby-gnome 0.14 but will this patch also apply there? At the moment I
can’t upgrade to 0.16 since I’m still getting hit ocasionally (which is
why I haven’t been able to reproduce a test case to report) with the
“GRClosure already destroyed” (like in Bug #1637290) issue.

Thanks a lot for looking in to this,
V. Segui

Hi,

On Thu, 21 Jun 2007 08:47:08 +0200
Vicent S. [email protected] wrote:

def test(obj)
objs = []
ObjectSpace.each_object(GLib::Object){|obj|
objs << obj
}
p objs

In my machine it will print thousands of Gtk::Buttons that are
Glib::destroyed but not GC. My guess is that this is probably because
the closure created with obj.signal_connect still has a reference to the
Buttons and it isn’t realeasing it, even if we disconnect it.

Hmm, unless Masahiro’s patch, it printed:
[#<Gtk::Button:0x2aaaafe04550 destroyed>, #<Gtk::Button:0x2aaaafe40cf8
ptr=0x917830>]

Of course it’s on 0.16.0. This result is the same with Masahiro’s patch.

I think Masahiro’s patch makes the thing better, but there are other
problems on 0.14.0
which have been fixed until 0.16.0.


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Masao M. wrote:

Of course it’s on 0.16.0. This result is the same with Masahiro’s patch.

Hi Masao,
Sorry for the confusion :). Many thanks Sakai for the patch!. I’ve
tried this with both ruby-gnome 0.16 and ruby-gnome 0.14 and both seem
to leak (I haven’t tried Sakai’s patch still) can you confirm that you
are running the testcase 1 of my previous post (the one with the
signal_connect code uncommented)?

Cheers,
V. Segui

I think Masahiro’s patch makes the thing better, but there are other
problems on 0.14.0
which have been fixed until 0.16.0.

I’ve tried 0.16 but I get the dreaded “GRClosure already destroyed” bug.
I will give a try to the CVS version (if I can’t apply this patch to
0.14) ASAP and report. Many thanks to all of you,

V. Segui

Hi Sakai-san,

Great!
Could you apply this patch to CVS ?

On Thu, 21 Jun 2007 23:42:34 +0900 (JST)
Masahiro S. [email protected] wrote:

Your guess is correct.
if (!rclosure_alive_p(rclosure)) {
g_list_free(rclosure->objects);
[email protected]
ruby-gnome2-devel-en List Signup and Options


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

Hi,

On Thu, 21 Jun 2007 17:40:35 +0200
Vicent S. [email protected] wrote:

signal_connect code uncommented)?
Oops. I was wrong. I tested the CVS version unless Masahiro’s patch.
On pure 0.16, the thousand Gtk::Button(destroyed) was remained.
Sorry for confusing you.

I’ve tried 0.16 but I get the dreaded “GRClosure already destroyed” bug.
I will give a try to the CVS version (if I can’t apply this patch to
0.14) ASAP and report. Many thanks to all of you,

I hope the latest version(CVS + Masahiro’s patch) fix your problem.


This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

I hope the latest version(CVS + Masahiro’s patch) fix your problem.

I still haven’t tried CVS (it’s blocked by the firewall :frowning: ) but using
the patch on pristine 0.16 will make it segfault ocasionally when doing
test_signal_connect2.rb with the signal_connect uncommented. Just
reporting in case you guys are interested. Will try to get the admin to
open the firewall and try again.

Cheers,
V. Seguí