Skip to content

Commit db9dd87

Browse files
committed
Fix bug in Resource.flatMap when acquire throws an exception
1 parent cc5a25f commit db9dd87

File tree

3 files changed

+41
-12
lines changed

3 files changed

+41
-12
lines changed

library/testUtil/src/main/scala/japgolly/scalajs/react/test/internal/Resource.scala

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,23 @@ class Resource[F[_]: Effect, A](private val acquire: F[A], private val release:
1919
var aOpt: Option[A] = None
2020
var bReleaseOpt: Option[B => F[Unit]] = None
2121

22-
Resource.make(
23-
F.flatMap(acquire){ a =>
24-
aOpt = Some(a)
25-
val other: Resource[F, B] = f(a)
26-
bReleaseOpt = Some(other.release)
27-
other.acquire
28-
},
29-
b =>
30-
(aOpt, bReleaseOpt) match {
31-
case (Some(a), Some(bRelease)) => F.finallyRun(bRelease(b), release(a))
32-
case _ => F.throwException(new IllegalStateException("Resource.flatMap: release attempted without acquire being invoked") )
22+
val newAcquire: F[B] = F.flatMap(acquire) { a =>
23+
aOpt = Some(a)
24+
val acquireB: F[B] =
25+
F.flatMap(F.delay(f(a))) { otherResource =>
26+
bReleaseOpt = Some(otherResource.release)
27+
otherResource.acquire
3328
}
34-
)
29+
F.onError(acquireB, _ => release(a))
30+
}
31+
32+
val newRelease: B => F[Unit] = b =>
33+
(aOpt, bReleaseOpt) match {
34+
case (Some(a), Some(bRelease)) => F.finallyRun(bRelease(b), release(a))
35+
case _ => F.throwException(new IllegalStateException("Resource.flatMap: release attempted without a successful acquire"))
36+
}
37+
38+
Resource.make[F, B](newAcquire, newRelease)
3539
}
3640

3741
def map[B](f: A => B): Resource[F, B] =
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package japgolly.scalajs.react.test.internal
2+
3+
import japgolly.scalajs.react._
4+
import japgolly.microlibs.testutil.TestUtil._
5+
import utest._
6+
7+
object ResourceTest extends TestSuite {
8+
9+
override def tests = Tests {
10+
11+
"error_in_acquire" - {
12+
var n = 0
13+
val nRes = Resource.make[CallbackTo, Unit](Callback { n += 1 }, _ => Callback { n -= 1 })
14+
val errRes = Resource.make[CallbackTo, Int](CallbackTo { ??? : Int }, _ => Callback { n -= 100 })
15+
val r = nRes.flatMap(_ => nRes.flatMap(_ => errRes))
16+
val t = r.use_(_ => ()).attemptTry.runNow()
17+
assert(t.isFailure)
18+
assertEq(n, 0)
19+
}
20+
21+
}
22+
}

library/util/src/main/scala/japgolly/scalajs/react/util/Effect.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ trait Effect[F[_]] extends Monad[F] {
1717

1818
@inline final def throwException[A](t: Throwable): F[A] =
1919
delay(throw t)
20+
21+
def onError[A, B](fa: => F[A], f: Throwable => F[B]): F[A] =
22+
handleError[A, A](fa)(t => finallyRun(throwException[A](t), f(t)))
2023
}
2124

2225
object Effect extends EffectFallbacks {

0 commit comments

Comments
 (0)