Skip to content

Commit

Permalink
Simplify AutoCloseScope interface to a single method.
Browse files Browse the repository at this point in the history
Remove unnecessary suppressions for usages of AutoClosable
Force closing on non-local returns
  • Loading branch information
kyay10 committed Oct 31, 2024
1 parent bc9bf92 commit c6cc54d
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 155 deletions.
7 changes: 4 additions & 3 deletions arrow-libs/core/arrow-autoclose/api/arrow-autoclose.api
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
public abstract interface class arrow/AutoCloseScope {
public abstract fun autoClose (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public abstract fun install (Ljava/lang/AutoCloseable;)Ljava/lang/AutoCloseable;
public abstract fun onClose (Lkotlin/jvm/functions/Function1;)V
}

public final class arrow/AutoCloseScope$DefaultImpls {
public static fun install (Larrow/AutoCloseScope;Ljava/lang/AutoCloseable;)Ljava/lang/AutoCloseable;
}

public final class arrow/AutoCloseScopeKt {
public static final fun autoClose (Larrow/AutoCloseScope;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static final fun autoCloseScope (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}

public final class arrow/DefaultAutoCloseScope : arrow/AutoCloseScope {
public fun <init> ()V
public fun autoClose (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public final fun close (Ljava/lang/Throwable;)Ljava/lang/Void;
public final fun close (Ljava/lang/Throwable;)V
public fun install (Ljava/lang/AutoCloseable;)Ljava/lang/AutoCloseable;
public fun onClose (Lkotlin/jvm/functions/Function1;)V
}

public final class arrow/ThrowIfFatalKt {
Expand Down
7 changes: 4 additions & 3 deletions arrow-libs/core/arrow-autoclose/api/arrow-autoclose.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@

// Library unique name: <io.arrow-kt:arrow-autoclose>
abstract interface arrow/AutoCloseScope { // arrow/AutoCloseScope|null[0]
abstract fun <#A1: kotlin/Any?> autoClose(kotlin/Function0<#A1>, kotlin/Function2<#A1, kotlin/Throwable?, kotlin/Unit>): #A1 // arrow/AutoCloseScope.autoClose|autoClose(kotlin.Function0<0:0>;kotlin.Function2<0:0,kotlin.Throwable?,kotlin.Unit>){0§<kotlin.Any?>}[0]
abstract fun onClose(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>) // arrow/AutoCloseScope.onClose|onClose(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
open fun <#A1: kotlin/AutoCloseable> install(#A1): #A1 // arrow/AutoCloseScope.install|install(0:0){0§<kotlin.AutoCloseable>}[0]
}

final class arrow/DefaultAutoCloseScope : arrow/AutoCloseScope { // arrow/DefaultAutoCloseScope|null[0]
constructor <init>() // arrow/DefaultAutoCloseScope.<init>|<init>(){}[0]

final fun <#A1: kotlin/Any?> autoClose(kotlin/Function0<#A1>, kotlin/Function2<#A1, kotlin/Throwable?, kotlin/Unit>): #A1 // arrow/DefaultAutoCloseScope.autoClose|autoClose(kotlin.Function0<0:0>;kotlin.Function2<0:0,kotlin.Throwable?,kotlin.Unit>){0§<kotlin.Any?>}[0]
final fun close(kotlin/Throwable?): kotlin/Nothing? // arrow/DefaultAutoCloseScope.close|close(kotlin.Throwable?){}[0]
final fun close(kotlin/Throwable?) // arrow/DefaultAutoCloseScope.close|close(kotlin.Throwable?){}[0]
final fun onClose(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>) // arrow/DefaultAutoCloseScope.onClose|onClose(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
}

final fun (kotlin/Throwable).arrow/throwIfFatal(): kotlin/Throwable // arrow/throwIfFatal|[email protected](){}[0]
final inline fun <#A: kotlin/Any?> (arrow/AutoCloseScope).arrow/autoClose(kotlin/Function0<#A>, crossinline kotlin/Function2<#A, kotlin/Throwable?, kotlin/Unit>): #A // arrow/autoClose|[email protected](kotlin.Function0<0:0>;kotlin.Function2<0:0,kotlin.Throwable?,kotlin.Unit>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/Any?> arrow/autoCloseScope(kotlin/Function1<arrow/AutoCloseScope, #A>): #A // arrow/autoCloseScope|autoCloseScope(kotlin.Function1<arrow.AutoCloseScope,0:0>){0§<kotlin.Any?>}[0]
1 change: 0 additions & 1 deletion arrow-libs/core/arrow-autoclose/knit.code.include
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// This file was automatically generated from ${file.name} by Knit tool. Do not edit.
@file:OptIn(ExperimentalStdlibApi::class)
package ${knit.package}.${knit.name}

import arrow.AutoCloseScope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,42 +63,46 @@ import kotlin.coroutines.cancellation.CancellationException
*/
public inline fun <A> autoCloseScope(block: AutoCloseScope.() -> A): A {
val scope = DefaultAutoCloseScope()
var finalized = false
return try {
block(scope)
.also { scope.close(null) }
} catch (e: CancellationException) {
scope.close(e) ?: throw e
finalized = true
scope.close(e)
throw e
} catch (e: Throwable) {
scope.close(e.throwIfFatal()) ?: throw e
finalized = true
scope.close(e.throwIfFatal())
throw e
} finally {
if (!finalized) {
scope.close(null)
}
}
}

public interface AutoCloseScope {
public fun <A> autoClose(
acquire: () -> A,
release: (A, Throwable?) -> Unit
): A
public fun onClose(release: (Throwable?) -> Unit)

@ExperimentalStdlibApi
public fun <A : AutoCloseable> install(autoCloseable: A): A =
autoClose({ autoCloseable }) { a, _ -> a.close() }
}

public inline fun <A> AutoCloseScope.autoClose(
acquire: () -> A,
crossinline release: (A, Throwable?) -> Unit
): A = acquire().also { a -> onClose { release(a, it) } }

@PublishedApi
internal class DefaultAutoCloseScope : AutoCloseScope {
private val finalizers = Atomic(emptyList<(Throwable?) -> Unit>())

override fun <A> autoClose(acquire: () -> A, release: (A, Throwable?) -> Unit): A =
try {
acquire().also { a ->
finalizers.update { it + { e -> release(a, e) } }
}
} catch (e: Throwable) {
throw e
}
override fun onClose(release: (Throwable?) -> Unit) {
finalizers.update { it + release }
}

fun close(error: Throwable?): Nothing? {
return finalizers.get().asReversed().fold(error) { acc, function ->
fun close(error: Throwable?) {
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 @@ -10,7 +10,6 @@ import kotlinx.coroutines.test.runTest
import kotlin.coroutines.cancellation.CancellationException
import kotlin.test.Test

@OptIn(ExperimentalStdlibApi::class)
class AutoCloseTest {

@Test
Expand Down Expand Up @@ -136,6 +135,23 @@ class AutoCloseTest {
res.isActive() shouldBe false
}

@Test
fun closeTheAutoScopeOnNonLocalReturn() = runTest {
val wasActive = CompletableDeferred<Boolean>()
val res = Resource()

run {
autoCloseScope {
val r = install(res)
wasActive.complete(r.isActive())
return@run
}
}

wasActive.await() shouldBe true
res.isActive() shouldBe false
}

@Test
fun closeInReversedOrder() = runTest {
val res1 = Resource()
Expand Down Expand Up @@ -172,7 +188,6 @@ class AutoCloseTest {
closed.cancel()
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.test.runTest
import kotlin.test.Test

@OptIn(ExperimentalStdlibApi::class)
class AutoCloseJvmTest {

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// This file was automatically generated from AutoCloseScope.kt by Knit tool. Do not edit.
@file:OptIn(ExperimentalStdlibApi::class)
package arrow.autocloseable.examples.exampleAutocloseable01

import arrow.AutoCloseScope
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// This file was automatically generated from AutoCloseScope.kt by Knit tool. Do not edit.
@file:OptIn(ExperimentalStdlibApi::class)
package arrow.autocloseable.examples.exampleAutocloseable02

import arrow.AutoCloseScope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,18 +510,18 @@ private value class ResourceScopeImpl(
}
})

override fun <A> autoClose(acquire: () -> A, release: (A, Throwable?) -> Unit): A =
acquire().also { a ->
val finalizer: suspend (ExitCase) -> Unit = { exitCase ->
val errorOrNull = when (exitCase) {
ExitCase.Completed -> null
is ExitCase.Cancelled -> exitCase.exception
is ExitCase.Failure -> exitCase.failure
}
release(a, errorOrNull)
}
finalizers.update(finalizer::prependTo)
override fun onClose(release: (Throwable?) -> Unit) {
val finalizer: suspend (ExitCase) -> Unit = { exitCase ->
release(exitCase.errorOrNull)
}
finalizers.update(finalizer::prependTo)
}

private val ExitCase.errorOrNull get() = when (this) {
ExitCase.Completed -> null
is ExitCase.Cancelled -> exception
is ExitCase.Failure -> failure
}

suspend fun cancelAll(
exitCase: ExitCase,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package arrow.fx.coroutines

import arrow.atomic.AtomicBoolean
import arrow.autoClose
import arrow.core.Either
import arrow.core.left
import arrow.core.raise.either
Expand Down Expand Up @@ -634,7 +635,6 @@ class ResourceTest {
}
}

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

Expand Down
Loading

0 comments on commit c6cc54d

Please sign in to comment.