Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f6823cfb9 | |||
| 6ea651ce6d | |||
| 5e9e741b51 | |||
| 8fc08d3e26 | |||
| 4ff6c0beb3 | |||
| 129cee26ca | |||
| e578517821 | |||
| 4b799c0ecf | |||
| bdb1fe56fa | |||
| 6bbade401f | |||
| 7d56a44323 | |||
| c078394921 | |||
| 96931fda41 | |||
| 7131a38bd4 | |||
| 999ff63ccf | |||
| 826f286404 | |||
| ad87bb7904 | |||
| 98d76569cc | |||
| c30c585f76 | |||
| ed06e6c2a0 | |||
| 05b9fc6078 | |||
| 8f277853bd | |||
| 131dd44d38 | |||
| 953af111b4 | |||
| e8d899928e | |||
| 860ffa98e0 | |||
| d4db1907d9 |
@ -15,7 +15,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "ru.landgrafhomyak.utility"
|
group = "ru.landgrafhomyak.utility"
|
||||||
version = "0.5"
|
version = "1.4"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -37,11 +37,12 @@ xomrk {
|
|||||||
|
|
||||||
compilations.configureEach {
|
compilations.configureEach {
|
||||||
compileJavaTaskProvider?.configure {
|
compileJavaTaskProvider?.configure {
|
||||||
targetCompatibility = "1.8"
|
targetCompatibility = "9"
|
||||||
|
sourceCompatibility = "9"
|
||||||
}
|
}
|
||||||
compileTaskProvider.configure {
|
compileTaskProvider.configure {
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
jvmTarget = JvmTarget.JVM_1_8
|
jvmTarget = JvmTarget.JVM_9
|
||||||
freeCompilerArgs.addAll(
|
freeCompilerArgs.addAll(
|
||||||
"-Xno-call-assertions",
|
"-Xno-call-assertions",
|
||||||
"-Xno-param-assertions",
|
"-Xno-param-assertions",
|
||||||
@ -64,13 +65,25 @@ xomrk {
|
|||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(kotlinStdlibDependency)
|
compileOnly(kotlinStdlibDependency)
|
||||||
implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.5")
|
implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.6")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val commonTest by getting {
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.jetbrains.kotlin:kotlin-test:${this@kotlin.coreLibrariesVersion}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val jvmMain by getting {
|
val jvmMain by getting {
|
||||||
dependsOn(commonMain)
|
dependsOn(commonMain)
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(kotlinStdlibDependency)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jvmTest {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlinStdlibDependency)
|
||||||
|
implementation("org.testng:testng:7.5.1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,11 +95,6 @@ xomrk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jvmTest {
|
|
||||||
dependencies {
|
|
||||||
implementation("org.testng:testng:7.5.1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configureEach {
|
configureEach {
|
||||||
when {
|
when {
|
||||||
@ -103,7 +111,7 @@ xomrk {
|
|||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
defineXomrkGiteaMavenRepo()
|
defineXomrkGiteaMavenRepo(user="LanguageUtilities")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 099c3fad269770649d5a67acd3cd07e378bc9f37
|
Subproject commit 6cfa6bddd04a041ae02ec8098c478762313c71de
|
||||||
@ -1,3 +1,3 @@
|
|||||||
rootProject.name = "reference-counter"
|
rootProject.name = "closeable-state-1"
|
||||||
|
|
||||||
includeBuild("./highlevel-try-finally")
|
includeBuild("./highlevel-try-finally")
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.jvm.JvmField
|
||||||
|
|
||||||
|
|
||||||
|
public sealed class ChildCloseableState<T : CloseableState> : CloseableState {
|
||||||
|
@JvmField
|
||||||
|
protected val _parent: CloseableState
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
protected val _state: T
|
||||||
|
|
||||||
|
protected constructor(parent: CloseableState, state: T) {
|
||||||
|
this._parent = parent
|
||||||
|
this._state = state
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun assertNotClosed(): Unit =
|
||||||
|
this._state.assertNotClosed()
|
||||||
|
|
||||||
|
override val isClosed: Boolean
|
||||||
|
get() = this._state.isClosed
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun startUsage(): Unit =
|
||||||
|
this._state.startUsage()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun startUsageIfNotClosed(): Boolean =
|
||||||
|
this._state.startUsageIfNotClosed()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun finishUsage(): Unit =
|
||||||
|
this._state.finishUsage()
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun close() {
|
||||||
|
this._state.finishUsage()
|
||||||
|
this._parent.finishUsage()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract override fun toString(): String
|
||||||
|
|
||||||
|
public class AllowsConcurrency : ChildCloseableState<CloseableState.AllowsConcurrency>, CloseableState.AllowsConcurrency {
|
||||||
|
public constructor(parent: CloseableState, state: CloseableState.AllowsConcurrency) : super(parent, state)
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
when {
|
||||||
|
this._state is OwnedUsagesCounter || this._state::class === UsagesCounter::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})>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExternallySynchronized : ChildCloseableState<CloseableState.ExternallySynchronized>, CloseableState.ExternallySynchronized {
|
||||||
|
public constructor(parent: CloseableState, state: CloseableState.ExternallySynchronized) : super(parent, state)
|
||||||
|
|
||||||
|
override val isInUse: Boolean
|
||||||
|
get() = this._state.isInUse
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun finishUsage(close: Boolean) {
|
||||||
|
this._state.finishUsage(close)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
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})>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
public interface CloseableState {
|
||||||
|
public val isClosed: Boolean
|
||||||
|
|
||||||
|
public fun assertNotClosed()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
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
|
||||||
|
public fun finishUsage()
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
public fun close()
|
||||||
|
|
||||||
|
public interface AllowsConcurrency : CloseableState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ExternallySynchronized : CloseableState {
|
||||||
|
public val isInUse: Boolean
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public fun finishUsage(close: Boolean)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
public expect class CloseableStateCloseableWrapper : CloseableState.ExternallySynchronized {
|
||||||
|
|
||||||
|
public constructor(parent: CloseableState.ExternallySynchronized, self: CloseableState.ExternallySynchronized)
|
||||||
|
|
||||||
|
override val isInUse: Boolean
|
||||||
|
|
||||||
|
override val isClosed: Boolean
|
||||||
|
|
||||||
|
override fun assertNotClosed()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun startUsage()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun startUsageIfNotClosed(): Boolean
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun finishUsage()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun finishUsage(close: Boolean)
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
override fun close()
|
||||||
|
|
||||||
|
override fun toString(): String
|
||||||
|
}
|
||||||
@ -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()
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
public expect open class ErrorOnConcurrentAccessState : CloseableState.ExternallySynchronized {
|
||||||
|
public constructor()
|
||||||
|
|
||||||
|
public final override val isInUse: Boolean
|
||||||
|
|
||||||
|
protected open fun throwClosed(): Nothing
|
||||||
|
|
||||||
|
protected open fun throwInUse(): Nothing
|
||||||
|
|
||||||
|
protected open fun throwConcurrent(): Nothing
|
||||||
|
|
||||||
|
public final override fun assertNotClosed()
|
||||||
|
|
||||||
|
public final override val isClosed: Boolean
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun startUsage()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun startUsageIfNotClosed(): Boolean
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun finishUsage()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun finishUsage(close: Boolean)
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun close()
|
||||||
|
|
||||||
|
@Suppress("RedundantModalityModifier")
|
||||||
|
public open override fun toString(): String
|
||||||
|
}
|
||||||
@ -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}>"
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
@RequiresOptIn(
|
||||||
|
message = "Manual state control is unsafe, are you sure to do this",
|
||||||
|
level = RequiresOptIn.Level.ERROR
|
||||||
|
)
|
||||||
|
public annotation class ManualStateManipulation
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
@file:OptIn(ExperimentalContracts::class)
|
||||||
|
|
||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
|
||||||
|
public class OwnedErrorOnConcurrentAccessState : ErrorOnConcurrentAccessState {
|
||||||
|
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 throwInUse(): Nothing {
|
||||||
|
throw IllegalStateException("Failed close object because it is in use: ${this._owner}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun throwConcurrent(): Nothing {
|
||||||
|
throw IllegalStateException("Object is in use by another thread: ${this._owner}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
val base = super.toString()
|
||||||
|
return base.substring(0, base.length - 1) + " of ${this._owner}>"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
@file:OptIn(ExperimentalContracts::class)
|
||||||
|
|
||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
|
||||||
|
public class OwnedUsagesCounter : UsagesCounter {
|
||||||
|
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 throwInUse(): Nothing {
|
||||||
|
throw IllegalStateException("Failed close object because it is in use: ${this._owner}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
val base = super.toString()
|
||||||
|
return base.substring(0, base.length - 1) + " of ${this._owner}>"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
@file:OptIn(ExperimentalContracts::class)
|
||||||
|
|
||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
|
||||||
|
public expect open class UsagesCounter : CloseableState.AllowsConcurrency {
|
||||||
|
public constructor()
|
||||||
|
|
||||||
|
protected open fun throwClosed(): Nothing
|
||||||
|
|
||||||
|
protected open fun throwInUse(): Nothing
|
||||||
|
|
||||||
|
|
||||||
|
public final override val isClosed: Boolean
|
||||||
|
|
||||||
|
public final override fun assertNotClosed()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun startUsage()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun startUsageIfNotClosed(): Boolean
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun finishUsage()
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
public final override fun close()
|
||||||
|
|
||||||
|
@Suppress("RedundantModalityModifier")
|
||||||
|
public open override fun toString(): String
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
internal object _MiscMultiplatform {
|
||||||
|
internal const val CLOSED_STATE_VALUE = -0x4000_0000_0000_0000L
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
@file:JvmName("ChildrenScopesKt")
|
||||||
|
|
||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
public inline fun <R> CloseableState.childAC(
|
||||||
|
constructor: () -> CloseableState.AllowsConcurrency = ::UsagesCounter,
|
||||||
|
scope: (CloseableState.AllowsConcurrency) -> R,
|
||||||
|
): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
|
||||||
|
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
val childState = this.tryStartUsage { ChildCloseableState.AllowsConcurrency(this, constructor()) }
|
||||||
|
return safeAutoClose1(
|
||||||
|
action = { scope(childState) },
|
||||||
|
finally = childState::close
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
public inline fun <R> CloseableState.childES(
|
||||||
|
constructor: () -> CloseableState.ExternallySynchronized = ::ErrorOnConcurrentAccessState,
|
||||||
|
scope: (CloseableState.ExternallySynchronized) -> R,
|
||||||
|
): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
|
||||||
|
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
val childState = this.tryStartUsage { ChildCloseableState.ExternallySynchronized(this, constructor()) }
|
||||||
|
return safeAutoClose1(
|
||||||
|
action = { scope(childState) },
|
||||||
|
finally = childState::close
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
public inline fun <R> CloseableState.ExternallySynchronized.closeableWrapper(
|
||||||
|
crossinline constructor: () -> CloseableState.ExternallySynchronized = ::ErrorOnConcurrentAccessState,
|
||||||
|
noinline scope: (CloseableState.ExternallySynchronized) -> R,
|
||||||
|
): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
|
||||||
|
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
return autoClosed(
|
||||||
|
constructor = { CloseableStateCloseableWrapper(this, constructor()) },
|
||||||
|
scope = scope
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
@file:JvmName("UsageScopesKt")
|
||||||
|
|
||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
@JvmName("autoClosed\$kt")
|
||||||
|
public 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)
|
||||||
|
}
|
||||||
|
this.startUsage()
|
||||||
|
return safeAutoClose1(action = block, finally = { this.finishUsage() })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
@JvmName("tryStartUsage\$kt")
|
||||||
|
public inline fun <R> CloseableState.tryStartUsage(block: () -> R): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
this.startUsage()
|
||||||
|
return safeAutoClose2(action = block, onError ={ this.finishUsage() })
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
@JvmName("tryFinishUsage\$kt")
|
||||||
|
public inline fun <R> CloseableState.tryFinishUsage(block: () -> R): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
var needClose = false
|
||||||
|
return safeAutoClose2(
|
||||||
|
action = { needClose = block() },
|
||||||
|
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() })
|
||||||
|
}
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
|
|
||||||
public class ChildReferenceCounter : MayBeClosedReferenceCounter {
|
|
||||||
private val _parent: MayBeClosedReferenceCounter
|
|
||||||
|
|
||||||
internal constructor(parent: MayBeClosedReferenceCounter, errMessageClosed: String) : super(errMessageClosed) {
|
|
||||||
this._parent = parent
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
|
||||||
@JvmName("close")
|
|
||||||
public override fun close(errExistRefs: String) {
|
|
||||||
this._parent.tryDecref {
|
|
||||||
super.close(errExistRefs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import kotlin.jvm.JvmStatic
|
|
||||||
|
|
||||||
public class CloseableReferenceCounter : MayBeClosedReferenceCounter {
|
|
||||||
public constructor(errMessageClosed: String) : super(errMessageClosed)
|
|
||||||
|
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
|
||||||
@JvmName("close")
|
|
||||||
public override fun close(errExistRefs: String) {
|
|
||||||
super.close(errExistRefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
|
||||||
@JvmStatic
|
|
||||||
@JvmName("autoClosed")
|
|
||||||
public fun <R> autoClosed(errMessageClosed: String, errExistRefs: String, scope: (MayBeClosedReferenceCounter) -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
_Platform.jvm_assertNotNull(errMessageClosed, "param: errMessageClosed")
|
|
||||||
_Platform.jvm_assertNotNull(errExistRefs, "param: errExistRefs")
|
|
||||||
_Platform.jvm_assertNotNull(scope, "param: scope")
|
|
||||||
|
|
||||||
val refcnt = CloseableReferenceCounter(errMessageClosed)
|
|
||||||
val ret = scope(refcnt)
|
|
||||||
refcnt.close(errExistRefs)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,164 +0,0 @@
|
|||||||
@file:OptIn(ExperimentalContracts::class)
|
|
||||||
|
|
||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
|
||||||
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
|
|
||||||
|
|
||||||
@CloseableReferenceCounter_Debug.RequiresExplicitDebug
|
|
||||||
public class CloseableReferenceCounter_Debug {
|
|
||||||
public fun interface Observer {
|
|
||||||
public fun observeState(instance: CloseableReferenceCounter_Debug, actions: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
|
||||||
public annotation class RequiresExplicitDebug
|
|
||||||
|
|
||||||
private val _dbgName: String
|
|
||||||
private val _errMessage: String
|
|
||||||
private val _logger: Observer?
|
|
||||||
private val _value: _AtomicLong
|
|
||||||
private val _id: Long
|
|
||||||
|
|
||||||
public constructor(dbgName: String, errMessage: String, logger: Observer? = null) {
|
|
||||||
_Platform.jvm_assertNotNull(dbgName, "param: dbgName")
|
|
||||||
_Platform.jvm_assertNotNull(errMessage, "param: errMessage")
|
|
||||||
|
|
||||||
this._dbgName = dbgName
|
|
||||||
this._errMessage = dbgName
|
|
||||||
this._logger = logger
|
|
||||||
this._value = _AtomicLong(0L)
|
|
||||||
@Suppress("RemoveRedundantQualifierName")
|
|
||||||
this._id = CloseableReferenceCounter_Debug._nextId.getAndUpdate(Long::inc)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline fun _throwErrors(valueToCheck: Long) {
|
|
||||||
when {
|
|
||||||
valueToCheck >= 0 || valueToCheck == _Misc.CLOSED_STATE_VALUE -> {}
|
|
||||||
valueToCheck < _Misc.CLOSED_STATE_VALUE -> throw RuntimeException("Too many references")
|
|
||||||
valueToCheck > _Misc.CLOSED_STATE_VALUE -> throw RuntimeException(".decref called more times than .incref")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresExplicitDebug
|
|
||||||
@JvmName("throwErrors")
|
|
||||||
public fun throwErrors() {
|
|
||||||
this._throwErrors(this._value.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("throwClosed")
|
|
||||||
public fun throwClosed() {
|
|
||||||
throw IllegalStateException(this._errMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("incref")
|
|
||||||
public fun incref() {
|
|
||||||
this._value.update { o ->
|
|
||||||
if (o < 0) {
|
|
||||||
this._throwErrors(o)
|
|
||||||
this.throwClosed()
|
|
||||||
this._throwErrors(o + 1)
|
|
||||||
}
|
|
||||||
return@update o + 1
|
|
||||||
}
|
|
||||||
this._logger?.observeState(this, "incref")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("tryIncref\$kt")
|
|
||||||
public inline fun <R> tryIncref(block: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
this.incref()
|
|
||||||
return safeAutoClose2(onError = this::decref, action = block)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("assertNotClosed")
|
|
||||||
public fun assertNotClosed() {
|
|
||||||
if (this._value.get() < 0) this.throwClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("decref")
|
|
||||||
public fun decref() {
|
|
||||||
this._value.update { o ->
|
|
||||||
if (o < 0) {
|
|
||||||
this._throwErrors(o)
|
|
||||||
this.throwClosed()
|
|
||||||
this._throwErrors(o - 1)
|
|
||||||
}
|
|
||||||
return@update o - 1
|
|
||||||
}
|
|
||||||
this._logger?.observeState(this, "decref")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("tryDecref\$kt")
|
|
||||||
public inline fun <R> tryDecref(block: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
this.assertNotClosed()
|
|
||||||
return safeAutoClose2(onSuccess = this::decref, action = block)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:JvmName("isClosed")
|
|
||||||
public val isClosed: Boolean
|
|
||||||
get() {
|
|
||||||
val state = this._value.get()
|
|
||||||
this._throwErrors(state)
|
|
||||||
return state < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("close")
|
|
||||||
public fun close(errExistRefs: String) {
|
|
||||||
_Platform.jvm_assertNotNull(errExistRefs, "param: errExistRefs")
|
|
||||||
val state = this._value.compareAndExchange(0, _Misc.CLOSED_STATE_VALUE)
|
|
||||||
this._throwErrors(state)
|
|
||||||
when {
|
|
||||||
state > 0 -> throw IllegalStateException(errExistRefs)
|
|
||||||
state < 0 -> this.throwClosed()
|
|
||||||
}
|
|
||||||
this._logger?.observeState(this, "closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("withRef\$kt")
|
|
||||||
public inline fun <R> withRef(protected: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(protected, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
this.incref()
|
|
||||||
return safeAutoClose1(finally = this::decref, action = protected)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val refcntCached = this._value.get()
|
|
||||||
val stateRepr: String
|
|
||||||
@Suppress("LiftReturnOrAssignment")
|
|
||||||
when {
|
|
||||||
refcntCached >= 0 -> stateRepr = refcntCached.toString()
|
|
||||||
refcntCached == _Misc.CLOSED_STATE_VALUE -> stateRepr = "closed"
|
|
||||||
refcntCached < _Misc.CLOSED_STATE_VALUE -> stateRepr = "overflow"
|
|
||||||
refcntCached > _Misc.CLOSED_STATE_VALUE -> stateRepr = "underflow"
|
|
||||||
else -> throw Error("Unreachable")
|
|
||||||
}
|
|
||||||
return "<ref counter \"${this._dbgName}@${this._id}\" [${stateRepr}]>"
|
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
private val _nextId = _AtomicLong(0L)
|
|
||||||
}
|
|
||||||
|
|
||||||
public object ObserveToStdout : Observer {
|
|
||||||
override fun observeState(instance: CloseableReferenceCounter_Debug, actions: String) {
|
|
||||||
_Platform.jvm_assertNotNull(instance, "param: instance")
|
|
||||||
_Platform.jvm_assertNotNull(actions, "param: actions")
|
|
||||||
print("${instance} ${actions}\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,145 +0,0 @@
|
|||||||
@file:OptIn(ExperimentalContracts::class)
|
|
||||||
|
|
||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
|
||||||
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
|
|
||||||
|
|
||||||
public sealed class MayBeClosedReferenceCounter {
|
|
||||||
private val _value: _AtomicLong
|
|
||||||
private val _errMessageClosed: String
|
|
||||||
|
|
||||||
protected constructor(errMessageClosed: String) {
|
|
||||||
_Platform.jvm_assertNotNull(errMessageClosed, "param: errMessageClosed")
|
|
||||||
this._errMessageClosed = errMessageClosed
|
|
||||||
this._value = _AtomicLong(0L)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("throwClosed")
|
|
||||||
public fun throwClosed() {
|
|
||||||
throw IllegalStateException(this._errMessageClosed)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("incref")
|
|
||||||
public fun incref() {
|
|
||||||
this._value.update { o ->
|
|
||||||
if (o < 0) this.throwClosed()
|
|
||||||
return@update o + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("tryIncref\$kt")
|
|
||||||
public inline fun <R> tryIncref(block: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
this.incref()
|
|
||||||
return safeAutoClose2(onError = this::decref, action = block)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("assertNotClosed")
|
|
||||||
public fun assertNotClosed() {
|
|
||||||
if (this._value.get() < 0) this.throwClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("decref")
|
|
||||||
public fun decref() {
|
|
||||||
this._value.update(Long::dec)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("tryDecref\$kt")
|
|
||||||
public inline fun <R> tryDecref(block: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
this.assertNotClosed()
|
|
||||||
return safeAutoClose2(onSuccess = this::decref, action = block)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:JvmName("isClosed")
|
|
||||||
public val isClosed: Boolean get() = this._value.get() < 0
|
|
||||||
|
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
|
||||||
@JvmName("close")
|
|
||||||
protected open fun close(errExistRefs: String) {
|
|
||||||
_Platform.jvm_assertNotNull(errExistRefs, "param: errExistRefs")
|
|
||||||
val state = this._value.compareAndExchange(0, _Misc.CLOSED_STATE_VALUE)
|
|
||||||
when {
|
|
||||||
state > 0 -> throw IllegalStateException(errExistRefs)
|
|
||||||
state < 0 -> this.throwClosed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("withRef\$kt")
|
|
||||||
public inline fun <R> withRef(protected: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(protected, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
this.incref()
|
|
||||||
return safeAutoClose1(finally = this::decref, action = protected)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val refcntCached = this._value.get()
|
|
||||||
@Suppress("LiftReturnOrAssignment")
|
|
||||||
if (refcntCached < 0)
|
|
||||||
return "<ref counter [closed]>"
|
|
||||||
else
|
|
||||||
return "<ref counter [${refcntCached}]>"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
|
||||||
@JvmName("child")
|
|
||||||
public fun <R> child(errMessageClosed: String, errExistRefs: String, scope: (MayBeClosedReferenceCounter) -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
|
|
||||||
_Platform.jvm_assertNotNull(errMessageClosed, "param: errMessageClosed")
|
|
||||||
_Platform.jvm_assertNotNull(errExistRefs, "param: errExistRefs")
|
|
||||||
_Platform.jvm_assertNotNull(scope, "param: scope")
|
|
||||||
|
|
||||||
this.withRef {
|
|
||||||
return CloseableReferenceCounter.autoClosed(
|
|
||||||
errMessageClosed = errMessageClosed,
|
|
||||||
errExistRefs = errExistRefs,
|
|
||||||
scope = scope
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("newChild")
|
|
||||||
public fun newChild(errMessageClosed: String): ChildReferenceCounter {
|
|
||||||
_Platform.jvm_assertNotNull(errMessageClosed, "param: errMessageClosed")
|
|
||||||
|
|
||||||
this.tryIncref {
|
|
||||||
return ChildReferenceCounter(this, errMessageClosed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
|
||||||
@JvmName("child_inheritErrMessage")
|
|
||||||
public fun <R> child_inheritErrMessage(errExistRefs: String, scope: (MayBeClosedReferenceCounter) -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
|
|
||||||
_Platform.jvm_assertNotNull(errExistRefs, "param: errExistRefs")
|
|
||||||
_Platform.jvm_assertNotNull(scope, "param: scope")
|
|
||||||
|
|
||||||
return this.child(
|
|
||||||
errMessageClosed = this._errMessageClosed,
|
|
||||||
errExistRefs = errExistRefs,
|
|
||||||
scope = scope
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("newChild_inheritErrMessage")
|
|
||||||
public fun newChild_inheritErrMessage(): ChildReferenceCounter {
|
|
||||||
return this.newChild(this._errMessageClosed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
internal expect class _AtomicLong {
|
|
||||||
constructor(initial: Long)
|
|
||||||
|
|
||||||
fun get(): Long
|
|
||||||
|
|
||||||
inline fun update(operator: (Long) -> Long)
|
|
||||||
|
|
||||||
inline fun getAndUpdate(operator: (Long) -> Long): Long
|
|
||||||
|
|
||||||
fun compareAndSet(expected: Long, newValue: Long): Boolean
|
|
||||||
|
|
||||||
fun compareAndExchange(expected: Long, newValue: Long): Long
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.jvm.JvmStatic
|
|
||||||
|
|
||||||
@Suppress("ClassName")
|
|
||||||
internal object _Misc {
|
|
||||||
@JvmStatic
|
|
||||||
internal inline fun _compareAndExchange(get: () -> Long, cas: (Long, Long) -> Boolean, expected: Long, newValue: Long): Long {
|
|
||||||
while (true) {
|
|
||||||
val old = get()
|
|
||||||
if (old != expected) return old
|
|
||||||
if (cas(old, newValue)) return old
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal const val CLOSED_STATE_VALUE = -0x4000_0000_0000_0000L
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.jvm.JvmStatic
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal expect object _Platform {
|
|
||||||
@PublishedApi
|
|
||||||
internal inline fun jvm_assertNotNull(x: Any?, name: String)
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
package ru.landrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import kotlin.jvm.JvmStatic
|
|
||||||
import ru.landgrafhomyak.utility.reference_counter._Platform
|
|
||||||
import ru.landgrafhomyak.utility.reference_counter.CloseableReferenceCounter as moved_CloseableReferenceCounter
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
@Deprecated(
|
|
||||||
message = "Class moved to another package",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
public class CloseableReferenceCounter : MayBeClosedReferenceCounter {
|
|
||||||
public constructor(errMessageClosed: String) : super(moved_CloseableReferenceCounter(errMessageClosed))
|
|
||||||
|
|
||||||
@Suppress("INAPPLICABLE_JVM_NAME", "NON_FINAL_MEMBER_IN_FINAL_CLASS")
|
|
||||||
@JvmName("close")
|
|
||||||
public open fun close(errExistRefs: String) {
|
|
||||||
(this._moved as moved_CloseableReferenceCounter).close(errExistRefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
|
||||||
@JvmStatic
|
|
||||||
@JvmName("autoClosed")
|
|
||||||
public fun <R> autoClosed(errMessageClosed: String, errExistRefs: String, scope: (MayBeClosedReferenceCounter) -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
_Platform.jvm_assertNotNull(scope, "param: scope")
|
|
||||||
|
|
||||||
return moved_CloseableReferenceCounter.autoClosed(errMessageClosed, errExistRefs, WrapperReferenceCounter.wrapperLambda(scope))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
@file:OptIn(ExperimentalContracts::class)
|
|
||||||
|
|
||||||
package ru.landrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.jvm.JvmField
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import ru.landgrafhomyak.utility.reference_counter.CloseableReferenceCounter_Debug as moved_CloseableReferenceCounter_Debug
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
@Deprecated(
|
|
||||||
message = "Class moved to another package",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@OptIn(moved_CloseableReferenceCounter_Debug.RequiresExplicitDebug::class)
|
|
||||||
@CloseableReferenceCounter_Debug.RequiresExplicitDebug
|
|
||||||
public class CloseableReferenceCounter_Debug {
|
|
||||||
public fun interface Observer {
|
|
||||||
public fun observeState(instance: CloseableReferenceCounter_Debug, actions: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ObserverAdapter(private val old: Observer) : moved_CloseableReferenceCounter_Debug.Observer {
|
|
||||||
lateinit var wrapper: CloseableReferenceCounter_Debug
|
|
||||||
override fun observeState(instance: moved_CloseableReferenceCounter_Debug, actions: String) {
|
|
||||||
this.old.observeState(this.wrapper, actions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
|
||||||
public annotation class RequiresExplicitDebug
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
@JvmField
|
|
||||||
internal val _moved: moved_CloseableReferenceCounter_Debug
|
|
||||||
|
|
||||||
public constructor(dbgName: String, errMessage: String, logger: Observer? = null) {
|
|
||||||
val obs = logger?.let(::ObserverAdapter)
|
|
||||||
this._moved = moved_CloseableReferenceCounter_Debug(dbgName, errMessage, obs)
|
|
||||||
obs?.wrapper = this
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresExplicitDebug
|
|
||||||
@JvmName("throwErrors")
|
|
||||||
public fun throwErrors() {
|
|
||||||
this._moved.throwErrors()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("throwClosed")
|
|
||||||
public fun throwClosed() {
|
|
||||||
this._moved.throwClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("incref")
|
|
||||||
public fun incref() {
|
|
||||||
this._moved.incref()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("tryIncref\$kt")
|
|
||||||
public inline fun <R> tryIncref(block: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
return this._moved.tryIncref(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("assertNotClosed")
|
|
||||||
public fun assertNotClosed() {
|
|
||||||
this._moved.assertNotClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("decref")
|
|
||||||
public fun decref() {
|
|
||||||
this._moved.decref()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("tryDecref\$kt")
|
|
||||||
public inline fun <R> tryDecref(block: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
return this._moved.tryDecref(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:JvmName("isClosed")
|
|
||||||
public val isClosed: Boolean
|
|
||||||
get() = this._moved.isClosed
|
|
||||||
|
|
||||||
@JvmName("close")
|
|
||||||
public fun close(errExistRefs: String) {
|
|
||||||
this._moved.close(errExistRefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("withRef\$kt")
|
|
||||||
public inline fun <R> withRef(protected: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(protected, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
return this._moved.withRef(protected)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val orig = this._moved.toString()
|
|
||||||
return orig.substring(0, orig.length - 1) + " !deprecated class used!>"
|
|
||||||
}
|
|
||||||
|
|
||||||
public object ObserveToStdout : Observer {
|
|
||||||
override fun observeState(instance: CloseableReferenceCounter_Debug, actions: String) {
|
|
||||||
moved_CloseableReferenceCounter_Debug.ObserveToStdout.observeState(instance._moved, actions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
@file:OptIn(ExperimentalContracts::class)
|
|
||||||
|
|
||||||
package ru.landrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.jvm.JvmField
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import ru.landgrafhomyak.utility.reference_counter._Platform
|
|
||||||
import ru.landgrafhomyak.utility.reference_counter.MayBeClosedReferenceCounter as moved_MayBeClosedReferenceCounter
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
message = "Class moved to another package",
|
|
||||||
/*replaceWith = ReplaceWith(
|
|
||||||
imports = ["ru.landgrafhomyak.utility.reference_counter.MayBeClosedReferenceCounter"],
|
|
||||||
expression = "MayBeClosedReferenceCounter"
|
|
||||||
),*/
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
public sealed class MayBeClosedReferenceCounter {
|
|
||||||
@PublishedApi
|
|
||||||
@JvmField
|
|
||||||
internal val _moved: moved_MayBeClosedReferenceCounter
|
|
||||||
|
|
||||||
protected constructor(moved: moved_MayBeClosedReferenceCounter) {
|
|
||||||
this._moved = moved
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("throwClosed")
|
|
||||||
public fun throwClosed() {
|
|
||||||
this._moved.throwClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("incref")
|
|
||||||
public fun incref() {
|
|
||||||
this._moved.incref()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("tryIncref\$kt")
|
|
||||||
public inline fun <R> tryIncref(block: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
return this._moved.tryIncref(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("assertNotClosed")
|
|
||||||
public fun assertNotClosed() {
|
|
||||||
this._moved.assertNotClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("decref")
|
|
||||||
public fun decref() {
|
|
||||||
this._moved.decref()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("tryDecref\$kt")
|
|
||||||
public inline fun <R> tryDecref(block: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
return this._moved.tryDecref(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:JvmName("isClosed")
|
|
||||||
public val isClosed: Boolean get() =
|
|
||||||
this._moved.isClosed
|
|
||||||
|
|
||||||
@JvmName("withRef\$kt")
|
|
||||||
public inline fun <R> withRef(protected: () -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(protected, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
return this._moved.withRef(protected)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val orig = this._moved.toString()
|
|
||||||
return orig.substring(0, orig.length - 1) + " !deprecated class used!>"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND", "DEPRECATION_ERROR")
|
|
||||||
@JvmName("child")
|
|
||||||
public fun <R> child(errMessageClosed: String, errExistRefs: String, scope: (MayBeClosedReferenceCounter) -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
|
|
||||||
_Platform.jvm_assertNotNull(errMessageClosed, "param: errMessageClosed")
|
|
||||||
_Platform.jvm_assertNotNull(errExistRefs, "param: errExistRefs")
|
|
||||||
_Platform.jvm_assertNotNull(scope, "param: scope")
|
|
||||||
|
|
||||||
return this._moved.child(errMessageClosed, errExistRefs, WrapperReferenceCounter.wrapperLambda(scope))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND", "DEPRECATION_ERROR")
|
|
||||||
@JvmName("child_inheritErrMessage")
|
|
||||||
public fun <R> child_inheritErrMessage(errExistRefs: String, scope: (MayBeClosedReferenceCounter) -> R): R {
|
|
||||||
contract {
|
|
||||||
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
|
|
||||||
_Platform.jvm_assertNotNull(scope, "param: scope")
|
|
||||||
|
|
||||||
return this._moved.child_inheritErrMessage(errExistRefs, WrapperReferenceCounter.wrapperLambda(scope))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
package ru.landrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlin.jvm.JvmStatic
|
|
||||||
import ru.landgrafhomyak.utility.reference_counter.MayBeClosedReferenceCounter as moved_MayBeClosedReferenceCounter
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
@Deprecated(
|
|
||||||
message = "Utility class for deprecated refcounters",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
internal class WrapperReferenceCounter : MayBeClosedReferenceCounter {
|
|
||||||
constructor(refcnt: moved_MayBeClosedReferenceCounter) : super(refcnt)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun <R> wrapperLambda(old: (MayBeClosedReferenceCounter) -> R): (moved_MayBeClosedReferenceCounter) -> R {
|
|
||||||
return w@{ moved -> return@w old(WrapperReferenceCounter(moved)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
internal class ErrorOnConcurrentAccessStateTest: _CloseableStateExternallySynchronizedAbstractTestSet() {
|
||||||
|
override fun createState(): CloseableState = ErrorOnConcurrentAccessState()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test() {}
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
internal class TestThrowable: Throwable("If this throwable isn't caught then test is bad written") {
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
internal class UsagesCounterTest: _CloseableStateAllowsConcurrencyAbstractTestSet() {
|
||||||
|
override fun createState(): CloseableState = UsagesCounter()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test() {}
|
||||||
|
}
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
internal abstract class _CloseableStateAbstractTestSet {
|
||||||
|
abstract fun createState(): CloseableState
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInstantClose() {
|
||||||
|
this.createState().close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOneManual() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.startUsage()
|
||||||
|
s.finishUsage()
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOneAuto() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.withUse { }
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSubsequentManual() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.startUsage()
|
||||||
|
s.finishUsage()
|
||||||
|
s.startUsage()
|
||||||
|
s.finishUsage()
|
||||||
|
s.startUsage()
|
||||||
|
s.finishUsage()
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSubsequentAuto() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.withUse { }
|
||||||
|
s.withUse { }
|
||||||
|
s.withUse { }
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTryStartSuccess() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.tryStartUsage { }
|
||||||
|
s.finishUsage()
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTryStartFailed() {
|
||||||
|
val s = this.createState()
|
||||||
|
try {
|
||||||
|
s.tryStartUsage { throw TestThrowable() }
|
||||||
|
} catch (_: TestThrowable) {
|
||||||
|
}
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTryFinishSuccess() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.startUsage()
|
||||||
|
s.tryFinishUsage { }
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTryFinishFailed() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.startUsage()
|
||||||
|
try {
|
||||||
|
s.tryFinishUsage { throw TestThrowable() }
|
||||||
|
} catch (_: TestThrowable) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
s.close()
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
s.finishUsage()
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
internal abstract class _CloseableStateAllowsConcurrencyAbstractTestSet: _CloseableStateAbstractTestSet() {
|
||||||
|
abstract override fun createState(): CloseableState
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRecursionManual() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.startUsage()
|
||||||
|
s.startUsage()
|
||||||
|
s.finishUsage()
|
||||||
|
s.finishUsage()
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRecursionAuto() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.withUse { s.withUse { } }
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
internal abstract class _CloseableStateExternallySynchronizedAbstractTestSet : _CloseableStateAbstractTestSet() {
|
||||||
|
abstract override fun createState(): CloseableState
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRecursionManual() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.startUsage()
|
||||||
|
try {
|
||||||
|
s.startUsage()
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
}
|
||||||
|
s.finishUsage()
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRecursionAuto() {
|
||||||
|
val s = this.createState()
|
||||||
|
s.withUse {
|
||||||
|
try {
|
||||||
|
s.withUse {}
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class jCloseableStateCloseableWrapper
|
||||||
|
implements CloseableState.ExternallySynchronized {
|
||||||
|
private final CloseableState.ExternallySynchronized _parent;
|
||||||
|
private final CloseableState.ExternallySynchronized _self;
|
||||||
|
|
||||||
|
public jCloseableStateCloseableWrapper(CloseableState.ExternallySynchronized parent, CloseableState.ExternallySynchronized self) {
|
||||||
|
Objects.requireNonNull(parent, "param: parent");
|
||||||
|
Objects.requireNonNull(self, "param: self");
|
||||||
|
this._parent = parent;
|
||||||
|
this._self = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInUse() {
|
||||||
|
return this._self.isInUse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return this._self.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void assertNotClosed() {
|
||||||
|
this._self.assertNotClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
@Override
|
||||||
|
public void startUsage() {
|
||||||
|
this._self.startUsage();
|
||||||
|
try {
|
||||||
|
this._parent.startUsage();
|
||||||
|
} catch (Throwable e1) {
|
||||||
|
try {
|
||||||
|
this._self.finishUsage();
|
||||||
|
} catch (Throwable e2) {
|
||||||
|
e1.addSuppressed(e2);
|
||||||
|
}
|
||||||
|
throw e1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
public void finishUsage() {
|
||||||
|
this._parent.finishUsage();
|
||||||
|
this._self.finishUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
@Override
|
||||||
|
public void finishUsage(boolean close) {
|
||||||
|
this._parent.finishUsage();
|
||||||
|
this._self.finishUsage(close);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
this._self.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<closeable state " + this._parent + " wrapped with " + this._self + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
public /* open */ class jErrorOnConcurrentAccessState
|
||||||
|
extends jErrorOnConcurrentAccessState$Errors
|
||||||
|
implements CloseableState.ExternallySynchronized {
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
OPEN, IN_USE, CLOSED
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AtomicReference<State> _currentState;
|
||||||
|
|
||||||
|
public jErrorOnConcurrentAccessState() {
|
||||||
|
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() {
|
||||||
|
switch (this._currentState.compareAndExchange(State.OPEN, State.IN_USE)) {
|
||||||
|
case IN_USE:
|
||||||
|
this.throwConcurrent();
|
||||||
|
case CLOSED:
|
||||||
|
return true;
|
||||||
|
case OPEN:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
switch (this._currentState.compareAndExchange(State.OPEN, State.CLOSED)) {
|
||||||
|
case CLOSED:
|
||||||
|
this.throwClosed();
|
||||||
|
case IN_USE:
|
||||||
|
this.throwInUse();
|
||||||
|
case OPEN:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
switch (this._currentState.get()) {
|
||||||
|
case CLOSED:
|
||||||
|
return "<closeable error-on-concurrent-access state [free]>";
|
||||||
|
case IN_USE:
|
||||||
|
return "<closeable error-on-concurrent-access state [in use]>";
|
||||||
|
case OPEN:
|
||||||
|
return "<closeable error-on-concurrent-access state [closed]>";
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unreachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
public /* open */ class jUsagesCounter
|
||||||
|
extends jUsagesCounter$Errors
|
||||||
|
implements CloseableState.AllowsConcurrency {
|
||||||
|
private final AtomicLong _currentUsagesCount;
|
||||||
|
|
||||||
|
public jUsagesCounter() {
|
||||||
|
this._currentUsagesCount = new AtomicLong(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isClosed() {
|
||||||
|
return this._currentUsagesCount.get() < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void assertNotClosed() {
|
||||||
|
if (this._currentUsagesCount.get() < 0)
|
||||||
|
this.throwClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
@Override
|
||||||
|
public final void startUsage() {
|
||||||
|
if (this.startUsageIfNotClosed())
|
||||||
|
this.throwClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
@Override
|
||||||
|
public final boolean startUsageIfNotClosed() {
|
||||||
|
while (true) {
|
||||||
|
final long cur = this._currentUsagesCount.get();
|
||||||
|
if (cur < 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._currentUsagesCount.compareAndSet(cur, cur + 1))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
@Override
|
||||||
|
public final void finishUsage() {
|
||||||
|
while (true) {
|
||||||
|
final long cur = this._currentUsagesCount.get();
|
||||||
|
if (cur < 0) {
|
||||||
|
this.throwClosed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._currentUsagesCount.compareAndSet(cur, cur - 1))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
@Override
|
||||||
|
public final void close() {
|
||||||
|
long currentReferencesCount;
|
||||||
|
while (true) {
|
||||||
|
currentReferencesCount = this._currentUsagesCount.get();
|
||||||
|
if (currentReferencesCount != 0) break;
|
||||||
|
if (this._currentUsagesCount.compareAndSet(currentReferencesCount, _MiscMultiplatform.CLOSED_STATE_VALUE))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentReferencesCount > 0) this.throwInUse();
|
||||||
|
if (currentReferencesCount < 0) this.throwClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final long cached = this._currentUsagesCount.get();
|
||||||
|
if (cached < 0)
|
||||||
|
return "<closeable usages counter [closed]>";
|
||||||
|
else
|
||||||
|
return "<closeable usages counter [" + cached + "]>";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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}>"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
public actual typealias UsagesCounter = jUsagesCounter
|
||||||
|
public actual typealias ErrorOnConcurrentAccessState = jErrorOnConcurrentAccessState
|
||||||
|
public actual typealias CloseableStateCloseableWrapper = jCloseableStateCloseableWrapper
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
internal abstract class `jErrorOnConcurrentAccessState$Errors` : CloseableState.ExternallySynchronized {
|
||||||
|
protected open fun throwClosed(): Nothing {
|
||||||
|
throw IllegalStateException("Object is closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun throwInUse(): Nothing {
|
||||||
|
throw IllegalStateException("Failed close object because it is in use")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun throwConcurrent(): Nothing {
|
||||||
|
throw IllegalStateException("Object is in use by another thread")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
internal abstract class `jUsagesCounter$Errors` {
|
||||||
|
protected open fun throwClosed(): Nothing {
|
||||||
|
throw IllegalStateException("Object is closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun throwInUse(): Nothing {
|
||||||
|
throw IllegalStateException("Failed close object because it is in use")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,58 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
|
||||||
|
|
||||||
internal actual /*value*/ class _AtomicLong {
|
|
||||||
private val _native: AtomicLong
|
|
||||||
|
|
||||||
actual constructor(initial: Long) {
|
|
||||||
this._native = AtomicLong(initial)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun get() = this._native.get()
|
|
||||||
|
|
||||||
actual inline fun update(operator: (Long) -> Long) {
|
|
||||||
while (true) {
|
|
||||||
val cur = this._native.get()
|
|
||||||
val upd = operator(cur)
|
|
||||||
if (this._native.compareAndSet(cur, upd)) return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actual inline fun getAndUpdate(operator: (Long) -> Long): Long {
|
|
||||||
while (true) {
|
|
||||||
val cur = this._native.get()
|
|
||||||
val upd = operator(cur)
|
|
||||||
if (this._native.compareAndSet(cur, upd)) return cur
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun compareAndSet(expected: Long, newValue: Long): Boolean =
|
|
||||||
this._native.compareAndSet(expected, newValue)
|
|
||||||
|
|
||||||
actual fun compareAndExchange(expected: Long, newValue: Long): Long {
|
|
||||||
if (_Linkage._isNativeCompareAndExchangeExists) {
|
|
||||||
return this._native.compareAndExchange(expected, newValue)
|
|
||||||
} else {
|
|
||||||
val a = this._native
|
|
||||||
return _Misc._compareAndExchange(
|
|
||||||
get = a::get,
|
|
||||||
cas = a::compareAndSet,
|
|
||||||
expected = expected, newValue = newValue
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private object _Linkage {
|
|
||||||
@JvmField
|
|
||||||
val _isNativeCompareAndExchangeExists = run {
|
|
||||||
val a = AtomicLong()
|
|
||||||
try {
|
|
||||||
a.compareAndExchange(0, 0)
|
|
||||||
return@run true
|
|
||||||
} catch (_: NoSuchMethodError) {
|
|
||||||
return@run false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import java.util.Objects
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal actual object _Platform {
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
@JvmStatic
|
|
||||||
@PublishedApi
|
|
||||||
internal actual inline fun jvm_assertNotNull(x: Any?, name: String) {
|
|
||||||
contract {
|
|
||||||
returns().implies(x != null)
|
|
||||||
}
|
|
||||||
Objects.requireNonNull(x, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1.tests
|
||||||
|
|
||||||
|
import org.testng.annotations.Test
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.ManualStateManipulation
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.UsagesCounter
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.withUse
|
||||||
|
|
||||||
|
@Test(enabled = false)
|
||||||
|
class KotlinStdlibDependencyTest {
|
||||||
|
@Test
|
||||||
|
fun testNoKotlinStdlib() {
|
||||||
|
try {
|
||||||
|
if (KotlinVersion.CURRENT.major != -1)
|
||||||
|
throw AssertionError("Kotlin stdlib still in runtime classpath")
|
||||||
|
} catch (_: LinkageError) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomTestException : RuntimeException()
|
||||||
|
|
||||||
|
private object CustomUnit
|
||||||
|
|
||||||
|
private fun throw7throwFn() {
|
||||||
|
safeAutoClose1(finally = { throw CustomTestException() }, action = { throw CustomTestException() })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline fun throw7throwIn() {
|
||||||
|
safeAutoClose1(finally = { throw CustomTestException() }, action = { throw CustomTestException() })
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
@Test(dependsOnMethods = ["testNoKotlinStdlib"])
|
||||||
|
fun testIncrefDecrefClose() {
|
||||||
|
try {
|
||||||
|
val refcnt = UsagesCounter()
|
||||||
|
refcnt.startUsage()
|
||||||
|
refcnt.finishUsage()
|
||||||
|
refcnt.startUsage()
|
||||||
|
refcnt.startUsage()
|
||||||
|
refcnt.finishUsage()
|
||||||
|
refcnt.finishUsage()
|
||||||
|
refcnt.close()
|
||||||
|
} catch (le: LinkageError) {
|
||||||
|
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
@Test(dependsOnMethods = ["testNoKotlinStdlib", "testIncrefDecrefClose"])
|
||||||
|
fun testWithRef() {
|
||||||
|
try {
|
||||||
|
val refcnt = UsagesCounter()
|
||||||
|
refcnt.withUse {
|
||||||
|
return@withUse CustomUnit
|
||||||
|
}
|
||||||
|
refcnt.withUse w1@{
|
||||||
|
return@w1 refcnt.withUse w2@{
|
||||||
|
return@w2 CustomUnit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refcnt.close()
|
||||||
|
} catch (le: LinkageError) {
|
||||||
|
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dependsOnMethods = ["testNoKotlinStdlib", "testIncrefDecrefClose", "testWithRef"])
|
||||||
|
fun testWithRef_Err() {
|
||||||
|
try {
|
||||||
|
val refcnt = UsagesCounter()
|
||||||
|
refcnt.withUse {
|
||||||
|
throw CustomTestException()
|
||||||
|
}
|
||||||
|
} catch (_: CustomTestException) {
|
||||||
|
} catch (le: LinkageError) {
|
||||||
|
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,130 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter.tests
|
|
||||||
|
|
||||||
import java.lang.AssertionError
|
|
||||||
import org.testng.annotations.Test
|
|
||||||
import org.testng.asserts.Assertion
|
|
||||||
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
|
||||||
import ru.landgrafhomyak.utility.reference_counter.CloseableReferenceCounter
|
|
||||||
import ru.landgrafhomyak.utility.reference_counter.CloseableReferenceCounter_Debug
|
|
||||||
|
|
||||||
@Test
|
|
||||||
class KotlinStdlibDependencyTest {
|
|
||||||
@Test
|
|
||||||
fun testNoKotlinStdlib() {
|
|
||||||
try {
|
|
||||||
if (KotlinVersion.CURRENT.major != -1)
|
|
||||||
throw AssertionError("Kotlin stdlib still in runtime classpath")
|
|
||||||
} catch (_: LinkageError) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CustomTestException : RuntimeException()
|
|
||||||
|
|
||||||
private object CustomUnit
|
|
||||||
|
|
||||||
private fun throw7throwFn() {
|
|
||||||
safeAutoClose1(finally = { throw CustomTestException() }, action = { throw CustomTestException() })
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline fun throw7throwIn() {
|
|
||||||
safeAutoClose1(finally = { throw CustomTestException() }, action = { throw CustomTestException() })
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(dependsOnMethods = ["testNoKotlinStdlib"])
|
|
||||||
fun testIncrefDecrefClose() {
|
|
||||||
try {
|
|
||||||
val refcnt = CloseableReferenceCounter("")
|
|
||||||
refcnt.incref()
|
|
||||||
refcnt.decref()
|
|
||||||
refcnt.incref()
|
|
||||||
refcnt.incref()
|
|
||||||
refcnt.decref()
|
|
||||||
refcnt.decref()
|
|
||||||
refcnt.close("")
|
|
||||||
} catch (le: LinkageError) {
|
|
||||||
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(CloseableReferenceCounter_Debug.RequiresExplicitDebug::class)
|
|
||||||
@Test(dependsOnMethods = ["testNoKotlinStdlib"])
|
|
||||||
fun testIncrefDecrefClose_Debug() {
|
|
||||||
try {
|
|
||||||
val refcnt = CloseableReferenceCounter_Debug("", "")
|
|
||||||
refcnt.incref()
|
|
||||||
refcnt.decref()
|
|
||||||
refcnt.incref()
|
|
||||||
refcnt.incref()
|
|
||||||
refcnt.decref()
|
|
||||||
refcnt.decref()
|
|
||||||
refcnt.close("")
|
|
||||||
} catch (le: LinkageError) {
|
|
||||||
throw AssertionError("CloseableReferenceCounter_Debug still has dependency on kotlin stdlib", le)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(dependsOnMethods = ["testNoKotlinStdlib", "testIncrefDecrefClose"])
|
|
||||||
fun testWithRef() {
|
|
||||||
try {
|
|
||||||
val refcnt = CloseableReferenceCounter("")
|
|
||||||
refcnt.withRef {
|
|
||||||
return@withRef CustomUnit
|
|
||||||
}
|
|
||||||
refcnt.withRef w1@{
|
|
||||||
return@w1 refcnt.withRef w2@{
|
|
||||||
return@w2 CustomUnit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
refcnt.close("")
|
|
||||||
} catch (le: LinkageError) {
|
|
||||||
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(CloseableReferenceCounter_Debug.RequiresExplicitDebug::class)
|
|
||||||
@Test(dependsOnMethods = ["testNoKotlinStdlib", "testIncrefDecrefClose_Debug"])
|
|
||||||
fun testWithRef_Debug() {
|
|
||||||
try {
|
|
||||||
val refcnt = CloseableReferenceCounter_Debug("", "")
|
|
||||||
refcnt.withRef {
|
|
||||||
return@withRef CustomUnit
|
|
||||||
}
|
|
||||||
refcnt.withRef w1@{
|
|
||||||
return@w1 refcnt.withRef w2@{
|
|
||||||
return@w2 CustomUnit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
refcnt.close("")
|
|
||||||
} catch (le: LinkageError) {
|
|
||||||
throw AssertionError("CloseableReferenceCounter_Debug still has dependency on kotlin stdlib", le)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(dependsOnMethods = ["testNoKotlinStdlib", "testIncrefDecrefClose", "testWithRef"])
|
|
||||||
fun testWithRef_Err() {
|
|
||||||
try {
|
|
||||||
val refcnt = CloseableReferenceCounter("")
|
|
||||||
refcnt.withRef {
|
|
||||||
throw CustomTestException()
|
|
||||||
}
|
|
||||||
} catch (_: CustomTestException) {
|
|
||||||
} catch (le: LinkageError) {
|
|
||||||
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(CloseableReferenceCounter_Debug.RequiresExplicitDebug::class)
|
|
||||||
@Test(dependsOnMethods = ["testNoKotlinStdlib", "testIncrefDecrefClose_Debug", "testWithRef_Debug"])
|
|
||||||
fun testWithRef_ErrDebug() {
|
|
||||||
try {
|
|
||||||
val refcnt = CloseableReferenceCounter_Debug("", "")
|
|
||||||
refcnt.withRef {
|
|
||||||
throw CustomTestException()
|
|
||||||
}
|
|
||||||
} catch (_: CustomTestException) {
|
|
||||||
} catch (le: LinkageError) {
|
|
||||||
throw AssertionError("CloseableReferenceCounter_Debug still has dependency on kotlin stdlib", le)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
public actual class CloseableStateCloseableWrapper : CloseableState.ExternallySynchronized {
|
||||||
|
private val _parent: CloseableState.ExternallySynchronized
|
||||||
|
private val _self: CloseableState.ExternallySynchronized
|
||||||
|
|
||||||
|
public actual constructor(
|
||||||
|
parent: CloseableState.ExternallySynchronized,
|
||||||
|
self: CloseableState.ExternallySynchronized,
|
||||||
|
) {
|
||||||
|
this._parent = parent
|
||||||
|
this._self = self
|
||||||
|
}
|
||||||
|
|
||||||
|
actual override val isInUse: Boolean
|
||||||
|
get() = this._self.isInUse
|
||||||
|
|
||||||
|
actual override val isClosed: Boolean
|
||||||
|
get() = this._self.isClosed
|
||||||
|
|
||||||
|
actual override fun assertNotClosed(): Unit =
|
||||||
|
this._self.assertNotClosed()
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
actual override fun startUsage() {
|
||||||
|
this._self.startUsage()
|
||||||
|
try {
|
||||||
|
this._parent.startUsage()
|
||||||
|
} catch (e1: Throwable) {
|
||||||
|
try {
|
||||||
|
this._self.finishUsage()
|
||||||
|
} catch (e2: Throwable) {
|
||||||
|
e1.addSuppressed(e2)
|
||||||
|
}
|
||||||
|
throw e1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
actual override fun finishUsage() {
|
||||||
|
this._parent.finishUsage()
|
||||||
|
this._self.finishUsage()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
actual override fun finishUsage(close: Boolean) {
|
||||||
|
this._parent.finishUsage()
|
||||||
|
this._self.finishUsage(close)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
actual override fun close() {
|
||||||
|
this._self.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
actual override fun toString(): String = "<closeable state ${this._parent} wrapped with ${this._self}>"
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.atomic
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1._MiscNonJvm.compareAndExchange
|
||||||
|
|
||||||
|
public actual open class ErrorOnConcurrentAccessState : CloseableState.ExternallySynchronized {
|
||||||
|
private enum class State {
|
||||||
|
OPEN, IN_USE, CLOSED
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _state = atomic(State.OPEN)
|
||||||
|
|
||||||
|
public actual final override val isInUse: Boolean
|
||||||
|
get() = this._state.value === State.IN_USE
|
||||||
|
|
||||||
|
protected actual open fun throwClosed(): Nothing {
|
||||||
|
throw IllegalStateException("Object is closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected actual open fun throwInUse(): Nothing {
|
||||||
|
throw IllegalStateException("Failed close object because it is in use")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected actual open fun throwConcurrent(): Nothing {
|
||||||
|
throw IllegalStateException("Object is in use by another thread")
|
||||||
|
}
|
||||||
|
|
||||||
|
public actual final override fun assertNotClosed() {
|
||||||
|
if (this._state.value === State.CLOSED)
|
||||||
|
this.throwClosed()
|
||||||
|
}
|
||||||
|
|
||||||
|
public actual final override val isClosed: Boolean
|
||||||
|
get() = this._state.value === State.CLOSED
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
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)) {
|
||||||
|
State.OPEN -> return false;
|
||||||
|
State.IN_USE -> this.throwConcurrent()
|
||||||
|
State.CLOSED -> return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
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)
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
public actual final override fun close() {
|
||||||
|
when (this._state.compareAndExchange(State.OPEN, State.CLOSED)) {
|
||||||
|
State.OPEN -> {}
|
||||||
|
State.IN_USE -> this.throwInUse()
|
||||||
|
State.CLOSED -> this.throwClosed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("RedundantModalityModifier")
|
||||||
|
public actual open override fun toString(): String =
|
||||||
|
when (this._state.value) {
|
||||||
|
State.OPEN -> "<closeable error-on-concurrent-access state [free]>"
|
||||||
|
State.IN_USE -> "<closeable error-on-concurrent-access state [in use]>"
|
||||||
|
State.CLOSED -> "<closeable error-on-concurrent-access state [closed]>"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
@file:OptIn(ExperimentalContracts::class)
|
||||||
|
|
||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlinx.atomicfu.AtomicLong
|
||||||
|
import kotlinx.atomicfu.atomic
|
||||||
|
import kotlinx.atomicfu.update
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1._MiscNonJvm.compareAndExchange
|
||||||
|
|
||||||
|
public actual open class UsagesCounter : CloseableState.AllowsConcurrency {
|
||||||
|
private val _value: AtomicLong = atomic(0L)
|
||||||
|
|
||||||
|
public actual constructor()
|
||||||
|
|
||||||
|
public fun getCurrentUsagesCount(): Long = this._value.value
|
||||||
|
|
||||||
|
protected actual open fun throwClosed(): Nothing {
|
||||||
|
throw IllegalStateException("Object is closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected actual open fun throwInUse(): Nothing {
|
||||||
|
throw IllegalStateException("Failed close object because it is in use")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public actual final override val isClosed: Boolean get() = this._value.value < 0
|
||||||
|
|
||||||
|
public actual final override fun assertNotClosed() {
|
||||||
|
if (this._value.value < 0) this.throwClosed()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public actual final override fun startUsage() {
|
||||||
|
this._value.update { o ->
|
||||||
|
if (o < 0) this.throwClosed()
|
||||||
|
return@update o + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public actual final override fun startUsageIfNotClosed(): Boolean {
|
||||||
|
this._value.update { o ->
|
||||||
|
if (o < 0) return true
|
||||||
|
return@update o + 1
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManualStateManipulation
|
||||||
|
public actual final override fun finishUsage() {
|
||||||
|
this._value.update(Long::dec)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
@ManualStateManipulation
|
||||||
|
public actual final override fun close() {
|
||||||
|
val state = this._value.compareAndExchange(0, _MiscMultiplatform.CLOSED_STATE_VALUE)
|
||||||
|
when {
|
||||||
|
state > 0 -> this.throwInUse()
|
||||||
|
state < 0 -> this.throwClosed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("RedundantModalityModifier")
|
||||||
|
public actual open override fun toString(): String {
|
||||||
|
val cached = this._value.value
|
||||||
|
@Suppress("LiftReturnOrAssignment")
|
||||||
|
if (cached < 0)
|
||||||
|
return "<closeable usages counter [closed]>"
|
||||||
|
else
|
||||||
|
return "<closeable usages counter [${cached}]>"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package ru.landgrafhomyak.utility.closeable_state_1
|
||||||
|
|
||||||
|
import kotlin.jvm.JvmStatic
|
||||||
|
import kotlinx.atomicfu.AtomicLong
|
||||||
|
import kotlinx.atomicfu.AtomicRef
|
||||||
|
import kotlinx.atomicfu.getAndUpdate
|
||||||
|
|
||||||
|
internal object _MiscNonJvm {
|
||||||
|
@JvmStatic
|
||||||
|
internal fun AtomicLong.compareAndExchange(expected: Long, newValue: Long): Long {
|
||||||
|
return this.getAndUpdate { old ->
|
||||||
|
if (old != expected) return@compareAndExchange old
|
||||||
|
return@getAndUpdate newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
internal fun <T> AtomicRef<T>.compareAndExchange(expected: T, newValue: T): T {
|
||||||
|
return this.getAndUpdate { old ->
|
||||||
|
if (old != expected) return@compareAndExchange old
|
||||||
|
return@getAndUpdate newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,35 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
import kotlinx.atomicfu.AtomicLong
|
|
||||||
import kotlinx.atomicfu.atomic
|
|
||||||
import kotlinx.atomicfu.getAndUpdate
|
|
||||||
import kotlinx.atomicfu.update
|
|
||||||
|
|
||||||
internal actual class _AtomicLong {
|
|
||||||
val _atomicfu: AtomicLong
|
|
||||||
|
|
||||||
actual constructor(initial: Long) {
|
|
||||||
this._atomicfu = atomic(0L)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun get() =
|
|
||||||
this._atomicfu.value
|
|
||||||
|
|
||||||
actual inline fun update(operator: (Long) -> Long) =
|
|
||||||
this._atomicfu.update(operator)
|
|
||||||
|
|
||||||
actual inline fun getAndUpdate(operator: (Long) -> Long) =
|
|
||||||
this._atomicfu.getAndUpdate(operator)
|
|
||||||
|
|
||||||
actual fun compareAndSet(expected: Long, newValue: Long): Boolean =
|
|
||||||
this._atomicfu.compareAndSet(expected, newValue)
|
|
||||||
|
|
||||||
actual fun compareAndExchange(expected: Long, newValue: Long): Long {
|
|
||||||
val a = this._atomicfu
|
|
||||||
return _Misc._compareAndExchange(
|
|
||||||
get = a::value::get,
|
|
||||||
cas = a::compareAndSet,
|
|
||||||
expected = expected, newValue = newValue
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.reference_counter
|
|
||||||
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal actual object _Platform {
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
@PublishedApi
|
|
||||||
internal actual inline fun jvm_assertNotNull(x: Any?, name: String) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user