Skip to content

Commit

Permalink
Ensure AutoCloseScope and ResourceScope only ever call finalizers once
Browse files Browse the repository at this point in the history
  • Loading branch information
kyay10 committed Nov 9, 2024
1 parent 4be9612 commit 80c5675
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package arrow

import arrow.atomic.Atomic
import arrow.atomic.update
import arrow.atomic.value
import kotlin.coroutines.cancellation.CancellationException

/**
Expand Down Expand Up @@ -97,7 +96,7 @@ internal class DefaultAutoCloseScope : AutoCloseScope {
}

fun close(error: Throwable?): Nothing? {
return finalizers.value.asReversed().fold(error) { acc, finalizer ->
return finalizers.getAndSet(emptyList()).asReversed().fold(error) { acc, finalizer ->
acc.add(runCatching { finalizer(error) }.exceptionOrNull())
}?.let { throw it }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package arrow.fx.coroutines
import arrow.AutoCloseScope
import arrow.atomic.Atomic
import arrow.atomic.update
import arrow.atomic.value
import arrow.core.nonFatalOrThrow
import arrow.core.prependTo
import kotlinx.coroutines.CoroutineDispatcher
Expand Down Expand Up @@ -471,7 +470,7 @@ internal class ResourceScopeImpl : ResourceScope {

suspend fun cancelAll(exitCase: ExitCase) {
withContext(NonCancellable) {
finalizers.value.fold(exitCase.errorOrNull) { acc, finalizer ->
finalizers.getAndSet(emptyList()).fold(exitCase.errorOrNull) { acc, finalizer ->
acc.add(runCatching { finalizer(exitCase) }.exceptionOrNull())
}
}?.let { throw it }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,21 @@ class ResourceTest {
}
}

@OptIn(DelicateCoroutinesApi::class)
@Test
fun allocatedRunsReleasersOnlyOnce() = runTest {
val released = CompletableDeferred<ExitCase>()
val (_, release) =
resource {
onRelease { exitCase ->
require(released.complete(exitCase))
}
}.allocate()
release(ExitCase.Completed)
release(ExitCase.Completed)
released.shouldHaveCompleted() shouldBe ExitCase.Completed
}

private class Res : AutoCloseable {
private val isActive = AtomicBoolean(true)

Expand Down

0 comments on commit 80c5675

Please sign in to comment.