Compare commits

...

46 Commits
v0.1 ... master

Author SHA1 Message Date
5f6823cfb9 SpinLockSynchronizedState.close now synchronized too 2025-10-03 20:05:40 +03:00
6ea651ce6d SpinLockSynchronizedState 2025-10-03 19:49:01 +03:00
5e9e741b51 withUseIfNotClosed 2025-09-15 01:39:27 +03:00
8fc08d3e26 startUsageIfNotClosed 2025-09-15 01:37:12 +03:00
4ff6c0beb3 'Destructor' annotation 2025-09-15 00:48:10 +03:00
129cee26ca minor fix 2025-09-05 19:40:18 +03:00
e578517821 autoClosed not inline anymore 2025-09-05 18:32:11 +03:00
4b799c0ecf More access to HandleWrapper members because it anyway must be wrapped 2025-09-05 00:49:19 +03:00
bdb1fe56fa HandleWrapper 2025-09-04 21:19:18 +03:00
6bbade401f v1.1 2025-08-24 17:26:08 +03:00
7d56a44323 Fixes to remove kotlin-stdlib dependency 2025-08-24 17:15:55 +03:00
c078394921 Java implementation of CloseableWrapper 2025-08-24 16:48:00 +03:00
96931fda41 Close-on-exit feature 2025-08-24 04:18:54 +03:00
7131a38bd4 Removed excess atomic functions and minor fixes 2025-08-24 03:59:16 +03:00
999ff63ccf Replacement of AutoCloseable.use{} 2025-08-24 02:05:20 +03:00
826f286404 Improved 'owner' property in corresponding states 2025-08-24 01:48:20 +03:00
ad87bb7904 ErrorOnConcurrentAccessState expect class, java8 -> java9 and minor fixes 2025-08-24 01:36:49 +03:00
98d76569cc UsagesCounter expect class and ErrorOnConcurrentAccessState java implementation 2025-08-24 01:25:10 +03:00
c30c585f76 UsagesCounter implementation on java 2025-08-24 00:43:25 +03:00
ed06e6c2a0 Removed 'throw*' methods 2025-08-24 00:10:33 +03:00
05b9fc6078 Some missed 'final' modifiers 2025-08-23 23:47:13 +03:00
8f277853bd Fixed typo in ES impl and CloseableWrapper 2025-08-23 23:42:34 +03:00
131dd44d38 Add generation id to package and artifact name 2025-08-23 23:35:55 +03:00
953af111b4 v1.0 2025-08-22 14:40:13 +03:00
e8d899928e Missed expect functions for atomics and tests 2025-08-22 14:26:37 +03:00
860ffa98e0 Inlined atomics (no wrapper class anymore) 2025-08-22 05:35:40 +03:00
d4db1907d9 Unified all classes to common interface 2025-08-22 02:47:45 +03:00
0332e3eef7 ChildReferenceCounter 2025-08-16 20:04:33 +03:00
4b63563cbd Contract in CloseableReferenceCounter_Debug.withRef 2025-08-16 19:33:56 +03:00
3302c67fa6 Fixed wrong package name 2025-08-16 19:33:00 +03:00
5ac9133813 Method to create child refcounters 2025-08-14 03:09:44 +03:00
8bf13d0f69 Missed contract in MayBeClosedReferenceCounter.withRef 2025-08-13 15:58:11 +03:00
1a26d61cd2 Missed contract in CloseableReferenceCounter.autoClosed 2025-08-13 15:54:03 +03:00
c1592736da v0.3 2025-08-13 14:25:51 +03:00
140b463ce8 'close' method moved to separate class; autoClosed scope 2025-08-13 14:23:56 +03:00
d5ed25cb54 Avoiding double access to field in 'compareAndExchange' inline implementation 2025-03-26 19:01:54 +03:00
fe6d2c22ef Using native 'compareAndExchange' on java if possible 2025-03-26 18:39:48 +03:00
77a72a2a7c Removed dependency on 'kotlin-dom-api-compat' in js target 2025-03-26 02:12:40 +03:00
4747db72c5 v0.2 2025-03-26 02:06:19 +03:00
7dabea756f JvmName annotations for beautiful mangling and binary compatibility 2025-03-26 02:03:07 +03:00
e3d0aabd0f isClosed property 2025-03-26 01:57:41 +03:00
6581c14072 Tests to check the existence of kotlin-stdlib in runtime 2025-03-26 01:50:35 +03:00
9282313552 Removed dependency on kotlin's Intrinsics.checkNotNull 2025-03-26 00:33:53 +03:00
d1773fd8ec Removed atomicfu dependency from jvm target 2025-03-26 00:06:28 +03:00
647dcbb363 Stub to exclude kotlin-stdlib dependency from jvm target 2025-03-25 23:50:06 +03:00
5de503dcb3 Updating dependency on highlevel-try-finally 2025-03-25 23:45:23 +03:00
41 changed files with 1583 additions and 236 deletions

3
.gitignore vendored
View File

@ -6,4 +6,5 @@ build/
*.jar
/out/
/gradlew*
.kotlin/
.kotlin/
/kotlin-js-store

View File

@ -1,3 +1,4 @@
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
@ -14,11 +15,10 @@ buildscript {
}
group = "ru.landgrafhomyak.utility"
version = "0.1"
version = "1.4"
repositories {
mavenCentral()
maven("https://maven.landgrafhomyak.ru/")
}
xomrk {
@ -26,22 +26,92 @@ xomrk {
setCompatibilityWithKotlin(KotlinVersion.KOTLIN_2_0)
optInContracts()
explicitApi()
noWarnExpectActual()
warningsAsErrors()
defineAllMultiplatformTargets()
sourceSets {
commonMain {
dependencies {
implementation("org.jetbrains.kotlinx:atomicfu:0.27.0")
implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.4")
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 {
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")
}
}
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()
defineXomrkGiteaMavenRepo(user="LanguageUtilities")
}
}
}

View File

@ -1 +1,6 @@
kotlin.code.style=official
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

@ -1 +1 @@
Subproject commit 88c8da21e4ec11c9755b6770565685bdc29acbca
Subproject commit 6cfa6bddd04a041ae02ec8098c478762313c71de

View File

@ -1,3 +1,3 @@
rootProject.name = "reference-counter"
rootProject.name = "closeable-state-1"
includeBuild("./highlevel-try-finally")

View File

@ -0,0 +1,82 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.jvm.JvmField
public sealed class ChildCloseableState<T : CloseableState> : CloseableState {
@JvmField
protected val _parent: CloseableState
@JvmField
protected val _state: T
protected constructor(parent: CloseableState, state: T) {
this._parent = parent
this._state = state
}
override fun assertNotClosed(): Unit =
this._state.assertNotClosed()
override val isClosed: Boolean
get() = this._state.isClosed
@ManualStateManipulation
override fun startUsage(): Unit =
this._state.startUsage()
@ManualStateManipulation
override fun startUsageIfNotClosed(): Boolean =
this._state.startUsageIfNotClosed()
@ManualStateManipulation
override fun finishUsage(): Unit =
this._state.finishUsage()
@Destructor
@ManualStateManipulation
override fun close() {
this._state.finishUsage()
this._parent.finishUsage()
}
abstract override fun toString(): String
public class AllowsConcurrency : ChildCloseableState<CloseableState.AllowsConcurrency>, CloseableState.AllowsConcurrency {
public constructor(parent: CloseableState, state: CloseableState.AllowsConcurrency) : super(parent, state)
override fun toString(): String {
when {
this._state is OwnedUsagesCounter || this._state::class === UsagesCounter::class -> {
val base = this._state.toString()
return base.substring(0, base.length - 1) + " (child of ${this._parent})>"
}
else -> return "<child closeable state ${this._state} (parent if ${this._parent})>"
}
}
}
public class ExternallySynchronized : ChildCloseableState<CloseableState.ExternallySynchronized>, CloseableState.ExternallySynchronized {
public constructor(parent: CloseableState, state: CloseableState.ExternallySynchronized) : super(parent, state)
override val isInUse: Boolean
get() = this._state.isInUse
@ManualStateManipulation
override fun finishUsage(close: Boolean) {
this._state.finishUsage(close)
}
override fun toString(): String {
when {
this._state is OwnedErrorOnConcurrentAccessState || this._state::class === ErrorOnConcurrentAccessState::class -> {
val base = this._state.toString()
return base.substring(0, base.length - 1) + " (child of ${this._parent})>"
}
else -> return "<child closeable state ${this._state} (parent if ${this._parent})>"
}
}
}
}

View File

@ -0,0 +1,35 @@
package ru.landgrafhomyak.utility.closeable_state_1
public interface CloseableState {
public val isClosed: Boolean
public fun assertNotClosed()
@ManualStateManipulation
public fun startUsage()
/**
* Tries to start usage and returns `false` if entered or `true` if closed.
* Doesn't prevent from throwing error if this state forbids concurrent access.
*/
@ManualStateManipulation
public fun startUsageIfNotClosed(): Boolean
@ManualStateManipulation
public fun finishUsage()
@Destructor
@ManualStateManipulation
public fun close()
public interface AllowsConcurrency : CloseableState {
}
public interface ExternallySynchronized : CloseableState {
public val isInUse: Boolean
@ManualStateManipulation
public fun finishUsage(close: Boolean)
}
}

View File

@ -0,0 +1,30 @@
package ru.landgrafhomyak.utility.closeable_state_1
public expect class CloseableStateCloseableWrapper : CloseableState.ExternallySynchronized {
public constructor(parent: CloseableState.ExternallySynchronized, self: CloseableState.ExternallySynchronized)
override val isInUse: Boolean
override val isClosed: Boolean
override fun assertNotClosed()
@ManualStateManipulation
override fun startUsage()
@ManualStateManipulation
override fun startUsageIfNotClosed(): Boolean
@ManualStateManipulation
override fun finishUsage()
@ManualStateManipulation
override fun finishUsage(close: Boolean)
@Destructor
@ManualStateManipulation
override fun close()
override fun toString(): String
}

View File

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

View File

@ -0,0 +1,36 @@
package ru.landgrafhomyak.utility.closeable_state_1
public expect open class ErrorOnConcurrentAccessState : CloseableState.ExternallySynchronized {
public constructor()
public final override val isInUse: Boolean
protected open fun throwClosed(): Nothing
protected open fun throwInUse(): Nothing
protected open fun throwConcurrent(): Nothing
public final override fun assertNotClosed()
public final override val isClosed: Boolean
@ManualStateManipulation
public final override fun startUsage()
@ManualStateManipulation
public final override fun startUsageIfNotClosed(): Boolean
@ManualStateManipulation
public final override fun finishUsage()
@ManualStateManipulation
public final override fun finishUsage(close: Boolean)
@Destructor
@ManualStateManipulation
public final override fun close()
@Suppress("RedundantModalityModifier")
public open override fun toString(): String
}

View File

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

View File

@ -0,0 +1,7 @@
package ru.landgrafhomyak.utility.closeable_state_1
@RequiresOptIn(
message = "Manual state control is unsafe, are you sure to do this",
level = RequiresOptIn.Level.ERROR
)
public annotation class ManualStateManipulation

View File

@ -0,0 +1,41 @@
@file:OptIn(ExperimentalContracts::class)
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.contracts.ExperimentalContracts
public class OwnedErrorOnConcurrentAccessState : ErrorOnConcurrentAccessState {
private var _owner: Any?
public constructor() : super() {
this._owner = null
}
public constructor(owner: Any) : super() {
this._owner = owner
}
public var owner: Any
get() = this._owner ?: throw IllegalStateException("Owner not set yet")
set(value) {
if (this._owner != null) throw IllegalStateException("Owner already initialized")
this._owner = value
}
override fun throwClosed(): Nothing {
throw IllegalStateException("Object is closed: ${this._owner}")
}
override fun throwInUse(): Nothing {
throw IllegalStateException("Failed close object because it is in use: ${this._owner}")
}
override fun throwConcurrent(): Nothing {
throw IllegalStateException("Object is in use by another thread: ${this._owner}")
}
override fun toString(): String {
val base = super.toString()
return base.substring(0, base.length - 1) + " of ${this._owner}>"
}
}

View File

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

View File

@ -0,0 +1,34 @@
@file:OptIn(ExperimentalContracts::class)
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.contracts.ExperimentalContracts
public expect open class UsagesCounter : CloseableState.AllowsConcurrency {
public constructor()
protected open fun throwClosed(): Nothing
protected open fun throwInUse(): Nothing
public final override val isClosed: Boolean
public final override fun assertNotClosed()
@ManualStateManipulation
public final override fun startUsage()
@ManualStateManipulation
public final override fun startUsageIfNotClosed(): Boolean
@ManualStateManipulation
public final override fun finishUsage()
@Destructor
@ManualStateManipulation
public final override fun close()
@Suppress("RedundantModalityModifier")
public open override fun toString(): String
}

View File

@ -0,0 +1,5 @@
package ru.landgrafhomyak.utility.closeable_state_1
internal object _MiscMultiplatform {
internal const val CLOSED_STATE_VALUE = -0x4000_0000_0000_0000L
}

View File

@ -0,0 +1,55 @@
@file:JvmName("ChildrenScopesKt")
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmName
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
@OptIn(ManualStateManipulation::class)
public inline fun <R> CloseableState.childAC(
constructor: () -> CloseableState.AllowsConcurrency = ::UsagesCounter,
scope: (CloseableState.AllowsConcurrency) -> R,
): R {
contract {
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
}
val childState = this.tryStartUsage { ChildCloseableState.AllowsConcurrency(this, constructor()) }
return safeAutoClose1(
action = { scope(childState) },
finally = childState::close
)
}
@OptIn(ManualStateManipulation::class)
public inline fun <R> CloseableState.childES(
constructor: () -> CloseableState.ExternallySynchronized = ::ErrorOnConcurrentAccessState,
scope: (CloseableState.ExternallySynchronized) -> R,
): R {
contract {
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
}
val childState = this.tryStartUsage { ChildCloseableState.ExternallySynchronized(this, constructor()) }
return safeAutoClose1(
action = { scope(childState) },
finally = childState::close
)
}
@OptIn(ManualStateManipulation::class)
public inline fun <R> CloseableState.ExternallySynchronized.closeableWrapper(
crossinline constructor: () -> CloseableState.ExternallySynchronized = ::ErrorOnConcurrentAccessState,
noinline scope: (CloseableState.ExternallySynchronized) -> R,
): R {
contract {
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
}
return autoClosed(
constructor = { CloseableStateCloseableWrapper(this, constructor()) },
scope = scope
)
}

View File

@ -0,0 +1,117 @@
@file:JvmName("UsageScopesKt")
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmName
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
@OptIn(ManualStateManipulation::class)
@JvmName("autoClosed\$kt")
public fun <S : CloseableState, R> autoClosed(
constructor: () -> S,
scope: (S) -> R,
): R {
contract {
callsInPlace(constructor, InvocationKind.EXACTLY_ONCE)
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
}
val state = constructor()
return safeAutoClose1(
action = { scope(state) },
finally = state::close
)
}
@OptIn(ManualStateManipulation::class)
@JvmName("withUse\$kt")
public inline fun <R> CloseableState.withUse(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
this.startUsage()
return safeAutoClose1(action = block, finally = { this.finishUsage() })
}
@ManualStateManipulation
@JvmName("tryStartUsage\$kt")
public inline fun <R> CloseableState.tryStartUsage(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
this.startUsage()
return safeAutoClose2(action = block, onError ={ this.finishUsage() })
}
@ManualStateManipulation
@JvmName("tryFinishUsage\$kt")
public inline fun <R> CloseableState.tryFinishUsage(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return safeAutoClose2(action = block, onSuccess = this::finishUsage)
}
@OptIn(ManualStateManipulation::class)
@JvmName("withUseThenClose\$kt")
public inline fun CloseableState.ExternallySynchronized.withUseThenClose(block: () -> Boolean) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
this.startUsage()
var needClose = false
return safeAutoClose1(
action = { needClose = block() },
finally = { this.finishUsage(needClose) }
)
}
@ManualStateManipulation
@JvmName("tryFinishUsageThenClose\$kt")
public inline fun <R> CloseableState.ExternallySynchronized.tryFinishUsageThenClose(close: Boolean, block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return safeAutoClose2(action = block, onSuccess = { this.finishUsage(close) })
}
@ManualStateManipulation
@JvmName("tryFinishUsageThenClose\$kt")
public inline fun CloseableState.ExternallySynchronized.tryFinishUsageThenClose(block: () -> Boolean) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
var needClose = false
return safeAutoClose2(
action = { needClose = block() },
onSuccess = { this.finishUsage(needClose) }
)
}
@OptIn(ManualStateManipulation::class)
@JvmName("withUseIfNotClosed\$kt")
public inline fun <R> CloseableState.withUseIfNotClosed(ifClosed: R, block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (this.startUsageIfNotClosed())
return ifClosed
return safeAutoClose1(action = block, finally = { this.finishUsage() })
}
@OptIn(ManualStateManipulation::class)
@JvmName("withUseIfNotClosed\$kt")
public inline fun <R> CloseableState.withUseIfNotClosed(ifClosed: () -> R, block: () -> R): R {
contract {
callsInPlace(ifClosed, InvocationKind.AT_MOST_ONCE)
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (this.startUsageIfNotClosed())
return ifClosed()
return safeAutoClose1(action = block, finally = { this.finishUsage() })
}

View File

@ -1,73 +0,0 @@
@file:OptIn(ExperimentalContracts::class)
package ru.landrafhomyak.utility.reference_counter
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import 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}]>"
}
}

View File

@ -1,135 +0,0 @@
@file:OptIn(ExperimentalContracts::class)
package ru.landrafhomyak.utility.reference_counter
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import 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")
}
}
}

View File

@ -1,16 +0,0 @@
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
}

View File

@ -0,0 +1,10 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.test.Test
internal class ErrorOnConcurrentAccessStateTest: _CloseableStateExternallySynchronizedAbstractTestSet() {
override fun createState(): CloseableState = ErrorOnConcurrentAccessState()
@Test
fun test() {}
}

View File

@ -0,0 +1,4 @@
package ru.landgrafhomyak.utility.closeable_state_1
internal class TestThrowable: Throwable("If this throwable isn't caught then test is bad written") {
}

View File

@ -0,0 +1,10 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.test.Test
internal class UsagesCounterTest: _CloseableStateAllowsConcurrencyAbstractTestSet() {
override fun createState(): CloseableState = UsagesCounter()
@Test
fun test() {}
}

View File

@ -0,0 +1,91 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.test.Test
@OptIn(ManualStateManipulation::class)
internal abstract class _CloseableStateAbstractTestSet {
abstract fun createState(): CloseableState
@Test
fun testInstantClose() {
this.createState().close()
}
@Test
fun testOneManual() {
val s = this.createState()
s.startUsage()
s.finishUsage()
s.close()
}
@Test
fun testOneAuto() {
val s = this.createState()
s.withUse { }
s.close()
}
@Test
fun testSubsequentManual() {
val s = this.createState()
s.startUsage()
s.finishUsage()
s.startUsage()
s.finishUsage()
s.startUsage()
s.finishUsage()
s.close()
}
@Test
fun testSubsequentAuto() {
val s = this.createState()
s.withUse { }
s.withUse { }
s.withUse { }
s.close()
}
@Test
fun testTryStartSuccess() {
val s = this.createState()
s.tryStartUsage { }
s.finishUsage()
s.close()
}
@Test
fun testTryStartFailed() {
val s = this.createState()
try {
s.tryStartUsage { throw TestThrowable() }
} catch (_: TestThrowable) {
}
s.close()
}
@Test
fun testTryFinishSuccess() {
val s = this.createState()
s.startUsage()
s.tryFinishUsage { }
s.close()
}
@Test
fun testTryFinishFailed() {
val s = this.createState()
s.startUsage()
try {
s.tryFinishUsage { throw TestThrowable() }
} catch (_: TestThrowable) {
}
try {
s.close()
} catch (_: Throwable) {}
s.finishUsage()
s.close()
}
}

View File

@ -0,0 +1,25 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.test.Test
@OptIn(ManualStateManipulation::class)
internal abstract class _CloseableStateAllowsConcurrencyAbstractTestSet: _CloseableStateAbstractTestSet() {
abstract override fun createState(): CloseableState
@Test
fun testRecursionManual() {
val s = this.createState()
s.startUsage()
s.startUsage()
s.finishUsage()
s.finishUsage()
s.close()
}
@Test
fun testRecursionAuto() {
val s = this.createState()
s.withUse { s.withUse { } }
s.close()
}
}

View File

@ -0,0 +1,32 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.test.Test
@OptIn(ManualStateManipulation::class)
internal abstract class _CloseableStateExternallySynchronizedAbstractTestSet : _CloseableStateAbstractTestSet() {
abstract override fun createState(): CloseableState
@Test
fun testRecursionManual() {
val s = this.createState()
s.startUsage()
try {
s.startUsage()
} catch (_: Throwable) {
}
s.finishUsage()
s.close()
}
@Test
fun testRecursionAuto() {
val s = this.createState()
s.withUse {
try {
s.withUse {}
} catch (_: Throwable) {
}
}
s.close()
}
}

View File

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

View File

@ -0,0 +1,97 @@
package ru.landgrafhomyak.utility.closeable_state_1;
import java.util.Objects;
public final class jCloseableStateCloseableWrapper
implements CloseableState.ExternallySynchronized {
private final CloseableState.ExternallySynchronized _parent;
private final CloseableState.ExternallySynchronized _self;
public jCloseableStateCloseableWrapper(CloseableState.ExternallySynchronized parent, CloseableState.ExternallySynchronized self) {
Objects.requireNonNull(parent, "param: parent");
Objects.requireNonNull(self, "param: self");
this._parent = parent;
this._self = self;
}
@Override
public boolean isInUse() {
return this._self.isInUse();
}
@Override
public boolean isClosed() {
return this._self.isClosed();
}
@Override
public void assertNotClosed() {
this._self.assertNotClosed();
}
@ManualStateManipulation
@Override
public void startUsage() {
this._self.startUsage();
try {
this._parent.startUsage();
} catch (Throwable e1) {
try {
this._self.finishUsage();
} catch (Throwable e2) {
e1.addSuppressed(e2);
}
throw e1;
}
}
@ManualStateManipulation
@Override
public boolean startUsageIfNotClosed() {
if (this._self.startUsageIfNotClosed())
return true;
try {
if (this._parent.startUsageIfNotClosed()) {
this._self.finishUsage();
return true;
}
} catch (Throwable e1) {
try {
this._self.finishUsage();
} catch (Throwable e2) {
e1.addSuppressed(e2);
}
throw e1;
}
return false;
}
@ManualStateManipulation
@Override
public void finishUsage() {
this._parent.finishUsage();
this._self.finishUsage();
}
@ManualStateManipulation
@Override
public void finishUsage(boolean close) {
this._parent.finishUsage();
this._self.finishUsage(close);
}
@Destructor
@ManualStateManipulation
@Override
public void close() {
this._self.close();
}
@Override
public String toString() {
return "<closeable state " + this._parent + " wrapped with " + this._self + ">";
}
}

View File

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

View File

@ -0,0 +1,85 @@
package ru.landgrafhomyak.utility.closeable_state_1;
import java.util.concurrent.atomic.AtomicLong;
public /* open */ class jUsagesCounter
extends jUsagesCounter$Errors
implements CloseableState.AllowsConcurrency {
private final AtomicLong _currentUsagesCount;
public jUsagesCounter() {
this._currentUsagesCount = new AtomicLong(0);
}
@Override
public final boolean isClosed() {
return this._currentUsagesCount.get() < 0;
}
@Override
public final void assertNotClosed() {
if (this._currentUsagesCount.get() < 0)
this.throwClosed();
}
@ManualStateManipulation
@Override
public final void startUsage() {
if (this.startUsageIfNotClosed())
this.throwClosed();
}
@ManualStateManipulation
@Override
public final boolean startUsageIfNotClosed() {
while (true) {
final long cur = this._currentUsagesCount.get();
if (cur < 0) {
return true;
}
if (this._currentUsagesCount.compareAndSet(cur, cur + 1))
return false;
}
}
@ManualStateManipulation
@Override
public final void finishUsage() {
while (true) {
final long cur = this._currentUsagesCount.get();
if (cur < 0) {
this.throwClosed();
return;
}
if (this._currentUsagesCount.compareAndSet(cur, cur - 1))
return;
}
}
@Destructor
@ManualStateManipulation
@Override
public final void close() {
long currentReferencesCount;
while (true) {
currentReferencesCount = this._currentUsagesCount.get();
if (currentReferencesCount != 0) break;
if (this._currentUsagesCount.compareAndSet(currentReferencesCount, _MiscMultiplatform.CLOSED_STATE_VALUE))
break;
}
if (currentReferencesCount > 0) this.throwInUse();
if (currentReferencesCount < 0) this.throwClosed();
}
@Override
public String toString() {
final long cached = this._currentUsagesCount.get();
if (cached < 0)
return "<closeable usages counter [closed]>";
else
return "<closeable usages counter [" + cached + "]>";
}
}

View File

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

View File

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

View File

@ -0,0 +1,5 @@
package ru.landgrafhomyak.utility.closeable_state_1
public actual typealias UsagesCounter = jUsagesCounter
public actual typealias ErrorOnConcurrentAccessState = jErrorOnConcurrentAccessState
public actual typealias CloseableStateCloseableWrapper = jCloseableStateCloseableWrapper

View File

@ -0,0 +1,15 @@
package ru.landgrafhomyak.utility.closeable_state_1
internal abstract class `jErrorOnConcurrentAccessState$Errors` : CloseableState.ExternallySynchronized {
protected open fun throwClosed(): Nothing {
throw IllegalStateException("Object is closed")
}
protected open fun throwInUse(): Nothing {
throw IllegalStateException("Failed close object because it is in use")
}
protected open fun throwConcurrent(): Nothing {
throw IllegalStateException("Object is in use by another thread")
}
}

View File

@ -0,0 +1,11 @@
package ru.landgrafhomyak.utility.closeable_state_1
internal abstract class `jUsagesCounter$Errors` {
protected open fun throwClosed(): Nothing {
throw IllegalStateException("Object is closed")
}
protected open fun throwInUse(): Nothing {
throw IllegalStateException("Failed close object because it is in use")
}
}

View File

@ -0,0 +1,81 @@
package ru.landgrafhomyak.utility.closeable_state_1.tests
import org.testng.annotations.Test
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.closeable_state_1.ManualStateManipulation
import ru.landgrafhomyak.utility.closeable_state_1.UsagesCounter
import ru.landgrafhomyak.utility.closeable_state_1.withUse
@Test(enabled = false)
class KotlinStdlibDependencyTest {
@Test
fun testNoKotlinStdlib() {
try {
if (KotlinVersion.CURRENT.major != -1)
throw AssertionError("Kotlin stdlib still in runtime classpath")
} catch (_: LinkageError) {
}
}
private class CustomTestException : RuntimeException()
private object CustomUnit
private fun throw7throwFn() {
safeAutoClose1(finally = { throw CustomTestException() }, action = { throw CustomTestException() })
}
@Suppress("NOTHING_TO_INLINE")
private inline fun throw7throwIn() {
safeAutoClose1(finally = { throw CustomTestException() }, action = { throw CustomTestException() })
}
@OptIn(ManualStateManipulation::class)
@Test(dependsOnMethods = ["testNoKotlinStdlib"])
fun testIncrefDecrefClose() {
try {
val refcnt = UsagesCounter()
refcnt.startUsage()
refcnt.finishUsage()
refcnt.startUsage()
refcnt.startUsage()
refcnt.finishUsage()
refcnt.finishUsage()
refcnt.close()
} catch (le: LinkageError) {
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
}
}
@OptIn(ManualStateManipulation::class)
@Test(dependsOnMethods = ["testNoKotlinStdlib", "testIncrefDecrefClose"])
fun testWithRef() {
try {
val refcnt = UsagesCounter()
refcnt.withUse {
return@withUse CustomUnit
}
refcnt.withUse w1@{
return@w1 refcnt.withUse w2@{
return@w2 CustomUnit
}
}
refcnt.close()
} catch (le: LinkageError) {
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
}
}
@Test(dependsOnMethods = ["testNoKotlinStdlib", "testIncrefDecrefClose", "testWithRef"])
fun testWithRef_Err() {
try {
val refcnt = UsagesCounter()
refcnt.withUse {
throw CustomTestException()
}
} catch (_: CustomTestException) {
} catch (le: LinkageError) {
throw AssertionError("CloseableReferenceCounter still has dependency on kotlin stdlib", le)
}
}
}

View File

@ -0,0 +1,78 @@
package ru.landgrafhomyak.utility.closeable_state_1
public actual class CloseableStateCloseableWrapper : CloseableState.ExternallySynchronized {
private val _parent: CloseableState.ExternallySynchronized
private val _self: CloseableState.ExternallySynchronized
public actual constructor(
parent: CloseableState.ExternallySynchronized,
self: CloseableState.ExternallySynchronized,
) {
this._parent = parent
this._self = self
}
actual override val isInUse: Boolean
get() = this._self.isInUse
actual override val isClosed: Boolean
get() = this._self.isClosed
actual override fun assertNotClosed(): Unit =
this._self.assertNotClosed()
@ManualStateManipulation
actual override fun startUsage() {
this._self.startUsage()
try {
this._parent.startUsage()
} catch (e1: Throwable) {
try {
this._self.finishUsage()
} catch (e2: Throwable) {
e1.addSuppressed(e2)
}
throw e1
}
}
@ManualStateManipulation
actual override fun startUsageIfNotClosed(): Boolean {
if (this._self.startUsageIfNotClosed())
return true
try {
if (this._parent.startUsageIfNotClosed()) {
this._self.finishUsage()
return true
}
} catch (e1: Throwable) {
try {
this._self.finishUsage()
} catch (e2: Throwable) {
e1.addSuppressed(e2)
}
throw e1
}
return false;
}
@ManualStateManipulation
actual override fun finishUsage() {
this._parent.finishUsage()
this._self.finishUsage()
}
@ManualStateManipulation
actual override fun finishUsage(close: Boolean) {
this._parent.finishUsage()
this._self.finishUsage(close)
}
@Destructor
@ManualStateManipulation
actual override fun close() {
this._self.close()
}
actual override fun toString(): String = "<closeable state ${this._parent} wrapped with ${this._self}>"
}

View File

@ -0,0 +1,86 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlinx.atomicfu.atomic
import ru.landgrafhomyak.utility.closeable_state_1._MiscNonJvm.compareAndExchange
public actual open class ErrorOnConcurrentAccessState : CloseableState.ExternallySynchronized {
private enum class State {
OPEN, IN_USE, CLOSED
}
private val _state = atomic(State.OPEN)
public actual final override val isInUse: Boolean
get() = this._state.value === State.IN_USE
protected actual open fun throwClosed(): Nothing {
throw IllegalStateException("Object is closed")
}
protected actual open fun throwInUse(): Nothing {
throw IllegalStateException("Failed close object because it is in use")
}
protected actual open fun throwConcurrent(): Nothing {
throw IllegalStateException("Object is in use by another thread")
}
public actual final override fun assertNotClosed() {
if (this._state.value === State.CLOSED)
this.throwClosed()
}
public actual final override val isClosed: Boolean
get() = this._state.value === State.CLOSED
@ManualStateManipulation
public actual final override fun startUsage() {
if (this.startUsageIfNotClosed())
this.throwClosed()
}
@ManualStateManipulation
public actual final override fun startUsageIfNotClosed(): Boolean {
when (this._state.compareAndExchange(State.OPEN, State.IN_USE)) {
State.OPEN -> return false;
State.IN_USE -> this.throwConcurrent()
State.CLOSED -> return true
}
}
@ManualStateManipulation
private fun _finishUsage(nextState: State) {
when (this._state.compareAndExchange(State.IN_USE, nextState)) {
State.OPEN -> throw IllegalStateException("Can't finish usage because it isn't started")
State.IN_USE -> {}
State.CLOSED -> this.throwClosed()
}
}
@ManualStateManipulation
public actual final override fun finishUsage(): Unit =
this._finishUsage(State.OPEN)
@ManualStateManipulation
public actual final override fun finishUsage(close: Boolean): Unit =
this._finishUsage(if (close) State.CLOSED else State.OPEN)
@Destructor
@ManualStateManipulation
public actual final override fun close() {
when (this._state.compareAndExchange(State.OPEN, State.CLOSED)) {
State.OPEN -> {}
State.IN_USE -> this.throwInUse()
State.CLOSED -> this.throwClosed()
}
}
@Suppress("RedundantModalityModifier")
public actual open override fun toString(): String =
when (this._state.value) {
State.OPEN -> "<closeable error-on-concurrent-access state [free]>"
State.IN_USE -> "<closeable error-on-concurrent-access state [in use]>"
State.CLOSED -> "<closeable error-on-concurrent-access state [closed]>"
}
}

View File

@ -0,0 +1,74 @@
@file:OptIn(ExperimentalContracts::class)
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.contracts.ExperimentalContracts
import kotlinx.atomicfu.AtomicLong
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.update
import ru.landgrafhomyak.utility.closeable_state_1._MiscNonJvm.compareAndExchange
public actual open class UsagesCounter : CloseableState.AllowsConcurrency {
private val _value: AtomicLong = atomic(0L)
public actual constructor()
public fun getCurrentUsagesCount(): Long = this._value.value
protected actual open fun throwClosed(): Nothing {
throw IllegalStateException("Object is closed")
}
protected actual open fun throwInUse(): Nothing {
throw IllegalStateException("Failed close object because it is in use")
}
public actual final override val isClosed: Boolean get() = this._value.value < 0
public actual final override fun assertNotClosed() {
if (this._value.value < 0) this.throwClosed()
}
@ManualStateManipulation
public actual final override fun startUsage() {
this._value.update { o ->
if (o < 0) this.throwClosed()
return@update o + 1
}
}
@ManualStateManipulation
public actual final override fun startUsageIfNotClosed(): Boolean {
this._value.update { o ->
if (o < 0) return true
return@update o + 1
}
return false
}
@ManualStateManipulation
public actual final override fun finishUsage() {
this._value.update(Long::dec)
}
@Destructor
@ManualStateManipulation
public actual final override fun close() {
val state = this._value.compareAndExchange(0, _MiscMultiplatform.CLOSED_STATE_VALUE)
when {
state > 0 -> this.throwInUse()
state < 0 -> this.throwClosed()
}
}
@Suppress("RedundantModalityModifier")
public actual open override fun toString(): String {
val cached = this._value.value
@Suppress("LiftReturnOrAssignment")
if (cached < 0)
return "<closeable usages counter [closed]>"
else
return "<closeable usages counter [${cached}]>"
}
}

View File

@ -0,0 +1,24 @@
package ru.landgrafhomyak.utility.closeable_state_1
import kotlin.jvm.JvmStatic
import kotlinx.atomicfu.AtomicLong
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.getAndUpdate
internal object _MiscNonJvm {
@JvmStatic
internal fun AtomicLong.compareAndExchange(expected: Long, newValue: Long): Long {
return this.getAndUpdate { old ->
if (old != expected) return@compareAndExchange old
return@getAndUpdate newValue
}
}
@JvmStatic
internal fun <T> AtomicRef<T>.compareAndExchange(expected: T, newValue: T): T {
return this.getAndUpdate { old ->
if (old != expected) return@compareAndExchange old
return@getAndUpdate newValue
}
}
}