diff --git a/core/src/main/scala/ox/resource.scala b/core/src/main/scala/ox/resource.scala index 5898315e..1fe84802 100644 --- a/core/src/main/scala/ox/resource.scala +++ b/core/src/main/scala/ox/resource.scala @@ -38,8 +38,20 @@ inline def use[R, T](inline acquire: R, inline release: R => Unit)(inline f: R = */ inline def useInterruptible[R, T](inline acquire: R, inline release: R => Unit)(inline f: R => T): T = val r = acquire + var caught: Throwable = null try f(r) - finally release(r) + catch + case e: Throwable => + caught = e + null.asInstanceOf[T] + finally + if caught == null then release(r) + else + try release(r) + catch case e: Throwable => caught.addSuppressed(e) + finally throw caught + end try +end useInterruptible /** Use the given [[AutoCloseable]] resource, acquired using `acquire` in the given `f` code block. Releasing is [[uninterruptible]]. To use * multiple resources, consider creating a [[supervised]] scope and [[useCloseableInScope]] method. diff --git a/core/src/test/scala/ox/ResourceTest.scala b/core/src/test/scala/ox/ResourceTest.scala index bdad8dd7..c1c30940 100644 --- a/core/src/test/scala/ox/ResourceTest.scala +++ b/core/src/test/scala/ox/ResourceTest.scala @@ -143,4 +143,23 @@ class ResourceTest extends AnyFlatSpec with Matchers: trail.get shouldBe Vector("allocate", "in scope", "release") } + + it should "add suppressed exception when there's an exception during releasing" in { + val trail = Trail() + + class TestResource: + trail.add("allocate") + def release(): Unit = + trail.add("release") + throw new RuntimeException("e1") + + try + use(new TestResource, _.release()) { r => + trail.add("in scope") + throw new RuntimeException("e2") + } + catch case e => trail.add(s"exception ${e.getMessage} (${e.getSuppressed.map(_.getMessage).mkString(", ")})") + + trail.get shouldBe Vector("allocate", "in scope", "release", "exception e2 (e1)") + } end ResourceTest