Resource Acquisition & Cleanup

L

larsch

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.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
 
D

Dan Zwell

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.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


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
 
R

Robert Klemme

2007/9/4 said:
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.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?


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
 
P

Paul Brannan

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
 

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

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top