package com.evolutiongaming.kafka.journal.util import cats.effect._ import cats.effect.concurrent.Ref import cats.effect.implicits._ import cats.implicits._ import cats.Applicative trait ResourceRegistry[F[_]] { def allocate[A](resource: Resource[F, A]): F[A] } object ResourceRegistry { def of[F[_] : Concurrent]: Resource[F, ResourceRegistry[F]] = { implicit val monoidUnit = Applicative.monoid[F, Unit] val result = for { releases <- Ref.of[F, List[F[Unit]]](List.empty[F[Unit]]) } yield { val registry = apply[F](releases) val release = for { releases <- releases.get ignore = (_: Throwable) => () _ <- releases.foldMap { _.handleError(ignore) } } yield {} (registry, release) } Resource(result) } def apply[F[_] : Sync](releases: Ref[F, List[F[Unit]]]): ResourceRegistry[F] = { new ResourceRegistry[F] { def allocate[B](resource: Resource[F, B]) = { resource.allocated.bracketCase { case (b, release) => for { _ <- releases.update(release :: _) } yield b } { case ((_, release), exitCase) => exitCase match { case ExitCase.Completed => ().pure[F] case _: ExitCase.Error[Throwable] => release case ExitCase.Canceled => release } } } } } }