Безопасны ли Iteratee для управления ресурсами?

Предположим, я читал из InputStream.

Как я обычно это делаю:

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

Независимо от того, выдает ли doStuff исключение, мы закроем файл InputStream.


Как бы я сделал это с итерациями:

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

Будет ли закрыт InputStream (даже если doStuff выдаст исключение)?

Небольшой тест:

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)

Мы видим только:

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

Поток никогда не закрывался. Замените 1 / 0 на 1 / 1, и вы увидите, что поток закрывается.

Конечно, я мог бы сохранить ссылку на исходный поток и закрыть его в случае сбоя, но, насколько мне известно, идея использования итераций заключается в создании компонуемой итерации без необходимости этого делать.


  1. Это ожидаемое поведение?

  2. Есть ли способ использовать итерации, чтобы ресурсы всегда располагались правильно?


person Paul Draper    schedule 26.07.2014    source источник
comment
Обратите внимание, что это поведение изменилось в версии 2.2 (хотя это все еще немного странно). Я никогда не думал, что итерируемая реализация Play имеет очень хорошую историю управления ресурсами.   -  person Travis Brown    schedule 27.07.2014


Ответы (1)


Итерации были специально разработаны для безопасного управления ресурсами. См. первое предложение Iteratee IO: безопасная, практичная, декларативная обработка ввода :

Iteratee IO — это стиль инкрементной обработки ввода с точным контролем ресурсов.

Идея состоит в том, что когда к вашему ресурсу обращаются только через итерации, код, владеющий ресурсом, может точно сказать, когда итерация закончила работу с ресурсом, и немедленно закрыть ее. С другой стороны, когда итерация управляется вручную (как в случае с традиционным InputStream), пользователь ресурса несет ответственность за его закрытие. Это может привести к утечкам.

Говоря это, в Play 2.1 была ошибка, из-за которой fromStream не справлялся с закрытием своего основного InputStream! Эта ошибка была исправлена ​​в Play 2.2.

Вы можете увидеть fromStream code, чтобы увидеть, как Enumerator было исправлено с помощью onDoneEnumerating для закрытия ресурса после завершения итерации.

person Rich Dougherty    schedule 29.07.2014
comment
О, хорошо, я рад, что это была ошибка, а не преднамеренное или принципиальное ограничение :) - person Paul Draper; 29.07.2014