Forum: Ruby scope and RAII

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.
6077bce034090408c12e561e35a2e184?d=identicon&s=25 unknown (Guest)
on 2007-03-24 15:45
(Received via mailing list)
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
Ac0085dae0703db56ad7f8cb9e1798ba?d=identicon&s=25 Phillip Gawlowski (Guest)
on 2007-03-24 16:27
(Received via mailing list)
jeff_alexander_44@yahoo.com 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
6077bce034090408c12e561e35a2e184?d=identicon&s=25 unknown (Guest)
on 2007-03-24 16:45
(Received via mailing list)
On Mar 24, 11:27 am, Phillip Gawlowski <cmdjackr...@googlemail.com>
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_...

I'll preemptively ask that we not discuss the necessity of RAII; as
a game programmer it is a necessity for me.
Ad7805c9fcc1f13efc6ed11251a6c4d2?d=identicon&s=25 Alex Young (regularfry)
on 2007-03-24 17:04
(Received via mailing list)
jeff_alexander_44@yahoo.com 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,
Ad7805c9fcc1f13efc6ed11251a6c4d2?d=identicon&s=25 Alex Young (regularfry)
on 2007-03-24 17:08
(Received via mailing list)
Alex Young 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,
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2007-03-24 17:16
(Received via mailing list)
On Sat, 24 Mar 2007 jeff_alexander_44@yahoo.com 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 ;-)

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!
;-)


> }
> # 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
6077bce034090408c12e561e35a2e184?d=identicon&s=25 unknown (Guest)
on 2007-03-24 18:06
(Received via mailing list)
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.)
8f6f95c4bd64d5f10dfddfdcd03c19d6?d=identicon&s=25 Rick Denatale (rdenatale)
on 2007-03-26 23:58
(Received via mailing list)
On 3/24/07, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> 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/
This topic is locked and can not be replied to.