Enumerators == Resource Leak Risk?

K

keithrbennett

I've recently been writing custom Enumerable classes, and thought it would be a good idea to support Enumerators as well. Making an Enumerable Enumerator-enabled seems to be as simple as adding the following line to the beginning of each():

return to_enum unless block_given?

However, I realized that I might be introducing a risk of resource leaks bydoing so. For example, if my enumerable opens a file to read from, and I read a few but not all values from an enumerator it gives me, then the Enumerable's each() method that feeds Enumerator's next method never completes, and the file close there is never executed.

Should I *not* support enumerators if this is the case?

My current workaround is to define a new 'close' method on the Enumerator returned by super.to_enum (see to_enum() call at https://github.com/keithrbennett/trick_bag/blob/master/lib/trick_bag/enumerables/file_line_reader.rb for an example). I then call the 'close' method when I'm done calling the enumerator's 'next' method.

In that trick_bag gem I have a FilteredEnumerable that can decorate (specifically, filter) enumerators (at https://github.com/keithrbennett/tr.../trick_bag/enumerables/filtered_enumerable.rb). I'm thinkingof adding a close to that enumerator too that calls the wrapped enumerator's close method if it exists:

def close
wrapped_enumerator.close if wrapped_enumerator.respond_to?:)close)
end

It would be nice if Ruby's Enumerator class defined a no-op close method sothis wouldn't be necessary.

A problem with my approach is that I've overridden to_enum to take 1 param instead of 0 params. This could be a problem if someone assumes that can call to_enum on an instance of my enumerable.

Any insight as to the best way to handle this?

Thanks.
 
R

Robert Klemme

I've recently been writing custom Enumerable classes, and thought it
would be a good idea to support Enumerators as well. Making an
Enumerable Enumerator-enabled seems to be as simple as adding the
following line to the beginning of each():

return to_enum unless block_given?

However, I realized that I might be introducing a risk of resource
leaks by doing so. For example, if my enumerable opens a file to read
from, and I read a few but not all values from an enumerator it gives
me, then the Enumerable's each() method that feeds Enumerator's next
method never completes, and the file close there is never executed.

Should I *not* support enumerators if this is the case?
Any insight as to the best way to handle this?

No idea whether it's the best approach but you could remove / invalidate
all methods which do not use each semantic on the returned Enumerator,
e.g. :feed, :inspect, :next, :next_values, :peek, :peek_values, :rewind

Personally I believe the external iteration functionality should be
separated from the "regular" internal iteration functionality. That way
a clean separation would be achieved. For me the biggest value in
to_enum lies in the fact that I can turn any method into an iteration,
e.g. by doing things like 5.times.map {|x| x * 2}. I cannot remember
having felt the need for external iteration and if so I would not have
minded to make it explicit.

Kind regards

robert
 

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
474,056
Messages
2,570,439
Members
47,101
Latest member
DoloresHol

Latest Threads

Top