finalizer not getting called

J

Joshua Chia

The finalizer is not getting called in this code -- it just prints
'exiting'. What's wrong? I'm running it on v1.8.6 on Windows Vista.

class A
def initialize
ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
#{id}"})
end
end
a = A.new
a = nil
GC.start
sleep(5)
puts 'exiting'
 
W

Wolfgang Nádasi-donner

Joshua said:
The finalizer is not getting called in this code -- it just prints
'exiting'. What's wrong? I'm running it on v1.8.6 on Windows Vista.

class A
def initialize
ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
#{id}"})
end
end
a = A.new
a = nil
GC.start
sleep(5)
puts 'exiting'

I used this example with Windows2000 and the same result. It works when
I put the finalizer definition outside of the class definition.
class A
def initialize
puts "initializing #{self.object_id}"
# ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
#{id}"})
end
end
a = A.new
puts "created #{a.object_id}"
ObjectSpace.define_finalizer(a, proc{|id| puts "finalizing #{id}"})
a = nil
GC.start
puts 'exiting'initializing 24036900
created 24036900
exiting
finalizing 24036900
When I change your original code a little bit, there will be an
interesting result...
class A
def initialize
puts "initializing #{self.object_id}"
ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
#{id}"})
end
end
a = A.new
puts "created #{a.object_id}"
#ObjectSpace.define_finalizer(a, proc{|id| puts "finalizing #{id}"})
a = nil
GC.start
ObjectSpace.each_object(A){|o|puts "still there: #{o.object_id}"}
puts 'exiting'initializing 24036690
created 24036690
still there: 24036690
exiting
...which means, that the object was not destroyed by the GC. I don't see
any additional actual reference to the object and don't understand this.

Wolfgang Nádasi-Donner
 
E

evanwebb

..which means, that the object was not destroyed by the GC. I don't see
any additional actual reference to the object and don't understand this.

There is actually a simple reason. Remember that you're passing a
block, ie. a closure, to define_finalizer. That closure holds on to
the stuff like self, which in this case is the object to be finalized.
Thus the finalizer code is actually keeping the object alive.

Use define_finalizer outside the object associates the block with a
context that does not contain the object to be finalized (once the
local is set to nil), so it's actually garbage, and the finalizer
runs.

This is the most common misstep when using finalizers, and they should
be avoided if at all possible, because remember, there is no guarantee
when a finalizer will run, or if it will even run at all.

- Evan Phoenix // evan () fallingsnow [] net
 
W

Wolfgang Nádasi-donner

There is actually a simple reason. Remember that you're passing a
block, ie. a closure, to define_finalizer. That closure holds on to
the stuff like self, which in this case is the object to be finalized.
Thus the finalizer code is actually keeping the object alive.

Oh yes, this has to be overlook easily. Usually I don't care about the
time objects will be destroyed.

...there is no guarantee ... if it will even run at all.

I understand from the description, that a finalizer will run for an
object directly after GC destroyes it. It will not be called if the
program ends without calling GC ever, but if GC destroyes it, the call
will be guaranteed. Is this correct?

Wolfgang Nádasi-Donner
 
J

Joshua Chia

...
There is actually a simple reason. Remember that you're passing a
block, ie. a closure, to define_finalizer. That closure holds on to
the stuff like self, which in this case is the object to be finalized.
Thus the finalizer code is actually keeping the object alive.

Use define_finalizer outside the object associates the block with a
context that does not contain the object to be finalized (once the
local is set to nil), so it's actually garbage, and the finalizer
runs.

I'm not familiar with all the nuances for blocks, but I only see that
the block makes no reference to self. Why does it need to hold on to a
reference to self?

With traditional C code, when you use a library, the client app often
needs to manually call init and teardown. With C++ and Java, you can
let the library do the init and teardown with constructors and
destructors, so you don't need to depend on the app to call init and
teardown at the right time. This is good from a software engineering
perspective since it reduces the number of things entangling the client
and library.

I'm primarily concerned about the teardown happening at all and
secondarily about it happening as soon as possible. How do I guarantee
the eventual execution of some teardown code without depending on the
client (in this case, whoever initialized the instance)? Do I just move
the call to define_finalizer into another method and call that method
from initialize?
 
A

Alex Young

Joshua said:
I'm not familiar with all the nuances for blocks, but I only see that
the block makes no reference to self. Why does it need to hold on to a
reference to self?

With traditional C code, when you use a library, the client app often
needs to manually call init and teardown. With C++ and Java, you can
let the library do the init and teardown with constructors and
destructors, so you don't need to depend on the app to call init and
teardown at the right time. This is good from a software engineering
perspective since it reduces the number of things entangling the client
and library.

I'm primarily concerned about the teardown happening at all and
secondarily about it happening as soon as possible. How do I guarantee
the eventual execution of some teardown code without depending on the
client (in this case, whoever initialized the instance)? Do I just move
the call to define_finalizer into another method and call that method
from initialize?
The Ruby way is not to rely on a finalizer, but to define your setup and
teardown code around a yield thus:

class Foo
def do_work
setup_bits
yield
teardown_bits
end
end

Then you use it like this:

Foo.new.do_work do
whatever_you_want
end

That way the execution sequence is
- setup_bits
- whatever_you_want
- teardown_bits

This is far simpler than relying on finalizer behaviour, which I find
rather impenetrable.
 
W

Wolfgang Nádasi-donner

Joshua said:
I'm not familiar with all the nuances for blocks, but I only see that
the block makes no reference to self. Why does it need to hold on to a
reference to self?

Blocks act as closures, which means, that variables not defined as block
parameter but used inside the block, will be taken from the environment
in which the block was defined. In this case the value of "self" - which
refers the new defined object - must be saved for this purpose.

If you define the block outside of initialize, it will work.
class A
def initialize(b)
puts "initializing #{self.object_id}"
ObjectSpace.define_finalizer(self, b)
end
end
a = A.new(proc{|id| puts "finalizing #{id}"})
puts "created #{a.object_id}"
a = nil
GC.start
ObjectSpace.each_object(A){|o|puts "still there: #{o.object_id}"}
puts 'exiting'initializing 24036640
created 24036640
finalizing 24036640
exiting
 
F

Florian Frank

Joshua said:
The finalizer is not getting called in this code -- it just prints
'exiting'. What's wrong? I'm running it on v1.8.6 on Windows Vista.

class A
def initialize
ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
#{id}"})
end
end
a = A.new
a = nil
GC.start
sleep(5)
puts 'exiting'

If the answer is finalizer in Ruby, then it's very likely, that you're
working on the wrong problem. They are quite difficult to get right
(=not referencing the value to be finalized anywhere). You could use a
class variable for example:

class A
@@foo = proc{|id| puts "finalizing #{id}"}

def initialize
ObjectSpace.define_finalizer(self, @@foo)
end
end

Another possibility is to use a binding, that doesn't
 
F

Florian Frank

Florian said:
Another possibility is to use a binding, that doesn't

Ahmmm. Let's continue here: ...contain the value, perhaps
TOPLEVEL_BINDING and evaluate a string in this context:

class A
def initialize
ObjectSpace.define_finalizer(self,
eval('proc{|id| puts "finalizing #{id}"}', TOPLEVEL_BINDING))
end
end

def foo
a = A.new
end
foo # don't taint the toplevel
GC.start
sleep(5)
puts 'exiting'

Like I said, it's not pretty to use finalizers in Ruby and you perhaps
shouldn't. Using the yield pattern mentioned by Alex Young is perhaps a
better solution to your problem.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top