Forum: wxRuby wxruby 1.9.9 menu - strange corruption

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ridge M. (Guest)
on 2009-01-02 19:39
Attachment: test.rb (0 Bytes)
Environment:
Windows XP Pro + SP3
ruby (1.8.6)
wxruby (1.9.9)

Please see attached code (smallest code to reproduce).
I'd like to know if others can reproduce this problem.
I completely removed and then re-installed ruby and wxruby
and was able to reproduce.

I'm subclassing menus to add an extra tag.
However, after some repeated menu use, the menu
item becomes corrupted.  The more menu items
there are, the more quickly the menu item becomes
corrupted.

It appears (speculation) that a garbage collector
or some memory reclamation code is at work that
reallocates the MyMenuItem and transforms it into
a MenuItem.

Anybody able to reproduce?
Alex F. (Guest)
on 2009-01-04 02:49
(Received via mailing list)
Hi

Ridge M. wrote:
> I'm subclassing menus to add an extra tag.
> However, after some repeated menu use, the menu
> item becomes corrupted.  The more menu items
> there are, the more quickly the menu item becomes
> corrupted.
>
> It appears (speculation) that a garbage collector
> or some memory reclamation code is at work that
> reallocates the MyMenuItem and transforms it into
> a MenuItem.

Hopefully I can explain. You're right that it's to do with garbage
collection. When Ruby's GC runs, there's no reference anywhere to the
MyMenuItems, so they're swept away and the ruby object is deleted. Next
time find_item is called, there is no Ruby object matching the found
MenuItem, so it is re-wrapped as an ordinary MenuItem.

This is expected behaviour - "info" type objects such as MenuItem are
not preserved. I'll have a look whether there is a way to change this
without causing crashes or leaking memory, but I would suggest that you
find an alternate way round this:

1) Keep a reference (eg in an Array or Hash as a member of MyMenu) to
each menu item, perhaps setting this up as part of the addEntry method
of MyMenu. This will preserve the items from GC.

2) Override MyMenu#find_item to return a MyMenuItem, rather than an
ordinary MenuItem.

hth
alex
Ridge M. (Guest)
on 2009-01-05 01:22
Alex F. wrote:
> Hi
>
> Ridge M. wrote:
>> I'm subclassing menus to add an extra tag.
>> However, after some repeated menu use, the menu
>> item becomes corrupted.  The more menu items
>> there are, the more quickly the menu item becomes
>> corrupted.
>>
>> It appears (speculation) that a garbage collector
>> or some memory reclamation code is at work that
>> reallocates the MyMenuItem and transforms it into
>> a MenuItem.
>
> Hopefully I can explain. You're right that it's to do with garbage
> collection. When Ruby's GC runs, there's no reference anywhere to the
> MyMenuItems, so they're swept away and the ruby object is deleted. Next
> time find_item is called, there is no Ruby object matching the found
> MenuItem, so it is re-wrapped as an ordinary MenuItem.
>
> This is expected behaviour - "info" type objects such as MenuItem are
> not preserved. I'll have a look whether there is a way to change this
> without causing crashes or leaking memory, but I would suggest that you
> find an alternate way round this:
>
> 1) Keep a reference (eg in an Array or Hash as a member of MyMenu) to
> each menu item, perhaps setting this up as part of the addEntry method
> of MyMenu. This will preserve the items from GC.
>
> 2) Override MyMenu#find_item to return a MyMenuItem, rather than an
> ordinary MenuItem.
>
> hth
> alex

Thanks for your response.

   Good suggestion (1) and that is actually what I did to move forward.
   However, I consider it a work-around.  I welcome your explanations
   because I want to understand.

   Here's my view:
   As a developer, I don't want just the "native" superclass to work
   properly with the GC.  I want my sub-classes to work properly, too.
   (e.g., my sub-class should inherit the reference count or whatever
   to make it a fully-qualified class, huh?)  If I understood your
   message, the GC is making decisions based on the type (MenuItem).
   Isn't this just what an object-oriented system should *not* do?

   Some questions:
   Is this a generic ruby issue (or is it unique to wxruby)?

   What triggers the GC ?  (While testing, I found that the "rewrapping"
   occurred whether or not I called find_item.  Just repeatedly
selecting
   the MyMenuItem, which triggered a menu_event and caused on_menu_event
   to be called, would result in the MyMenuItem being "rewrapped" as a
   MenuItem.  As such, over-riding MyMenu#find_item would not be
expected
   to solve the problem.)

   I appreciate your offer to "have a look" and I'd be interested to
   know whatever you find out.

Cheers,
Ridge
Mario S. (Guest)
on 2009-01-05 06:39
(Received via mailing list)
Hello Ridge,

Let me explain this a bit further in detail as to the process the
Garbage
Collector, and wxRuby's own classes work about things.

On Sun, Jan 4, 2009 at 5:22 PM, Ridge M. <removed_email_address@domain.invalid>
wrote:

>   to make it a fully-qualified class, huh?)  If I understood your
>   message, the GC is making decisions based on the type (MenuItem).
>   Isn't this just what an object-oriented system should *not* do?


What Alex was trying to explain here, is that some classes, such as
Menu,
MenuItem, Rect, Point, and various other ones, are considered
Information
Classes.  Information classes only hold the information of what is
relevent
at the point and time of the execution of the code.  To make it a bit
more
clearer to understand, let's take the problem of MenuItem.  Imagine that
MenuItem is a Ruby Struct class.

Defining it as such:

MenuItem = Struct.new(:title,:id,:parent,:bitmap)

my_new_menu_item =
MenuItem.new("&Open",Wx::ID_OPEN,my_file_menu,Wx::ICON_FOLDER)

This is a silly mockup of what I'm trying to explain, but this is what
it
boils down to.  Once you actually create the control, there's no further
need to reference this control, till something happens with it, such as
evt_menu(Wx::ID_OPEN), this is where we re-retrive the actual wxRuby
control, for any association needed to do special things, such as
changing
the title of the Menu item.  There is no other things you can really do
with
a MenuItem, aside from either updating it, or deleting it.  Atleast, as
far
as wxWidgets is concerned.  Therefore, if you don't store the instance
of
the MenuItem in a variable, where the Garbage Collector can see that it
is
still being referenced within your code, Ruby thinks that it is no
longer
needed, and therefore frees it up for others to use.

  Some questions:
>   Is this a generic ruby issue (or is it unique to wxruby)?


This is a unique problem with wxRuby.  It's mainly a decision of what we
need to continue to keep tracking, without tracking every single object
created.

  What triggers the GC ?  (While testing, I found that the "rewrapping"
>   occurred whether or not I called find_item.  Just repeatedly
> selecting
>   the MyMenuItem, which triggered a menu_event and caused on_menu_event
>   to be called, would result in the MyMenuItem being "rewrapped" as a
>   MenuItem.  As such, over-riding MyMenu#find_item would not be
> expected
>   to solve the problem.)


Garbage Collection, especially in a wxRuby app, happens quite often, as
there are many times that a Ruby object is temporarly created, to
associate
it with a wxWidget's object.  And 9 times out of 10, it's a temporary
reference, which is immediately de-referenced within the code, and when
Garbage Collection runs again, it'll delete this instance.  However, in
the
particular instance of Menu Event (evt_menu), there is no particular
reference to review to say, Oh, this needs to be referenced by previous
instance creation.

  I appreciate your offer to "have a look" and I'd be interested to
>   know whatever you find out.


Unfortunately, in this case, the procedure that Alex has referenced,
would
be the best way, as it would be nearly neigh improbable to track every
single custom class creation, between wxWidgets and wxRuby.  As with
wxWidgets, there's nothing to allow us to track custom classes created
in
Ruby of wxWidgets classes, least we create a new memory structure, that
would inherit from one of our classes.  It may be doable, in the Ruby
level,
at the actual Ruby level itself, but not on the C++ Side.

Cheers,
Ridge M. (Guest)
on 2009-01-05 11:06
Mario S. wrote:
> Hello Ridge,

Hi Mario,

Thanks for joining the discussion.  Hopefully, we can have some
significant mindshare.

> Let me explain this a bit further in detail as to the process the
> Garbage
> Collector, and wxRuby's own classes work about things.
>
> On Sun, Jan 4, 2009 at 5:22 PM, Ridge M. <removed_email_address@domain.invalid>
> wrote:
>
>>   to make it a fully-qualified class, huh?)  If I understood your
>>   message, the GC is making decisions based on the type (MenuItem).
>>   Isn't this just what an object-oriented system should *not* do?
>
>
> What Alex was trying to explain here, is that some classes, such as
> Menu,
> MenuItem, Rect, Point, and various other ones, are considered
> Information
> Classes.  Information classes only hold the information of what is
> relevent
> at the point and time of the execution of the code.  To make it a bit
> more
> clearer to understand, let's take the problem of MenuItem.  Imagine that
> MenuItem is a Ruby Struct class.
>
> Defining it as such:
>
> MenuItem = Struct.new(:title,:id,:parent,:bitmap)
>
> my_new_menu_item =
> MenuItem.new("&Open",Wx::ID_OPEN,my_file_menu,Wx::ICON_FOLDER)
>
> This is a silly mockup of what I'm trying to explain, but this is what
> it
> boils down to.  Once you actually create the control, there's no further
> need to reference this control, till something happens with it, such as
> evt_menu(Wx::ID_OPEN), this is where we re-retrive the actual wxRuby
> control, for any association needed to do special things, such as
> changing
> the title of the Menu item.  There is no other things you can really do
> with
> a MenuItem, aside from either updating it, or deleting it.  Atleast, as
> far
> as wxWidgets is concerned.  Therefore, if you don't store the instance
> of
> the MenuItem in a variable, where the Garbage Collector can see that it
> is
> still being referenced within your code, Ruby thinks that it is no
> longer
> needed, and therefore frees it up for others to use.

Your explanation is clear and I appreciate your effort to make it so.
I'm hoping I can be as clear.

I understand your perspective but respectfully disagree with your
assertion:
   "There is no other things you can really do with a MenuItem, aside
   from either updating it, or deleting it."

One of the hallmarks and most powerful aspects of an object-oriented
language is that new developers are able to extend the capabilities of
existing classes via subclassing (in ways that the class originators
didn't necessarily anticipate).  By constraining classes in arbitrary
ways (IMHO), that power is severely limited.

I do understand (I think) your desire to free up memory that is not
being used.  However, there are already mechanisms in place: scoping
rules and delete.  Please help me understand why these mechanisms
are not sufficient in this case.

If you are providing extra GC as a convenience, then I respectfully
submit that the developer should be permitted to accept or reject
that convenience so that his/her code will behave as expected.  By
forcing extra GC upon the developer for certain classes that are
considered "information classes" by the originators, the code
(originally attached) behaves in a most unexpected way:

On (approximately) the 25th time a class instance is used (in this
case a menu appears), the (subclassed) menu items suddenly lose
their identity and instead assume the identity of their superclass.

Until I have the opportunity to understand your perspective more
fully, I respectfully assert:

Class instantiations should maintain their existence and identity
until they move out of scope or until they are expressly deleted.

>   Some questions:
>>   Is this a generic ruby issue (or is it unique to wxruby)?
>
>
> This is a unique problem with wxRuby.  It's mainly a decision of what we
> need to continue to keep tracking, without tracking every single object
> created.

I appreciate your candidness.  I'm hoping we can find a way for
wxruby to be more ruby-ish.

>   What triggers the GC ?  (While testing, I found that the "rewrapping"
>>   occurred whether or not I called find_item.  Just repeatedly
>> selecting
>>   the MyMenuItem, which triggered a menu_event and caused on_menu_event
>>   to be called, would result in the MyMenuItem being "rewrapped" as a
>>   MenuItem.  As such, over-riding MyMenu#find_item would not be
>> expected
>>   to solve the problem.)
>
> Garbage Collection, especially in a wxRuby app, happens quite often, as
> there are many times that a Ruby object is temporarly created, to
> associate
> it with a wxWidget's object.  And 9 times out of 10, it's a temporary
> reference, which is immediately de-referenced within the code, and when
> Garbage Collection runs again, it'll delete this instance.  However, in
> the
> particular instance of Menu Event (evt_menu), there is no particular
> reference to review to say, Oh, this needs to be referenced by previous
> instance creation.

I understand your point that object creation is often
temporary and that, with regard to Menu events, there
is some difficulty in knowing what is a temporary
instantiation and what is not.

However, again I respectfully submit that it seems
ill-advised to use probability as a solution without
offering an alternative.

> Unfortunately, in this case, the procedure that Alex has referenced,
> would
> be the best way, as it would be nearly neigh improbable to track every
> single custom class creation, between wxWidgets and wxRuby.  As with
> wxWidgets, there's nothing to allow us to track custom classes created
> in
> Ruby of wxWidgets classes, least we create a new memory structure, that
> would inherit from one of our classes.  It may be doable, in the Ruby
> level,
> at the actual Ruby level itself, but not on the C++ Side.

Thank you for your trying to educate me about wxruby and
for offering work-arounds that I can put in my code.

I am not sure at this point why you seem so concerned about
"tracking every custom class creation" when, in both C++ and
ruby, subclassing is such a fundamental part of the language.
There are already mechanisms available to the developer to
handle memory bloat (scoping rules and express deletion of
unneeded instantiations).

I am very happy that you folks are working hard to support
wxruby.  wxruby seems to be a comprehensive, cross-platform
GUI toolkit that could meet my needs.  I respect your
willingness to discuss issues such as the present one.
I also respect your willingness to explain why you have
made the decisions you have made.

I hope you can also understand my surprise to be faced
with such an unexpected problem on the first day of wxruby
development.  I want to believe that there is a solution
that will meet the needs of all developers.

At the risk of sounding bold, would you consider supporting
the following:

"wxruby fully supports subclassing, respecting scoping rules
to handle object persistence.  However, developers should be
advised that they can unwittingly consume memory resources
if they do not properly manage their objects, specifically
if they do not delete objects when they are no longer needed.
We have learned this through bitter experience.

"Since object management can be a daunting and tedious task
for the developer, wxruby provides a super-cool garbage
collection facility that can automatically delete objects
that are no longer referenced, significantly reducing runtime
memory usage.  When creating your application, simply set
supercool_gc to true, as in the following example:

class MyApp < App
    def on_init
        @supercool_gc = true
    end
end

"The following classes are considered 'information classes'
and will be specifically targeted by the GC:
Menu
MenuItem
(any others)

"If you subclass any of the information classes, please
note that you may need to disable supercool_gc for
those classes."

At this point, I'm concerned that I will find other
instances in which the GC will impede my development
progress.  But I hoping this will not be the case.  It
may be that the problem is limited to menu items and
that the GC works beautifully in all other cases.  In
that case, developers would want to take advantage of
the GC and would alternatively perfer to have a specific
@supercool_menuitem_gc flag so that they could manage
their menuitems independently but have the GC manage
the rest of the objects.  Or, perhaps there should be a
flag for each "information class".  I'm hoping we can
sort this out.

As noted, since I'm a wxruby newbie, I welcome further
explanations to help me understand why the GC must behave
as it does.  I would very much like wxruby to succeed.

> Cheers,

Ridge
Alex F. (Guest)
on 2009-01-05 13:35
(Received via mailing list)
Hi Ridge

Ridge M. wrote:
>    Good suggestion (1) and that is actually what I did to move forward.
>

Glad that worked, and also thanks for spotting this and bringing it up.

>    However, I consider it a work-around.  I welcome your explanations
>    because I want to understand.
>
>    Here's my view:
>    As a developer, I don't want just the "native" superclass to work
>    properly with the GC.  I want my sub-classes to work properly, too.
>
>


I understand that, and indeed most classes where you'd want that should
already do that. First up I'll say that having seen your example, I
agree that MenuItem should work correctly - ie that a custom subclass
should be maintained.

As you'll have realised wxRuby is a big library with 200+ classes, and
for each of them we have to select a memory management strategy. It
needs

1) to be safe - ie it won't crash under any various circumstance it's
used
2) to be efficient - it won't leak memory, or unnecessarily slow the
library (for example, GNOME2 can suffer pauses due to GC, which limits
its usefulessness for real-time drawing apps)
3) to work well with Ruby's dynamic object model
4) to work OK with wxWidgets C++ object ownership and memory management

The current implementation does 1, 2 and 4 but not 3 - I simply hadn't
foreseen the use-case demonstrated by your sample.

>    I appreciate your offer to "have a look" and I'd be interested to
>    know whatever you find out.

Over the weekend I made a change to my working tree. The result of this
patch would be that MenuItems, once added to a Menu, would have the
original object preserved. This would resolve your issue.

Historically Menus and MenuItems have been among the most problematic
for GC, so it's moderately high-risk. I'm going to test it further for a
few days, then commit, then we can give it some further working out on
all platforms. Since you're on Windows I'm guessing you don't have the
compiler infrastructure, in which case it'll be available for the next
2.0 release.

best
alex
Alex F. (Guest)
on 2009-01-05 14:04
(Received via mailing list)
Hi Ridge/Mario

Just read through the discussion, and think you're both making sense.
Ridge, the OO philosophy which you describe is that which I've had in
mind developing wxRuby, and I believe we already follow in large part.

This is just an implementation issue of an unforeseen use case for a
particular class, which hopefully we've resolved for the next release.


Ridge M. wrote:
>> What Alex was trying to explain here, is that some classes, such as
>> Menu,
>> MenuItem, Rect, Point, and various other ones, are considered
>> Information
>> Classes.  Information classes only hold the information of what is
>> relevent
>> at the point and time of the execution of the code.  To make it a bit
>> more
>> clearer to understand, let's take the problem of MenuItem.  Imagine that
>> MenuItem is a Ruby Struct class.

Just to add to Mario's comment - indeed Size/Rect/Point are always going
to be 'info' classes and not have information added to them. This is
probably clearer if you think of how they work

window.size = Size.new(100, 50)
# user uses application, including resizing window to 200 x 600
size = window.size
# Size is now [200, 600]

In this case it's a moot point whether it's the 'same' size object, as
its properties (size, width) are inherent to its identity. And in
reality, wxWidgets doesn't think it is, so we couldn't return a same
MySize object in Ruby.

I think the need arises where one could sensibly want to add member data
(ie instance variables) in a subclass. Your example demonstrated this
for MenuItem, and I think it's already covered for other classes.
Indeed, I went through hoops to get this working right for some
difficult cases (eg custom Event objects).

> Class instantiations should maintain their existence and identity
> until they move out of scope or until they are expressly deleted.
>

Yep, so far as the tools we have available (wxWidgets, Ruby's GC API,
SWIG etc) permit.

>>>   Is this a generic ruby issue (or is it unique to wxruby)?

It's an issue which any Ruby C extension library faces: how to make
C/C++'s explicit alloc/free memory management interact smoothly with
Ruby's GC, whilst supporting Ruby's highly dynamic OO scheme. It's a
particularly acute problem for long-running (eg GUI or server)
libraries.

As an aside, it's also one reason that Ruby itself is a difficult
language to implement efficiently - see the initial slowness of most
early implementations.

>>   What triggers the GC ?

Ruby's GC runs whenever the size of allocated objects has reached a
certain maximum heap size, and ruby is asked to allocate a new object.
So from a programmer's point of view, it's not determined, although
GC.start can be used to invoke GC explicitly.

> At this point, I'm concerned that I will find other
> instances in which the GC will impede my development
> progress.  But I hoping this will not be the case.  It
> may be that the problem is limited to menu items and
> that the GC works beautifully in all other cases.

I believe this is the case. As Mario's mentioned, there are other
classes which work like MenuItem (currently) works but I don't think
they will impede your progress. Although technically a 'beta' release,
1.9.9 has a few years history behind it, and is one of the most
widely-used GUI libraries.

alex
Ridge M. (Guest)
on 2009-01-05 22:39
Alex F. wrote:
> Hi Ridge/Mario
>
> Just read through the discussion, and think you're both making sense.
> Ridge, the OO philosophy which you describe is that which I've had in
> mind developing wxRuby, and I believe we already follow in large part.
>
> This is just an implementation issue of an unforeseen use case for a
> particular class, which hopefully we've resolved for the next release.

Great news.  Thank you both for your prompt responses.

>
> alex
This topic is locked and can not be replied to.