3

Suppose I were reading from an InputStream.

How I would normally do it:

val inputStream = ...
try {
    doStuff(inputStream)
} finally {
    inputStream.close()
}

Whether or not doStuff throws an exception, we will close the InputStream.


How I would do it with iteratees:

val inputStream ...
Enumerator.fromStream(inputStream)(Iteratee.foreach(doStuff))

Will the InputStream be closed (even if doStuff throws an exception)?

A little test:

val inputStream = new InputStream() { // returns 10, 9, ... 0, -1
    private var i = 10
    def read() = {
       i = math.max(0, i) - 1
       i
    }
    override def close() = println("closed") // looking for this
}
Enumerator.fromStream(inputStream)(Iteratee.foreach(a => 1 / 0)).onComplete(println)

We only see:

Failure(java.lang.ArithmeticException: / by zero)

The stream was never closed. Replace 1 / 0 with 1 / 1 and you'll see that the stream closes.

Of course, I could maintain a reference to the original stream and close it in case of failure, but AFAIK the idea of using iteratees is creating composable iteration without having to do that.


  1. Is this expected behavior?

  2. Is there a way to use iteratees so the resources are always disposed correctly?

Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • Note that this behavior changed in 2.2 (although it's still kind of weird). I've never really thought Play's iteratee implementation had a very good resource-management story. – Travis Brown Jul 26 '14 at 23:52

1 Answers1

2

Iteratees were specially designed for safe resource management. See the first sentence of the Iteratee IO: safe, practical, declarative input processing:

Iteratee IO is a style of incremental input processing with precise resource control.

The idea is that when your resource is only accessed through iteratees then the resource-owning code can tell exactly when the iteratee is finished with the resource and close it immediately. On the other hand, when iteration is managed manually (as with a traditional InputStream) the user of the resource is responsible for closing it. This can lead to leaks.

Saying that, there was a bug in Play 2.1 where fromStream didn't manage the closing of its underlying InputStream! This bug was fixed in Play 2.2.

You can see the fromStream code to see how the Enumerator was fixed by using onDoneEnumerating to close the resource when the iteratee is finished.

Rich Dougherty
  • 3,231
  • 21
  • 24