Compare commits
No commits in common. "master" and "v0.1" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,4 +7,3 @@ build/
|
|||||||
/out/
|
/out/
|
||||||
/gradlew*
|
/gradlew*
|
||||||
.kotlin/
|
.kotlin/
|
||||||
/kotlin-js-store
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.*
|
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.*
|
||||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.plugin.xomrk
|
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.plugin.xomrk
|
||||||
@ -15,10 +14,11 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "ru.landgrafhomyak.utility"
|
group = "ru.landgrafhomyak.utility"
|
||||||
version = "1.4"
|
version = "0.1"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven("https://maven.landgrafhomyak.ru/")
|
||||||
}
|
}
|
||||||
|
|
||||||
xomrk {
|
xomrk {
|
||||||
@ -26,92 +26,22 @@ xomrk {
|
|||||||
setCompatibilityWithKotlin(KotlinVersion.KOTLIN_2_0)
|
setCompatibilityWithKotlin(KotlinVersion.KOTLIN_2_0)
|
||||||
optInContracts()
|
optInContracts()
|
||||||
explicitApi()
|
explicitApi()
|
||||||
noWarnExpectActual()
|
|
||||||
warningsAsErrors()
|
|
||||||
|
|
||||||
defineAllMultiplatformTargets()
|
defineAllMultiplatformTargets()
|
||||||
|
|
||||||
jvmToolchain(11)
|
|
||||||
jvm {
|
|
||||||
withJava()
|
|
||||||
|
|
||||||
compilations.configureEach {
|
|
||||||
compileJavaTaskProvider?.configure {
|
|
||||||
targetCompatibility = "9"
|
|
||||||
sourceCompatibility = "9"
|
|
||||||
}
|
|
||||||
compileTaskProvider.configure {
|
|
||||||
compilerOptions {
|
|
||||||
jvmTarget = JvmTarget.JVM_9
|
|
||||||
freeCompilerArgs.addAll(
|
|
||||||
"-Xno-call-assertions",
|
|
||||||
"-Xno-param-assertions",
|
|
||||||
"-Xno-receiver-assertions"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named { t -> t == "${this@jvm.name}Test" }.configureEach {
|
|
||||||
this as Test
|
|
||||||
useTestNG()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
// if use kotlin("stdlib") gitea ui brokes at paragraph with dependency versions
|
commonMain {
|
||||||
val kotlinStdlibDependency = "org.jetbrains.kotlin:kotlin-stdlib:${this@kotlin.coreLibrariesVersion}"
|
|
||||||
|
|
||||||
val commonMain by getting {
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(kotlinStdlibDependency)
|
|
||||||
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 {
|
|
||||||
dependsOn(commonMain)
|
|
||||||
dependencies {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jvmTest {
|
|
||||||
dependencies {
|
|
||||||
implementation(kotlinStdlibDependency)
|
|
||||||
implementation("org.testng:testng:7.5.1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val nonJvmMain by creating {
|
|
||||||
dependsOn(commonMain)
|
|
||||||
dependencies {
|
|
||||||
implementation(kotlinStdlibDependency)
|
|
||||||
implementation("org.jetbrains.kotlinx:atomicfu:0.27.0")
|
implementation("org.jetbrains.kotlinx:atomicfu:0.27.0")
|
||||||
|
implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.4")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
configureEach {
|
|
||||||
when {
|
|
||||||
// commonMain !in dependsOn -> return@configureEach
|
|
||||||
!name.endsWith("Main") -> return@configureEach
|
|
||||||
this@configureEach === commonMain -> return@configureEach
|
|
||||||
this@configureEach === jvmMain -> return@configureEach
|
|
||||||
this@configureEach === nonJvmMain -> return@configureEach
|
|
||||||
}
|
|
||||||
dependsOn(nonJvmMain)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
defineXomrkGiteaMavenRepo(user="LanguageUtilities")
|
defineXomrkGiteaMavenRepo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1 @@
|
|||||||
kotlin.stdlib.default.dependency=false
|
kotlin.code.style=official
|
||||||
kotlin.mpp.applyDefaultHierarchyTemplate=false
|
|
||||||
kotlin.native.enableKlibsCrossCompilation=true
|
|
||||||
# compileOnly dependencies from commonMain still throw warning
|
|
||||||
kotlin.suppressGradlePluginWarnings=IncorrectCompileOnlyDependencyWarning
|
|
||||||
kotlin.js.stdlib.dom.api.included=false
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit 6cfa6bddd04a041ae02ec8098c478762313c71de
|
Subproject commit 88c8da21e4ec11c9755b6770565685bdc29acbca
|
||||||
@ -1,3 +1,3 @@
|
|||||||
rootProject.name = "closeable-state-1"
|
rootProject.name = "reference-counter"
|
||||||
|
|
||||||
includeBuild("./highlevel-try-finally")
|
includeBuild("./highlevel-try-finally")
|
||||||
@ -1,82 +0,0 @@
|
|||||||
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})>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
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()
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
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}>"
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
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
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
@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}>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
@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}>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
@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
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.closeable_state_1
|
|
||||||
|
|
||||||
internal object _MiscMultiplatform {
|
|
||||||
internal const val CLOSED_STATE_VALUE = -0x4000_0000_0000_0000L
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
@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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,117 +0,0 @@
|
|||||||
@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() })
|
|
||||||
}
|
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
@file:OptIn(ExperimentalContracts::class)
|
||||||
|
|
||||||
|
package ru.landrafhomyak.utility.reference_counter
|
||||||
|
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlinx.atomicfu.AtomicLong
|
||||||
|
import kotlinx.atomicfu.atomic
|
||||||
|
import kotlinx.atomicfu.update
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
|
||||||
|
|
||||||
|
public class CloseableReferenceCounter(private val _errMessage: String) {
|
||||||
|
private val _value: AtomicLong = atomic(0L)
|
||||||
|
|
||||||
|
public fun throwClosed() {
|
||||||
|
throw IllegalStateException(this._errMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun incref() {
|
||||||
|
this._value.update { o ->
|
||||||
|
if (o < 0) this.throwClosed()
|
||||||
|
return@update o + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <R> tryIncref(block: () -> R): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
this.incref()
|
||||||
|
return safeAutoClose2(onError = this::decref, action = block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun assertNotClosed() {
|
||||||
|
if (this._value.value < 0) this.throwClosed()
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun decref() {
|
||||||
|
this._value.update(Long::dec)
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <R> tryDecref(block: () -> R): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
this.assertNotClosed()
|
||||||
|
return safeAutoClose2(onSuccess = this::decref, action = block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun close(errExistRefs: String) {
|
||||||
|
val state = _CloseableReferenceCounter_LowLevel.compareAndExchange(this._value, 0, _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE)
|
||||||
|
when {
|
||||||
|
state > 0 -> throw IllegalStateException(errExistRefs)
|
||||||
|
state < 0 -> this.throwClosed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <R> withRef(protected: () -> R): R {
|
||||||
|
this.incref()
|
||||||
|
return safeAutoClose1(finally = this::decref, action = protected)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
val refcntCached = this._value.value
|
||||||
|
@Suppress("LiftReturnOrAssignment")
|
||||||
|
if (refcntCached < 0)
|
||||||
|
return "<ref counter [closed]>"
|
||||||
|
else
|
||||||
|
return "<ref counter [${refcntCached}]>"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
@file:OptIn(ExperimentalContracts::class)
|
||||||
|
|
||||||
|
package ru.landrafhomyak.utility.reference_counter
|
||||||
|
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlinx.atomicfu.AtomicLong
|
||||||
|
import kotlinx.atomicfu.atomic
|
||||||
|
import kotlinx.atomicfu.getAndUpdate
|
||||||
|
import kotlinx.atomicfu.update
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
|
||||||
|
|
||||||
|
@CloseableReferenceCounter_Debug.RequiresExplicitDebug
|
||||||
|
public class CloseableReferenceCounter_Debug(
|
||||||
|
private val _dbgName: String,
|
||||||
|
private val _errMessage: String,
|
||||||
|
private val _logger: Observer? = null,
|
||||||
|
) {
|
||||||
|
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 _value: AtomicLong = atomic(0L)
|
||||||
|
|
||||||
|
@Suppress("RemoveRedundantQualifierName")
|
||||||
|
private val _id = CloseableReferenceCounter_Debug._nextId.getAndUpdate(ULong::inc)
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline fun _throwErrors(valueToCheck: Long) {
|
||||||
|
when {
|
||||||
|
valueToCheck >= 0 || valueToCheck == _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE -> {}
|
||||||
|
valueToCheck < _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE -> throw RuntimeException("Too many references")
|
||||||
|
valueToCheck > _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE -> throw RuntimeException(".decref called more times than .incref")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresExplicitDebug
|
||||||
|
public fun throwErrors() {
|
||||||
|
this._throwErrors(this._value.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun throwClosed() {
|
||||||
|
throw IllegalStateException(this._errMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <R> tryIncref(block: () -> R): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
this.incref()
|
||||||
|
return safeAutoClose2(onError = this::decref, action = block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun assertNotClosed() {
|
||||||
|
if (this._value.value < 0) this.throwClosed()
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <R> tryDecref(block: () -> R): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
this.assertNotClosed()
|
||||||
|
return safeAutoClose2(onSuccess = this::decref, action = block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun close(errExistRefs: String) {
|
||||||
|
val state = _CloseableReferenceCounter_LowLevel.compareAndExchange(this._value, 0, _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE)
|
||||||
|
this._throwErrors(state)
|
||||||
|
when {
|
||||||
|
state > 0 -> throw IllegalStateException(errExistRefs)
|
||||||
|
state < 0 -> this.throwClosed()
|
||||||
|
}
|
||||||
|
this._logger?.observeState(this, "closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <R> withRef(protected: () -> R): R {
|
||||||
|
this.incref()
|
||||||
|
return safeAutoClose1(finally = this::decref, action = protected)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
val refcntCached = this._value.value
|
||||||
|
val stateRepr: String
|
||||||
|
@Suppress("LiftReturnOrAssignment")
|
||||||
|
when {
|
||||||
|
refcntCached >= 0 -> stateRepr = refcntCached.toString()
|
||||||
|
refcntCached == _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE -> stateRepr = "closed"
|
||||||
|
refcntCached < _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE -> stateRepr = "overflow"
|
||||||
|
refcntCached > _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE -> stateRepr = "underflow"
|
||||||
|
else -> throw Error("Unreachable")
|
||||||
|
}
|
||||||
|
return "<ref counter \"${this._dbgName}@${this._id}\" [${stateRepr}]>"
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
private val _nextId = atomic(0uL)
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ObserveToStdout : Observer {
|
||||||
|
override fun observeState(instance: CloseableReferenceCounter_Debug, actions: String) {
|
||||||
|
print("${instance} ${actions}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package ru.landrafhomyak.utility.reference_counter
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.AtomicLong
|
||||||
|
|
||||||
|
@Suppress("ClassName")
|
||||||
|
internal object _CloseableReferenceCounter_LowLevel {
|
||||||
|
internal fun compareAndExchange(atomic: AtomicLong, expected: Long, newValue: Long): Long {
|
||||||
|
while (true) {
|
||||||
|
val old = atomic.value
|
||||||
|
if (old != expected) return old
|
||||||
|
if (atomic.compareAndSet(old, newValue)) return old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal const val CLOSED_STATE_VALUE = -0x4000_0000_0000_0000L
|
||||||
|
}
|
||||||
@ -1,10 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.closeable_state_1
|
|
||||||
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
internal class ErrorOnConcurrentAccessStateTest: _CloseableStateExternallySynchronizedAbstractTestSet() {
|
|
||||||
override fun createState(): CloseableState = ErrorOnConcurrentAccessState()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun test() {}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.closeable_state_1
|
|
||||||
|
|
||||||
internal class TestThrowable: Throwable("If this throwable isn't caught then test is bad written") {
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.closeable_state_1
|
|
||||||
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
internal class UsagesCounterTest: _CloseableStateAllowsConcurrencyAbstractTestSet() {
|
|
||||||
override fun createState(): CloseableState = UsagesCounter()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun test() {}
|
|
||||||
}
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
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 + ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
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 + "]>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
@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}>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.closeable_state_1
|
|
||||||
|
|
||||||
internal abstract class `SpinLockSynchronizedState$Errors` : CloseableState.ExternallySynchronized {
|
|
||||||
protected open fun throwClosed(): Nothing {
|
|
||||||
throw IllegalStateException("Object is closed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
package ru.landgrafhomyak.utility.closeable_state_1
|
|
||||||
|
|
||||||
public actual typealias UsagesCounter = jUsagesCounter
|
|
||||||
public actual typealias ErrorOnConcurrentAccessState = jErrorOnConcurrentAccessState
|
|
||||||
public actual typealias CloseableStateCloseableWrapper = jCloseableStateCloseableWrapper
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
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,81 +0,0 @@
|
|||||||
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,78 +0,0 @@
|
|||||||
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}>"
|
|
||||||
}
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
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]>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
@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}]>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user