Close-on-exit feature

This commit is contained in:
Andrew Golovashevich 2025-08-24 04:18:54 +03:00
parent 7131a38bd4
commit 96931fda41
9 changed files with 109 additions and 52 deletions

View File

@ -59,8 +59,20 @@ public sealed class ChildCloseableState<T : CloseableState> : CloseableState {
override val isInUse: Boolean
get() = this._state.isInUse
@ManualStateManipulation
override fun finishUsage(close: Boolean) {
this._state.finishUsage(close)
}
override fun toString(): String {
TODO("Not yet implemented")
when {
this._state is OwnedErrorOnConcurrentAccessState || this._state::class === ErrorOnConcurrentAccessState::class -> {
val base = this._state.toString()
return base.substring(0, base.length - 1) + " (child of ${this._parent})>"
}
else -> return "<child closeable state ${this._state} (parent if ${this._parent})>"
}
}
}
}

View File

@ -20,5 +20,8 @@ public interface CloseableState {
public interface ExternallySynchronized : CloseableState {
public val isInUse: Boolean
@ManualStateManipulation
public fun finishUsage(close: Boolean)
}
}

View File

@ -6,6 +6,7 @@ public class CloseableStateCloseableWrapper(
) : CloseableState.ExternallySynchronized {
override val isInUse: Boolean by this._self::isInUse
override val isClosed: Boolean by this._self::isInUse
override fun assertNotClosed(): Unit =
@ -25,6 +26,13 @@ public class CloseableStateCloseableWrapper(
}
}
@ManualStateManipulation
override fun finishUsage(close: Boolean) {
this._self.tryFinishUsageThenClose(close) {
this._parent.finishUsage()
}
}
@ManualStateManipulation
override fun close() {
this._self.close()

View File

@ -21,6 +21,9 @@ public expect open class ErrorOnConcurrentAccessState : CloseableState.Externall
@ManualStateManipulation
public final override fun finishUsage()
@ManualStateManipulation
public final override fun finishUsage(close: Boolean)
@ManualStateManipulation
public final override fun close()

View File

@ -7,23 +7,6 @@ import kotlin.contracts.contract
import kotlin.jvm.JvmName
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
@OptIn(ManualStateManipulation::class)
public inline fun <S : CloseableState, R> autoClosed(
constructor: () -> S,
scope: (S) -> R,
): R {
contract {
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
}
val state = constructor()
return safeAutoClose1(
action = { scope(state) },
finally = state::close
)
}
@OptIn(ManualStateManipulation::class)
public inline fun <R> CloseableState.childAC(
constructor: () -> CloseableState.AllowsConcurrency = ::UsagesCounter,

View File

@ -1,25 +0,0 @@
package ru.landgrafhomyak.utility.closeable_state_1.internal
import kotlin.jvm.JvmStatic
internal object Misc {
@JvmStatic
internal inline fun <T> _compareAndExchange(get: () -> T, cas: (T, T) -> Boolean, expected: T, newValue: T): T {
while (true) {
val old = get()
if (old != expected) return old
if (cas(old, newValue)) return old
}
}
@JvmStatic
internal inline fun <T> _getAndUpdate(get: () -> T, cas: (T, T) -> Boolean, operator: (T) -> T): T {
while (true) {
val cur = get()
val upd = operator(cur)
if (cas(cur, upd)) return cur
}
}
internal const val CLOSED_STATE_VALUE = -0x4000_0000_0000_0000L
}

View File

@ -9,7 +9,23 @@ import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
@OptIn(ManualStateManipulation::class)
@JvmName("use\$kt")
public inline fun <S : CloseableState, R> autoClosed(
constructor: () -> S,
scope: (S) -> R,
): R {
contract {
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
}
val state = constructor()
return safeAutoClose1(
action = { scope(state) },
finally = state::close
)
}
@OptIn(ManualStateManipulation::class)
@JvmName("withUse\$kt")
public inline fun <R> CloseableState.withUse(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
@ -37,4 +53,43 @@ public inline fun <R> CloseableState.tryFinishUsage(block: () -> R): R {
}
this.assertNotClosed()
return safeAutoClose2(action = block, onSuccess = this::finishUsage)
}
}
@OptIn(ManualStateManipulation::class)
@JvmName("withUseThenClose\$kt")
public inline fun CloseableState.ExternallySynchronized.withUseThenClose(block: () -> Boolean) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
this.startUsage()
var needClose = false
return safeAutoClose1(
action = { needClose = block() },
finally = { this.finishUsage(needClose) }
)
}
@ManualStateManipulation
@JvmName("tryFinishUsageThenClose\$kt")
public inline fun <R> CloseableState.ExternallySynchronized.tryFinishUsageThenClose(close: Boolean, block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
this.assertNotClosed()
return safeAutoClose2(action = block, onSuccess = { this.finishUsage(close) })
}
@ManualStateManipulation
@JvmName("tryFinishUsageThenClose\$kt")
public inline fun CloseableState.ExternallySynchronized.tryFinishUsageThenClose(block: () -> Boolean) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
this.assertNotClosed()
var needClose = false
return safeAutoClose2(
action = { needClose = block() },
onSuccess = { this.finishUsage(needClose) }
)
}

View File

@ -44,17 +44,27 @@ public /* open */ class jErrorOnConcurrentAccessState
}
}
@Override
public final void finishUsage() {
switch (this._currentState.compareAndExchange(State.IN_USE, State.OPEN)) {
private void _finishUsage(State nextState) {
switch (this._currentState.compareAndExchange(State.IN_USE, nextState)) {
case OPEN:
throw new IllegalStateException("Can't finish usage because not it not started");
throw new IllegalStateException("Can't finish usage because it isn't started");
case CLOSED:
this.throwClosed();
case IN_USE:
}
}
@Override
public final void finishUsage() {
this._finishUsage(State.OPEN);
}
@Override
public void finishUsage(boolean close) {
this._finishUsage(close ? State.CLOSED : State.OPEN);
}
@Override
public final void close() {
switch (this._currentState.compareAndExchange(State.OPEN, State.CLOSED)) {

View File

@ -43,14 +43,22 @@ public actual open class ErrorOnConcurrentAccessState : CloseableState.Externall
}
@ManualStateManipulation
public actual final override fun finishUsage() {
when (this._state.compareAndExchange(State.IN_USE, State.OPEN)) {
State.OPEN -> throw IllegalStateException("Can't finish usage because not it not started")
private fun _finishUsage(nextState: State) {
when (this._state.compareAndExchange(State.IN_USE, nextState)) {
State.OPEN -> throw IllegalStateException("Can't finish usage because it isn't started")
State.IN_USE -> {}
State.CLOSED -> this.throwClosed()
}
}
@ManualStateManipulation
public actual final override fun finishUsage(): Unit =
this._finishUsage(State.OPEN)
@ManualStateManipulation
public actual final override fun finishUsage(close: Boolean): Unit =
this._finishUsage(if (close) State.CLOSED else State.OPEN)
@ManualStateManipulation
public actual final override fun close() {
when (this._state.compareAndExchange(State.OPEN, State.CLOSED)) {