Compare commits
No commits in common. "master" and "v0.1" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,5 +6,4 @@ build/
|
||||
*.jar
|
||||
/out/
|
||||
/gradlew*
|
||||
.kotlin/
|
||||
/kotlin-js-store
|
||||
.kotlin/
|
||||
@ -1,4 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.*
|
||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.plugin.xomrk
|
||||
@ -15,10 +14,11 @@ buildscript {
|
||||
}
|
||||
|
||||
group = "ru.landgrafhomyak.utility"
|
||||
version = "1.4"
|
||||
version = "0.1"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://maven.landgrafhomyak.ru/")
|
||||
}
|
||||
|
||||
xomrk {
|
||||
@ -26,92 +26,22 @@ xomrk {
|
||||
setCompatibilityWithKotlin(KotlinVersion.KOTLIN_2_0)
|
||||
optInContracts()
|
||||
explicitApi()
|
||||
noWarnExpectActual()
|
||||
warningsAsErrors()
|
||||
|
||||
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 {
|
||||
// if use kotlin("stdlib") gitea ui brokes at paragraph with dependency versions
|
||||
val kotlinStdlibDependency = "org.jetbrains.kotlin:kotlin-stdlib:${this@kotlin.coreLibrariesVersion}"
|
||||
|
||||
val commonMain by getting {
|
||||
commonMain {
|
||||
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("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 {
|
||||
repositories {
|
||||
defineXomrkGiteaMavenRepo(user="LanguageUtilities")
|
||||
defineXomrkGiteaMavenRepo()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1 @@
|
||||
kotlin.stdlib.default.dependency=false
|
||||
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
|
||||
kotlin.code.style=official
|
||||
|
||||
@ -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")
|
||||
@ -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