Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A borrowed resource can postpone its termination #291

Open
iRevive opened this issue Nov 9, 2021 · 0 comments · May be fixed by #293
Open

A borrowed resource can postpone its termination #291

iRevive opened this issue Nov 9, 2021 · 0 comments · May be fixed by #293

Comments

@iRevive
Copy link
Contributor

iRevive commented Nov 9, 2021

keypool version: 0.4.7

When a resource is borrowed from the pool, it's not a part of the PoolMap anymore. Hence it will not be destroyed during the finalization of the pool.
The resource will be open until it's not completed itself. Thus IO.never as a part of the operation with the resource can hang the whole system.

Example:

sealed trait Key
case object Key extends Key

class Connection(val isOpen: Ref[IO, Boolean])

val pool = KeyPool
  .Builder(
    (_: Key) => IO.ref(true).map(new Connection(_)),
    (conn: Connection) => conn.isOpen.set(false)
  )
  .doOnCreate(conn => IO.println(s"created $conn"))
  .doOnDestroy(conn => IO.println(s"destroyed $conn"))
  .build
  .onFinalize(IO.println("pool was closed"))
  
def example(outerAwait: Deferred[IO, Unit]): IO[FiberIO[(Boolean, Boolean)]] =
  pool.use { p =>
    def job(innerAwait: Deferred[IO, Unit]): IO[(Boolean, Boolean)] =
      p.take(Key).use { managed =>
        val connection = managed.value
        
        for {
          status1 <- connection.isOpen.get
          _       <- IO.println("awaiting inner")
          _       <- innerAwait.complete(())
          _       <- IO.println("awaiting outer")
          _       <- outerAwait.get          // wait for the signal from the outside
          _       <- IO.println("after awaits")
          status2 <- connection.isOpen.get   // isOpen should be 'false' there
          // _ <- IO.never[Unit] // once uncommented the program will hang
        } yield (status1, status2)
      }
      
    for {
      await <- IO.deferred[Unit]
      fiber <- job(await).start
      _     <- await.get // make sure first part happens inside of the open pool
    } yield fiber
  }
  
for {
  await   <- IO.deferred[Unit]
  fiber   <- example(await)
  _       <- await.complete(()) // pool is closed and we continue execution of the job
  outcome <- fiber.join
  (status1, status2) <- outcome.embedNever
  _                  <- IO.println(s"(pool open): is connection open $status1")
  _                  <- IO.println(s"(pool closed): is connection open $status2")
} yield ()

Output:

created Connection@76426537
awaiting inner
awaiting outer
pool was closed
after awaits
destroyed Connection@76426537
(pool open): is connection open true
(pool closed): is connection open true

Expected output:

created Connection@76426537
awaiting inner
awaiting outer
destroyed Connection@76426537
pool was closed
after awaits
(pool open): is connection open true
(pool closed): is connection open false
@iRevive iRevive linked a pull request Nov 10, 2021 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant