Skip to content

Commit bd02450

Browse files
authored
Merge pull request #3841 from armanbilge/issue/3840
Acquire exclusive lock before finalizing `Hotswap`
2 parents 541d6f5 + 7d73c73 commit bd02450

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

std/shared/src/main/scala/cats/effect/std/Hotswap.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ object Hotswap {
113113

114114
def finalize(state: Ref[F, State]): F[Unit] =
115115
state.getAndSet(Finalized).flatMap {
116-
case Acquired(_, finalizer) => finalizer
116+
case Acquired(_, finalizer) => exclusive.surround(finalizer)
117117
case Cleared => F.unit
118118
case Finalized => raise("Hotswap already finalized")
119119
}

tests/shared/src/test/scala/cats/effect/std/HotswapSpec.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,23 @@ class HotswapSpec extends BaseSpec { outer =>
9797
go must completeAs(())
9898
}
9999

100+
"not finalize Hotswap while resource is in use" in ticked { implicit ticker =>
101+
val r = Resource.make(IO.ref(true))(_.set(false))
102+
val go = Hotswap.create[IO, Ref[IO, Boolean]].allocated.flatMap {
103+
case (hs, fin) =>
104+
hs.swap(r) *> (IO.sleep(1.second) *> fin).background.surround {
105+
hs.get.use {
106+
case Some(ref) =>
107+
val notReleased = ref.get.flatMap(b => IO(b must beTrue))
108+
notReleased *> IO.sleep(2.seconds) *> notReleased.void
109+
case None => IO(false must beTrue).void
110+
}
111+
}
112+
}
113+
114+
go must completeAs(())
115+
}
116+
100117
"resource can be accessed concurrently" in ticked { implicit ticker =>
101118
val go = Hotswap.create[IO, Unit].use { hs =>
102119
hs.swap(Resource.unit) *>

0 commit comments

Comments
 (0)