Compare commits

...

9 Commits
v1.1 ... master

19 changed files with 333 additions and 11 deletions

View File

@ -15,7 +15,7 @@ buildscript {
} }
group = "ru.landgrafhomyak.utility" group = "ru.landgrafhomyak.utility"
version = "1.1" version = "1.4"
repositories { repositories {
mavenCentral() mavenCentral()
@ -82,6 +82,7 @@ xomrk {
jvmTest { jvmTest {
dependencies { dependencies {
implementation(kotlinStdlibDependency)
implementation("org.testng:testng:7.5.1") implementation("org.testng:testng:7.5.1")
} }
} }

View File

@ -25,10 +25,15 @@ public sealed class ChildCloseableState<T : CloseableState> : CloseableState {
override fun startUsage(): Unit = override fun startUsage(): Unit =
this._state.startUsage() this._state.startUsage()
@ManualStateManipulation
override fun startUsageIfNotClosed(): Boolean =
this._state.startUsageIfNotClosed()
@ManualStateManipulation @ManualStateManipulation
override fun finishUsage(): Unit = override fun finishUsage(): Unit =
this._state.finishUsage() this._state.finishUsage()
@Destructor
@ManualStateManipulation @ManualStateManipulation
override fun close() { override fun close() {
this._state.finishUsage() this._state.finishUsage()

View File

@ -8,9 +8,17 @@ public interface CloseableState {
@ManualStateManipulation @ManualStateManipulation
public fun startUsage() public fun startUsage()
/**
* Tries to start usage and returns `false` if entered or `true` if closed.
* Doesn't prevent from throwing error if this state forbids concurrent access.
*/
@ManualStateManipulation
public fun startUsageIfNotClosed(): Boolean
@ManualStateManipulation @ManualStateManipulation
public fun finishUsage() public fun finishUsage()
@Destructor
@ManualStateManipulation @ManualStateManipulation
public fun close() public fun close()

View File

@ -13,12 +13,16 @@ public expect class CloseableStateCloseableWrapper : CloseableState.ExternallySy
@ManualStateManipulation @ManualStateManipulation
override fun startUsage() override fun startUsage()
@ManualStateManipulation
override fun startUsageIfNotClosed(): Boolean
@ManualStateManipulation @ManualStateManipulation
override fun finishUsage() override fun finishUsage()
@ManualStateManipulation @ManualStateManipulation
override fun finishUsage(close: Boolean) override fun finishUsage(close: Boolean)
@Destructor
@ManualStateManipulation @ManualStateManipulation
override fun close() override fun close()

View File

@ -0,0 +1,8 @@
package ru.landgrafhomyak.utility.closeable_state_1
/**
* Utility annotation to simplify searching method that closes/destructs/deallocs object.
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
public annotation class Destructor()

View File

@ -18,12 +18,16 @@ public expect open class ErrorOnConcurrentAccessState : CloseableState.Externall
@ManualStateManipulation @ManualStateManipulation
public final override fun startUsage() public final override fun startUsage()
@ManualStateManipulation
public final override fun startUsageIfNotClosed(): Boolean
@ManualStateManipulation @ManualStateManipulation
public final override fun finishUsage() public final override fun finishUsage()
@ManualStateManipulation @ManualStateManipulation
public final override fun finishUsage(close: Boolean) public final override fun finishUsage(close: Boolean)
@Destructor
@ManualStateManipulation @ManualStateManipulation
public final override fun close() public final override fun close()

View File

@ -0,0 +1,26 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
public class HandleWrapper<H>(
public val handle: H,
public val state: CloseableState,
) {
public fun <R> useHandle(method: (H) -> R): R {
contract {
callsInPlace(method, InvocationKind.EXACTLY_ONCE)
}
return this.state.withUse { method(this.handle) }
}
@Destructor
@ManualStateManipulation
public fun close() {
this.state.close()
}
override fun toString(): String = "<wrapper of handle=${this.handle} protected with state=${this.state}>"
}

View File

@ -19,9 +19,13 @@ public expect open class UsagesCounter : CloseableState.AllowsConcurrency {
@ManualStateManipulation @ManualStateManipulation
public final override fun startUsage() public final override fun startUsage()
@ManualStateManipulation
public final override fun startUsageIfNotClosed(): Boolean
@ManualStateManipulation @ManualStateManipulation
public final override fun finishUsage() public final override fun finishUsage()
@Destructor
@ManualStateManipulation @ManualStateManipulation
public final override fun close() public final override fun close()

View File

@ -41,8 +41,8 @@ public inline fun <R> CloseableState.childES(
@OptIn(ManualStateManipulation::class) @OptIn(ManualStateManipulation::class)
public inline fun <R> CloseableState.ExternallySynchronized.closeableWrapper( public inline fun <R> CloseableState.ExternallySynchronized.closeableWrapper(
constructor: () -> CloseableState.ExternallySynchronized = ::ErrorOnConcurrentAccessState, crossinline constructor: () -> CloseableState.ExternallySynchronized = ::ErrorOnConcurrentAccessState,
scope: (CloseableState.ExternallySynchronized) -> R, noinline scope: (CloseableState.ExternallySynchronized) -> R,
): R { ): R {
contract { contract {
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE) callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)

View File

@ -10,7 +10,7 @@ import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
@OptIn(ManualStateManipulation::class) @OptIn(ManualStateManipulation::class)
@JvmName("autoClosed\$kt") @JvmName("autoClosed\$kt")
public inline fun <S : CloseableState, R> autoClosed( public fun <S : CloseableState, R> autoClosed(
constructor: () -> S, constructor: () -> S,
scope: (S) -> R, scope: (S) -> R,
): R { ): R {
@ -91,3 +91,27 @@ public inline fun CloseableState.ExternallySynchronized.tryFinishUsageThenClose(
onSuccess = { this.finishUsage(needClose) } onSuccess = { this.finishUsage(needClose) }
) )
} }
@OptIn(ManualStateManipulation::class)
@JvmName("withUseIfNotClosed\$kt")
public inline fun <R> CloseableState.withUseIfNotClosed(ifClosed: R, block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (this.startUsageIfNotClosed())
return ifClosed
return safeAutoClose1(action = block, finally = { this.finishUsage() })
}
@OptIn(ManualStateManipulation::class)
@JvmName("withUseIfNotClosed\$kt")
public inline fun <R> CloseableState.withUseIfNotClosed(ifClosed: () -> R, block: () -> R): R {
contract {
callsInPlace(ifClosed, InvocationKind.AT_MOST_ONCE)
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (this.startUsageIfNotClosed())
return ifClosed()
return safeAutoClose1(action = block, finally = { this.finishUsage() })
}

View File

@ -0,0 +1,110 @@
package ru.landgrafhomyak.utility.closeable_state_1;
import java.util.concurrent.atomic.AtomicReference;
public /* open */ class SpinLockSynchronizedState
extends SpinLockSynchronizedState$Errors
implements CloseableState.ExternallySynchronized {
private enum State {
OPEN, IN_USE, CLOSED
}
private final AtomicReference<State> _currentState;
public SpinLockSynchronizedState() {
this._currentState = new AtomicReference<>(State.OPEN);
}
@Override
public final boolean isInUse() {
return this._currentState.get() == State.IN_USE;
}
@Override
public final boolean isClosed() {
return this._currentState.get() == State.CLOSED;
}
@Override
public final void assertNotClosed() {
if (this.isClosed())
this.throwClosed();
}
@ManualStateManipulation
@Override
public final void startUsage() {
if (this.startUsageIfNotClosed())
this.throwClosed();
}
@ManualStateManipulation
@Override
public final boolean startUsageIfNotClosed() {
while (true) {
switch (this._currentState.compareAndExchange(State.OPEN, State.IN_USE)) {
case IN_USE:
Thread.onSpinWait();
continue;
case CLOSED:
return true;
case OPEN:
return false;
}
}
}
private void _finishUsage(State nextState) {
switch (this._currentState.compareAndExchange(State.IN_USE, nextState)) {
case OPEN:
throw new IllegalStateException("Can't finish usage because it isn't started");
case CLOSED:
this.throwClosed();
case IN_USE:
}
}
@ManualStateManipulation
@Override
public final void finishUsage() {
this._finishUsage(State.OPEN);
}
@ManualStateManipulation
@Override
public void finishUsage(boolean close) {
this._finishUsage(close ? State.CLOSED : State.OPEN);
}
@Destructor
@ManualStateManipulation
@Override
public final void close() {
while (true) {
switch (this._currentState.compareAndExchange(State.OPEN, State.CLOSED)) {
case CLOSED:
this.throwClosed();
case IN_USE:
Thread.onSpinWait();
continue;
case OPEN:
return;
}
}
}
@Override
public String toString() {
switch (this._currentState.get()) {
case CLOSED:
return "<closeable spinlock-synchronized state [free]>";
case IN_USE:
return "<closeable spinlock-synchronized state [in use]>";
case OPEN:
return "<closeable spinlock-synchronized state [closed]>";
}
throw new RuntimeException("Unreachable");
}
}

View File

@ -31,6 +31,7 @@ public final class jCloseableStateCloseableWrapper
this._self.assertNotClosed(); this._self.assertNotClosed();
} }
@ManualStateManipulation
@Override @Override
public void startUsage() { public void startUsage() {
this._self.startUsage(); this._self.startUsage();
@ -46,18 +47,43 @@ public final class jCloseableStateCloseableWrapper
} }
} }
@ManualStateManipulation
@Override
public boolean startUsageIfNotClosed() {
if (this._self.startUsageIfNotClosed())
return true;
try {
if (this._parent.startUsageIfNotClosed()) {
this._self.finishUsage();
return true;
}
} catch (Throwable e1) {
try {
this._self.finishUsage();
} catch (Throwable e2) {
e1.addSuppressed(e2);
}
throw e1;
}
return false;
}
@ManualStateManipulation
@Override @Override
public void finishUsage() { public void finishUsage() {
this._parent.finishUsage(); this._parent.finishUsage();
this._self.finishUsage(); this._self.finishUsage();
} }
@ManualStateManipulation
@Override @Override
public void finishUsage(boolean close) { public void finishUsage(boolean close) {
this._parent.finishUsage(); this._parent.finishUsage();
this._self.finishUsage(close); this._self.finishUsage(close);
} }
@Destructor
@ManualStateManipulation
@Override @Override
public void close() { public void close() {
this._self.close(); this._self.close();
@ -67,4 +93,5 @@ public final class jCloseableStateCloseableWrapper
public String toString() { public String toString() {
return "<closeable state " + this._parent + " wrapped with " + this._self + ">"; return "<closeable state " + this._parent + " wrapped with " + this._self + ">";
} }
} }

View File

@ -32,16 +32,25 @@ public /* open */ class jErrorOnConcurrentAccessState
this.throwClosed(); this.throwClosed();
} }
@ManualStateManipulation
@Override @Override
public final void startUsage() { public final void startUsage() {
if (this.startUsageIfNotClosed())
this.throwClosed();
}
@ManualStateManipulation
@Override
public final boolean startUsageIfNotClosed() {
switch (this._currentState.compareAndExchange(State.OPEN, State.IN_USE)) { switch (this._currentState.compareAndExchange(State.OPEN, State.IN_USE)) {
case IN_USE: case IN_USE:
this.throwConcurrent(); this.throwConcurrent();
case CLOSED: case CLOSED:
this.throwClosed(); return true;
case OPEN: case OPEN:
break; return false;
} }
throw new RuntimeException("Unreachable");
} }
private void _finishUsage(State nextState) { private void _finishUsage(State nextState) {
@ -55,16 +64,20 @@ public /* open */ class jErrorOnConcurrentAccessState
} }
@ManualStateManipulation
@Override @Override
public final void finishUsage() { public final void finishUsage() {
this._finishUsage(State.OPEN); this._finishUsage(State.OPEN);
} }
@ManualStateManipulation
@Override @Override
public void finishUsage(boolean close) { public void finishUsage(boolean close) {
this._finishUsage(close ? State.CLOSED : State.OPEN); this._finishUsage(close ? State.CLOSED : State.OPEN);
} }
@Destructor
@ManualStateManipulation
@Override @Override
public final void close() { public final void close() {
switch (this._currentState.compareAndExchange(State.OPEN, State.CLOSED)) { switch (this._currentState.compareAndExchange(State.OPEN, State.CLOSED)) {

View File

@ -22,20 +22,28 @@ public /* open */ class jUsagesCounter
this.throwClosed(); this.throwClosed();
} }
@ManualStateManipulation
@Override @Override
public final void startUsage() { public final void startUsage() {
if (this.startUsageIfNotClosed())
this.throwClosed();
}
@ManualStateManipulation
@Override
public final boolean startUsageIfNotClosed() {
while (true) { while (true) {
final long cur = this._currentUsagesCount.get(); final long cur = this._currentUsagesCount.get();
if (cur < 0) { if (cur < 0) {
this.throwClosed(); return true;
return;
} }
if (this._currentUsagesCount.compareAndSet(cur, cur + 1)) if (this._currentUsagesCount.compareAndSet(cur, cur + 1))
return; return false;
} }
} }
@ManualStateManipulation
@Override @Override
public final void finishUsage() { public final void finishUsage() {
while (true) { while (true) {
@ -50,6 +58,8 @@ public /* open */ class jUsagesCounter
} }
} }
@Destructor
@ManualStateManipulation
@Override @Override
public final void close() { public final void close() {
long currentReferencesCount; long currentReferencesCount;

View File

@ -0,0 +1,33 @@
@file:OptIn(ExperimentalContracts::class)
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.contracts.ExperimentalContracts
public class OwnedSpinLockSynchronizedState : SpinLockSynchronizedState {
private var _owner: Any?
public constructor() : super() {
this._owner = null
}
public constructor(owner: Any) : super() {
this._owner = owner
}
public var owner: Any
get() = this._owner ?: throw IllegalStateException("Owner not set yet")
set(value) {
if (this._owner != null) throw IllegalStateException("Owner already initialized")
this._owner = value
}
override fun throwClosed(): Nothing {
throw IllegalStateException("Object is closed: ${this._owner}")
}
override fun toString(): String {
val base = super.toString()
return base.substring(0, base.length - 1) + " of ${this._owner}>"
}
}

View File

@ -0,0 +1,7 @@
package ru.landgrafhomyak.utility.closeable_state_1
internal abstract class `SpinLockSynchronizedState$Errors` : CloseableState.ExternallySynchronized {
protected open fun throwClosed(): Nothing {
throw IllegalStateException("Object is closed")
}
}

View File

@ -36,6 +36,26 @@ public actual class CloseableStateCloseableWrapper : CloseableState.ExternallySy
} }
} }
@ManualStateManipulation
actual override fun startUsageIfNotClosed(): Boolean {
if (this._self.startUsageIfNotClosed())
return true
try {
if (this._parent.startUsageIfNotClosed()) {
this._self.finishUsage()
return true
}
} catch (e1: Throwable) {
try {
this._self.finishUsage()
} catch (e2: Throwable) {
e1.addSuppressed(e2)
}
throw e1
}
return false;
}
@ManualStateManipulation @ManualStateManipulation
actual override fun finishUsage() { actual override fun finishUsage() {
this._parent.finishUsage() this._parent.finishUsage()
@ -48,6 +68,7 @@ public actual class CloseableStateCloseableWrapper : CloseableState.ExternallySy
this._self.finishUsage(close) this._self.finishUsage(close)
} }
@Destructor
@ManualStateManipulation @ManualStateManipulation
actual override fun close() { actual override fun close() {
this._self.close() this._self.close()

View File

@ -35,10 +35,16 @@ public actual open class ErrorOnConcurrentAccessState : CloseableState.Externall
@ManualStateManipulation @ManualStateManipulation
public actual final override fun startUsage() { public actual final override fun startUsage() {
if (this.startUsageIfNotClosed())
this.throwClosed()
}
@ManualStateManipulation
public actual final override fun startUsageIfNotClosed(): Boolean {
when (this._state.compareAndExchange(State.OPEN, State.IN_USE)) { when (this._state.compareAndExchange(State.OPEN, State.IN_USE)) {
State.OPEN -> {} State.OPEN -> return false;
State.IN_USE -> this.throwConcurrent() State.IN_USE -> this.throwConcurrent()
State.CLOSED -> this.throwClosed() State.CLOSED -> return true
} }
} }
@ -59,6 +65,7 @@ public actual open class ErrorOnConcurrentAccessState : CloseableState.Externall
public actual final override fun finishUsage(close: Boolean): Unit = public actual final override fun finishUsage(close: Boolean): Unit =
this._finishUsage(if (close) State.CLOSED else State.OPEN) this._finishUsage(if (close) State.CLOSED else State.OPEN)
@Destructor
@ManualStateManipulation @ManualStateManipulation
public actual final override fun close() { public actual final override fun close() {
when (this._state.compareAndExchange(State.OPEN, State.CLOSED)) { when (this._state.compareAndExchange(State.OPEN, State.CLOSED)) {

View File

@ -38,11 +38,21 @@ public actual open class UsagesCounter : CloseableState.AllowsConcurrency {
} }
} }
@ManualStateManipulation
public actual final override fun startUsageIfNotClosed(): Boolean {
this._value.update { o ->
if (o < 0) return true
return@update o + 1
}
return false
}
@ManualStateManipulation @ManualStateManipulation
public actual final override fun finishUsage() { public actual final override fun finishUsage() {
this._value.update(Long::dec) this._value.update(Long::dec)
} }
@Destructor
@ManualStateManipulation @ManualStateManipulation
public actual final override fun close() { public actual final override fun close() {
val state = this._value.compareAndExchange(0, _MiscMultiplatform.CLOSED_STATE_VALUE) val state = this._value.compareAndExchange(0, _MiscMultiplatform.CLOSED_STATE_VALUE)