Scope and RAII


#1

def scope(*args)
yield(*args)
end

scope(Array.new) { |array|
array.push 1
array.push 7
array.push 11
puts array.join(",")
}

here the variable ‘array’ is gone – is it deallocated?

I am interested in using ruby for some resource-intensive tasks. In
the above example, imagine Array replaced with some resource-tied
class written in C, for example a set of hardware textures.

Does ruby make a special effort to release a variable when it goes out
of scope? The canonical example is

File.open(“foo.txt”) { |f|
print f.read
}

internal file handle released here

Is this behavior a general rule, or just a special case for
File.open()? And is it released immediately or upon the
next GC pass?

Thanks,
Jeff


#2

removed_email_address@domain.invalid wrote:

next GC pass?
The file handle should be released ASAP if you use:

file = File.open(“foo”, “r”) #opens foo in read-only mode
file.readlines { |f| print f } #prints each line
file.close #closes the file handle.

In that case, you can use the mixins from IO via File, including, but
not limited, to IO#close.

IMHO, it is a good idea to tell Ruby the mode it should open a file,
too.[0]

Disclaimer: I’m still a Ruby nuby, and don’t know anything about the
inner workings of Ruby itself.

Bibliography:
[0] http://www.zenspider.com/Languages/Ruby/QuickRef.html#15


Phillip “CynicalRyan” Gawlowski

Rule of Open-Source Programming #20:

Open Code != Good Code


#3

On Mar 24, 11:27 am, Phillip G. removed_email_address@domain.invalid
wrote:

file = File.open(“foo”, “r”) #opens foo in read-only mode
file.readlines { |f| print f } #prints each line
file.close #closes the file handle.

I should have mentioned why I put “RAII” in the subject. The coding
style you show above is exactly what I’m trying to avoid.
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

I’ll preemptively ask that we not discuss the necessity of RAII; as
a game programmer it is a necessity for me.


#4

removed_email_address@domain.invalid wrote:

here the variable ‘array’ is gone – is it deallocated?

No. In general, it won’t be deallocated until the next GC pass.

internal file handle released here

Is this behavior a general rule, or just a special case for
File.open()?
The release of a file handle is a different case to freeing a variable.
You might be thinking of the release of a file handle as being handled
by a destructor - that’s not what’s happening. Ruby doesn’t have
destructors. It does have finalizers, but they aren’t the same (mainly
because they’re not guaranteed to be called, although I’ll have to defer
to someone more knowledgeable than myself as to the details). In
essence, as I understand it File.open with a block does something
equivalent to this (very)pseudocode:

class File
def open(filename, mode)
file_handle = fopen(filename, mode)
yield(File.create_from_handle(file_handle))
fclose(file_handle)
end
end

And is it released immediately or upon the
next GC pass?
It’s released immediately, but that’s as a consequence of the code in
File.open rather than because it’s gone out of the block’s scope.

Hope this helps,


#5

Alex Y. wrote:

}

here the variable ‘array’ is gone – is it deallocated?

No. In general, it won’t be deallocated until the next GC pass.
I should have pointed out here (in case you hadn’t spotted it already)
that the variable is utterly distinct from the object to which it
refers. The variable can go out of scope and the object may not be
collected at all - it entirely depends on what else has a reference to
that object.

Hope that’s clear,


#6

On Sat, 24 Mar 2007 removed_email_address@domain.invalid wrote:

}

here the variable ‘array’ is gone – is it deallocated?

array is merely a label - a handle. the variable is references, the
one
created by

Array.new

still exists. you can prove this to yourself

harp:~ > cat a.rb
def scope(*args)
yield(*args)
end

scope(Array.new) { |array|
array.push 1
array.push 7
array.push 11
puts array.join(",")
}

ObjectSpace.each_object(Array){|obj| p obj}

harp:~ > ruby a.rb
1,7,11
[[1, 7, 11]]
[1, 7, 11]
[]
[]
[]
[]
["/home/ahoward//lib/ruby/site_ruby/1.8",
“/home/ahoward//lib/ruby/site_ruby/1.8/i686-linux”,
“/home/ahoward//lib/ruby/site_ruby”, “/home/ahoward//lib/ruby/1.8”,
“/home/ahoward//lib/ruby/1.8/i686-linux”, “.”]
[:utime, :stime, :cutime, :cstime]

in fact, your scoping method has managed to create three of them :wink:

one here

scope(Array.new)
^^^^^^^^^

one here

def scope(*args)
^^^^

and one here

 yield(*args)
        ^^^^

so this probably isn’t the best way to conserve on resource utilization!
:wink:

}

internal file handle released here

Is this behavior a general rule, or just a special case for
File.open()? And is it released immediately or upon the
next GC pass?

there are two issues here

  • the fd, which is released at the end of the block

  • any ruby variables, which will only be freed by the GC

in the case of the first the only reason it’s freed is because it’s
coded to do
exactly that, you can imagine it like

class File
def open path, mode, &b
fd = open_file path, mode
b.call fd
ensure
fd.close
end
end

this is the general pattern you use to force resource deallocation.
note that
it’s your responsiblity and that ruby does nothing special to make sure
it
happens.

-a


#7

On 3/24/07, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

there are two issues here

  • the fd, which is released at the end of the block

Released in the sense that File.open closes the file handle after
execution of the block when a block is given.

  • any ruby variables, which will only be freed by the GC

I’ve got to quibble with this. It’s objects which are freed by the
GC, not variables. The distinction between objects and
variables(references) is subtle, but it also trips up a lot of newbies
which is why I’m going into this.

Ruby variables are simply ‘slots’ which reference objects.

Method or block temporaries (pretty much the same thing in 1.8) and
method parameters exist on the call stack and go away when the call
stack is popped.

Instance variables are value slots in a hash table pointed to by an
object. These variables live as long as the object does, or until
they are removed with remove_instance_variable.

Class variables and Constants are similarly associated with a Class
or Module object, and Globals are kept in a global registry.

So other than the variables which exist on the call stack, variables
are never automatically freed.

But an object becomes a candidate to be freeded (actually to have its
space reclaimed) by the GC when there are no variables reachable by
following chains of variable references starting with either variables
on the stacks of any threads, or constants (actually constants in the
root namespace)

Also this only makes the object a candidate for GC, there is no
guarantee of how soon, if ever, after all references to an object are
gone that an object will be reclaimed by the GC. Which is why it’s a
bad idea to rely on finalization to clean up external resources
associated with an object.

Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/


#8

Thanks to everyone for the responses. It turns out I was mislead by
this example I had in mind:

handle = nil
File.open(“test.txt”, “w”) { |f|
f.puts “line 1”
handle = f
}
handle.puts “line 2”

In my mind, I thought that assigning “handle” to “f” would prevent the
file from being closed. But now that I actually run the code, I see
the exception on the second puts. (In retrospect, I must have been
thinking in terms of perl.)