Skip to content

Commit

Permalink
fix(autoclose): close Closeables in reversed order (#3387)
Browse files Browse the repository at this point in the history
* fix(autoclose): close closeables in reversed order
issue #3386

* fix(autoclose): close closeables in reversed order
issue #3386

* fix test: suppressedExceptions
  • Loading branch information
hoc081098 authored Mar 5, 2024
1 parent e0a13f1 commit 484c0a2
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ internal class DefaultAutoCloseScope : AutoCloseScope {
}

fun close(error: Throwable?): Nothing? {
return finalizers.get().fold(error) { acc, function ->
return finalizers.get().asReversed().fold(error) { acc, function ->
acc.add(runCatching { function.invoke(error) }.exceptionOrNull())
}?.let { throw it }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import arrow.atomic.AtomicBoolean
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.toList
import kotlinx.coroutines.test.runTest
import kotlin.coroutines.cancellation.CancellationException
import kotlin.test.Test
Expand Down Expand Up @@ -76,7 +78,7 @@ class AutoCloseTest {
}

e shouldBe error
e.suppressedExceptions shouldBe listOf(error2, error3)
e.suppressedExceptions shouldBe listOf(error3, error2)
promise.await() shouldBe error
wasActive.await() shouldBe true
res.isActive() shouldBe false
Expand Down Expand Up @@ -134,6 +136,42 @@ class AutoCloseTest {
res.isActive() shouldBe false
}

@Test
fun closeInReversedOrder() = runTest {
val res1 = Resource()
val res2 = Resource()
val res3 = Resource()

val wasActive = Channel<Boolean>(Channel.UNLIMITED)
val closed = Channel<Resource>(Channel.UNLIMITED)

autoCloseScope {
val r1 = autoClose({ res1 }) { r, _ ->
closed.trySend(r).getOrThrow()
r.shutdown()
}
val r2 = autoClose({ res2 }) { r, _ ->
closed.trySend(r).getOrThrow()
r.shutdown()
}
val r3 = autoClose({ res3 }) { r, _ ->
closed.trySend(r).getOrThrow()
r.shutdown()
}

wasActive.trySend(r1.isActive()).getOrThrow()
wasActive.trySend(r2.isActive()).getOrThrow()
wasActive.trySend(r3.isActive()).getOrThrow()
wasActive.close()
}

wasActive.toList() shouldBe listOf(true, true, true)
closed.receive() shouldBe res3
closed.receive() shouldBe res2
closed.receive() shouldBe res1
closed.cancel()
}

@OptIn(ExperimentalStdlibApi::class)
private class Resource : AutoCloseable {
private val isActive = AtomicBoolean(true)
Expand Down

0 comments on commit 484c0a2

Please sign in to comment.