Robert Klemme
6/20/2005 7:37:00 AM
Eric Mahurin wrote:
> --- Robert Klemme <bob.news@gmx.net> wrote:
>> "Robert Klemme" <bob.news@gmx.net> schrieb im Newsbeitrag
>> def self.list
>> FOOS.each do |oid, x|
>> begin
>> p ObjectSpace._id2ref(oid)
>> rescue RangeError => e
>> puts "#{oid} collected but not finalized:
>> x=#{x.inspect}"
>> end
>> end
>> end
>
> I assume you meant FOOS above. I fixed it.
Correct.
> Just catching a RangeError is not all you need. You better
> make sure the object is finalized and the finalizer removes its
> oid from FOOS before the space is reclaimed. Otherwise oid
> could refer to a completely new object and you wouldn't detect
> it.
Probably. My assumption was, that oids are not reused. But I may be
wrong here.
> On top of that, these finalizers can't be called while this
> FOOS.each loop is going on. Otherwise you'd get an error about
> the hash being modified while your iterating over it. It is
> like the GC and finalizers are in another thread, but
> unfortunately you can't control it like a thread (i.e.
> Thread.critical=). This is my primary dilemma.
Hm... Did you try Mutex or Monitor?
> These issues may be very difficult to detect problems with and
> you may need 1000's of tests to excite the GC differently to
> detect the problem. You must be prepared for this using
> _id2ref.
>
>>> This approach works ok - you'll have to imagine that x contains
>>> information needed for proper cleanup of a Foo instance, for
>>> example, an open IO instance (although I'm sure that will do proper
>>> cleanup on finalization):
>>>
>>> class Foo
>>> FOOS = {}
>>>
>>> def initialize(x)
>>> self.x=x
>>>
>>> ObjectSpace.define_finalizer(self) do |oid|
>>> puts "Cleanup of #{oid} with #{FOOS[oid]}"
>>> end
>>> end
>
> Won't work. This is the exact mistake I made when first trying
> to use a finalizer. In the above, you gave define_finalizer a
> Proc that has a Binding with direct access to self (a Foo).
> This creates an unintended reference to the object in
> ObjectSpace/GC. It will never be GCed because of this.
Darn, yes you're right!
Two alternatives would be
class Foo
class<<self
alias :_new :new
def new(*a,&b)
obj = _new(*a,&b)
ObjectSpace.define_finalizer(obj) do |oid|
puts "Cleanup of #{oid} with #{FOOS[oid]}"
end
obj
end
end
end
class Foo
def initialize(x)
self.x=x
self.class.instance_eval do
ObjectSpace.define_finalizer(obj) do |oid|
puts "Cleanup of #{oid} with #{FOOS[oid]}"
end
end
end
end
> It
> took me a while to figure this out. The Proc/Method needs to
> be defined in a context that doesn't have access to the object
> you are trying to put a finalizer on. For this reason, I don't
> think define_finalizer should even allow the block form. Also,
> a Proc#unbind would be nice to have in this situation.
>
> Also, I assume you'd want to delete the oid entry from FOOS in
> this finalizer, right?
Yes. Sorry for the errors and omissions - it was late already...
>
>>> def x=(y)
>>> @x = y
>>> FOOS[object_id] = y
>>> end
>>>
>>> def x() @x end
>>> end
What is the real world problem you are trying to solve?
Kind regards
robert