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

parallel finalization in Concurrent[Resource]#both #1689

Merged
merged 2 commits into from
Feb 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ sealed abstract class Resource[F[_], +A] {
*/
def race[B](
that: Resource[F, B]
)(implicit F: Async[F]): Resource[F, Either[A, B]] =
Async[Resource[F, *]].race(this, that)
)(implicit F: Concurrent[F]): Resource[F, Either[A, B]] =
Concurrent[Resource[F, *]].race(this, that)

/**
* Implementation for the `flatMap` operation, as described via the
Expand Down Expand Up @@ -675,7 +675,7 @@ object Resource extends ResourceFOInstances0 with ResourceHOInstances0 with Reso
* Races the evaluation of two resource allocations and returns the result of the winner,
* except in the case of cancellation.
*/
def race[F[_]: Async, A, B](
def race[F[_]: Concurrent, A, B](
rfa: Resource[F, A],
rfb: Resource[F, B]
): Resource[F, Either[A, B]] =
Expand Down Expand Up @@ -1073,6 +1073,9 @@ abstract private[effect] class ResourceConcurrent[F[_]]

def ref[A](a: A): Resource[F, Ref[Resource[F, *], A]] =
Resource.eval(F.ref(a)).map(_.mapK(Resource.liftK[F]))

override def both[A, B](fa: Resource[F, A], fb: Resource[F, B]): Resource[F, (A, B)] =
Resource.both(fa, fb)
}

private[effect] trait ResourceClock[F[_]] extends Clock[Resource[F, *]] {
Expand Down
46 changes: 46 additions & 0 deletions tests/shared/src/test/scala/cats/effect/ResourceSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,52 @@ class ResourceSpec extends BaseSpec with ScalaCheck with Discipline {
}
}

"Concurrent[Resource]" >> {
"both" >> {
"parallel acquisition and release" in ticked { implicit ticker =>
var leftAllocated = false
var rightAllocated = false
var leftReleasing = false
var rightReleasing = false
var leftReleased = false
var rightReleased = false

val wait = IO.sleep(1.second)
val lhs = Resource.make(wait >> IO { leftAllocated = true }) { _ =>
IO { leftReleasing = true } >> wait >> IO { leftReleased = true }
}
val rhs = Resource.make(wait >> IO { rightAllocated = true }) { _ =>
IO { rightReleasing = true } >> wait >> IO { rightReleased = true }
}

Async[Resource[IO, *]].both(lhs, rhs).use(_ => wait).unsafeToFuture()

// after 1 second:
// both resources have allocated (concurrency, serially it would happen after 2 seconds)
// resources are still open during `use` (correctness)
ticker.ctx.tick(1.second)
leftAllocated must beTrue
rightAllocated must beTrue
leftReleasing must beFalse
rightReleasing must beFalse

// after 2 seconds:
// both resources have started cleanup (correctness)
ticker.ctx.tick(1.second)
leftReleasing must beTrue
rightReleasing must beTrue
leftReleased must beFalse
rightReleased must beFalse

// after 3 seconds:
// both resources have terminated cleanup (concurrency, serially it would happen after 4 seconds)
ticker.ctx.tick(1.second)
leftReleased must beTrue
rightReleased must beTrue
}
}
}

"Resource[Resource[IO, *], *]" should {
"flatten with finalizers inside-out" in ticked { implicit ticker =>
var results = ""
Expand Down