Resource Acquisition & Cleanup

Hello Rubyists,

I am looking for a style or idiom that I can apply to a resource
management problem. I have a number of resources (objects) that I need
to release if any exception is thrown. While ensure-blocks or block
parameters work well for few resources, they get cumbersome when you
have many resources, or when the number of resources is not known.

Let’s say I have something similar to this:

resources = []
n.times { |i|
resources.push( Resource.new(i) }
}

loop {

do some processing on the resources

}

n.times { |i|
resources[i].release
}

If an exception is thrown in the main processing loop, i still need to
release the resources. If an exception is thrown during construction,
i need to release the resources already succesfully created. If an
exception is thrown while releasing a resource during exception
handling, i still need to release the remaining resources.

Is there a way to do this, especially when ‘n’ is unknown?

Regards,
Lars

[email protected] wrote:

resources = []
}
Lars

How about this? It should catch and print exceptions that happen during
release, but continue looping through the rest of the array.

resources.each do |resource|
begin
resource.release
rescue => err
puts err
end
end

If you want to also do this upon exceptions in the main processing loop,
I would make it into a method and call it like this:

def release(resources)
resources.each do |resource|
begin
resource.release
rescue => err
p err
end
end
end

n.times { |i|
resources.push( Resource.new(i) }
}

begin
loop {
# do some processing on the resources
}
rescue => err
p err

release resources upon error:

release(resources)
ensure

release resources if there is no error:

release(resources)
end

Hope this helps,
Dan

I used to use an idiom like this:

class Rollback
attr_reader :procs

def initialize
@procs = []
end

def undo(&b)
@procs << b
end

def self.rollback
rb = self.new
begin
yield rb
rescue Exception
rb.procs.reverse.each do |p|
p.call rescue Exception
end
raise
end
end
end

Rollback.rollback do |rb|
rb.undo { puts “1” }
rb.undo { puts “2” }
rb.undo { puts “3” }
raise “FOO”
end

=> 3

=> 2

=> 1

=> RuntimeError: FOO

Looking back I think I could/should have picked a better name, but the
idea is that after you allocate each resource, call rb.undo to set up a
rollback procedure if an exception is raised later on.

I think I posted this somewhere to ruby-talk a few years ago, but I
couldn’t find the post.

Paul

2007/9/4, [email protected] [email protected]:

resources = []
}

If an exception is thrown in the main processing loop, i still need to
release the resources. If an exception is thrown during construction,
i need to release the resources already succesfully created. If an
exception is thrown while releasing a resource during exception
handling, i still need to release the remaining resources.

Is there a way to do this, especially when ‘n’ is unknown?

This is pretty simply and should satisfy your requirements:

def your_method(n)
res = []
begin
n.times {|i| res << Resource.new(i)}

loop do
  # main work
end

ensure
res.each {|r| r.release rescue nil}
end
end

Kind regards

robert