typelevel / fs2

Compositional, streaming I/O library for Scala
https://fs2.io
Other
2.37k stars 601 forks source link

Resource finalization out of order when using with concurrent stream. #2936

Open Swoorup opened 2 years ago

Swoorup commented 2 years ago

It appears that resource finalizer is not being called correctly in order. In this simple example, almost all runs I did, the finalizer gets called before the last .use statement

Version: 3.2.8

import cats.effect.syntax.all.*
import cats.syntax.all.*
import cats.effect.*
import fs2.Stream

object Main extends IOApp.Simple:
  val run = Stream
    .resource(
      Resource.make(IO.println("hello").as(100))(n =>
        IO.println(s"goodbye: $n")
      )
    )
    .concurrently(Stream.emits[IO, Int](1 to 10).evalMap(IO.println))
    .compile
    .resource
    .lastOrError
    .use(x => IO.println(s"From use: $x"))

Output:

1
hello
2
goodbye: 100
3
4
From use: 100
5

https://scastie.scala-lang.org/NmxxGQjXREyyA3RflV9nqA

mpilquist commented 2 years ago

Initial guess is that this is example of badly interacting features -- concurrently and .compile.resource. That is, concurrently introduces its own root scope which results in the scope from Stream.resource no longer being a root scope, and hence not having its lifetime extended to the lifetime of the returned resource.

diesalbla commented 2 years ago

I have checked the test in scastie. I have also tried changing versions, and it already happens with versions 2.5.1 and 2.5.6 of fs2 and cats-effect.

nikiforo commented 2 years ago

I'll leave this comment here, it may be relevant(I see as == map in both examples). https://github.com/typelevel/fs2/issues/2785#issuecomment-1007825447