8000 Incorrect bracketState implementation · Issue #841 · tofu-tf/tofu · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Incorrect bracketState implementation #841
Open
@ioaoue

Description

@ioaoue

The current implementation of bracketState does not guarantee that:

  • use will be cancelled when fiber is cancelled
  • commit will be executed if fa succeeds

The following code demonstrates the problem:

import cats.effect._
import java.time.Instant
import scala.concurrent.duration._
import tofu.syntax.bracket._

object Demo extends IOApp {
  def log(text: String): IO[Unit] = IO(println(s"${Instant.now}: $text"))

  val slow: IO[Unit] = for {
    _ <- log("start")
    _ <- Timer[IO].sleep(3.seconds)
    _ <- log("finish")
  } yield ()
  
  override def run(args: List[String]): IO[ExitCode] = 
    slow.bracketState { _ => 
      log("use").as((), ())
    } { _ =>
      log("commit")
    }.timeoutTo(1.second, log("timeout"))
      .as(ExitCode.Success)
}

The output looks like this:

2021-12-09T17:18:30.869187693Z: start
2021-12-09T17:18:33.897543168Z: finish
2021-12-09T17:18:33.899531757Z: use
2021-12-09T17:18:33.900238459Z: timeout

The "finish" message is still printed although the fiber is cancelled by timeoutTo. This is because the first FG.bracket call in bracketState code makes its init block uncancellable. The "commit" message is not printed because bracketCase calls it in the use block of the first FG.bracket, but it is not guaranteed that use will be executed after successful init.

This behavior breaks bracketModify and MVar-based tofu.memo.Cached.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0