From fe6d2c22ef0b5edfdddb5751098266ede2d1c659 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Wed, 26 Mar 2025 18:39:48 +0300 Subject: [PATCH] Using native 'compareAndExchange' on java if possible --- build.gradle.kts | 2 +- .../CloseableReferenceCounter.kt | 2 +- .../CloseableReferenceCounter_Debug.kt | 14 ++++++------- .../utility/reference_counter/_AtomicLong.kt | 4 +++- .../_CloseableReferenceCounter_LowLevel.kt | 14 ------------- .../utility/reference_counter/_Misc.kt | 17 +++++++++++++++ .../utility/reference_counter/_AtomicLong.kt | 21 +++++++++++++++++++ .../utility/reference_counter/_AtomicLong.kt | 7 +++++++ 8 files changed, 57 insertions(+), 24 deletions(-) delete mode 100644 src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_CloseableReferenceCounter_LowLevel.kt create mode 100644 src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_Misc.kt diff --git a/build.gradle.kts b/build.gradle.kts index 978997e..c2ed0c6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ xomrk { defineAllMultiplatformTargets() - jvmToolchain(8) + jvmToolchain(11) jvm { withJava() diff --git a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/CloseableReferenceCounter.kt b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/CloseableReferenceCounter.kt index 7698570..cd0bc3b 100644 --- a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/CloseableReferenceCounter.kt +++ b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/CloseableReferenceCounter.kt @@ -66,7 +66,7 @@ public class CloseableReferenceCounter { @JvmName("close") public fun close(errExistRefs: String) { _Platform.jvm_assertNotNull(errExistRefs, "param: errExistRefs") - val state = _CloseableReferenceCounter_LowLevel.compareAndExchange(this._value, 0, _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE) + val state = this._value.compareAndExchange(0, _Misc.CLOSED_STATE_VALUE) when { state > 0 -> throw IllegalStateException(errExistRefs) state < 0 -> this.throwClosed() diff --git a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/CloseableReferenceCounter_Debug.kt b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/CloseableReferenceCounter_Debug.kt index 581eb4a..2f0c97c 100644 --- a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/CloseableReferenceCounter_Debug.kt +++ b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/CloseableReferenceCounter_Debug.kt @@ -41,9 +41,9 @@ public class CloseableReferenceCounter_Debug { @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") + valueToCheck >= 0 || valueToCheck == _Misc.CLOSED_STATE_VALUE -> {} + valueToCheck < _Misc.CLOSED_STATE_VALUE -> throw RuntimeException("Too many references") + valueToCheck > _Misc.CLOSED_STATE_VALUE -> throw RuntimeException(".decref called more times than .incref") } } @@ -118,7 +118,7 @@ public class CloseableReferenceCounter_Debug { @JvmName("close") public fun close(errExistRefs: String) { _Platform.jvm_assertNotNull(errExistRefs, "param: errExistRefs") - val state = _CloseableReferenceCounter_LowLevel.compareAndExchange(this._value, 0, _CloseableReferenceCounter_LowLevel.CLOSED_STATE_VALUE) + val state = this._value.compareAndExchange(0, _Misc.CLOSED_STATE_VALUE) this._throwErrors(state) when { state > 0 -> throw IllegalStateException(errExistRefs) @@ -139,9 +139,9 @@ public class CloseableReferenceCounter_Debug { @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" + refcntCached == _Misc.CLOSED_STATE_VALUE -> stateRepr = "closed" + refcntCached < _Misc.CLOSED_STATE_VALUE -> stateRepr = "overflow" + refcntCached > _Misc.CLOSED_STATE_VALUE -> stateRepr = "underflow" else -> throw Error("Unreachable") } return "" diff --git a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt index c1e1ac1..3aa6174 100644 --- a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt +++ b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt @@ -7,7 +7,9 @@ internal expect class _AtomicLong { inline fun update(operator: (Long) -> Long) - inline fun getAndUpdate(operator: (Long) -> Long):Long + inline fun getAndUpdate(operator: (Long) -> Long): Long fun compareAndSet(expected: Long, newValue: Long): Boolean + + fun compareAndExchange(expected: Long, newValue: Long): Long } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_CloseableReferenceCounter_LowLevel.kt b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_CloseableReferenceCounter_LowLevel.kt deleted file mode 100644 index e069e25..0000000 --- a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_CloseableReferenceCounter_LowLevel.kt +++ /dev/null @@ -1,14 +0,0 @@ -package ru.landrafhomyak.utility.reference_counter - -@Suppress("ClassName") -internal object _CloseableReferenceCounter_LowLevel { - internal fun compareAndExchange(atomic: _AtomicLong, expected: Long, newValue: Long): Long { - while (true) { - val old = atomic.get() - if (old != expected) return old - if (atomic.compareAndSet(old, newValue)) return old - } - } - - internal const val CLOSED_STATE_VALUE = -0x4000_0000_0000_0000L -} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_Misc.kt b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_Misc.kt new file mode 100644 index 0000000..8729f77 --- /dev/null +++ b/src/commonMain/kotlin/ru/landrafhomyak/utility/reference_counter/_Misc.kt @@ -0,0 +1,17 @@ +package ru.landrafhomyak.utility.reference_counter + +import kotlin.jvm.JvmStatic + +@Suppress("ClassName") +internal object _Misc { + @JvmStatic + internal inline fun _compareAndExchange(get: () -> Long, cas: (Long, Long) -> Boolean, expected: Long, newValue: Long): Long { + while (true) { + val old = get() + if (old != expected) return old + if (cas(old, newValue)) return old + } + } + + internal const val CLOSED_STATE_VALUE = -0x4000_0000_0000_0000L +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt b/src/jvmMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt index dd4eb55..d5c99c8 100644 --- a/src/jvmMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt +++ b/src/jvmMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt @@ -29,4 +29,25 @@ internal actual /*value*/ class _AtomicLong { actual fun compareAndSet(expected: Long, newValue: Long): Boolean = this._native.compareAndSet(expected, newValue) + + actual fun compareAndExchange(expected: Long, newValue: Long): Long = + if (_Linkage._isNativeCompareAndExchangeExists) this._native.compareAndExchange(expected, newValue) + else _Misc._compareAndExchange( + get = this._native::get, + cas = this._native::compareAndSet, + expected = expected, newValue = newValue + ) + + private object _Linkage { + @JvmField + val _isNativeCompareAndExchangeExists = run { + val a = AtomicLong() + try { + a.compareAndExchange(0, 0) + return@run true + } catch (_: NoSuchMethodError) { + return@run false + } + } + } } \ No newline at end of file diff --git a/src/nonJvmMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt b/src/nonJvmMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt index f26768a..466ce68 100644 --- a/src/nonJvmMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt +++ b/src/nonJvmMain/kotlin/ru/landrafhomyak/utility/reference_counter/_AtomicLong.kt @@ -23,4 +23,11 @@ internal actual class _AtomicLong { actual fun compareAndSet(expected: Long, newValue: Long): Boolean = this._atomicfu.compareAndSet(expected, newValue) + + actual fun compareAndExchange(expected: Long, newValue: Long): Long = + _Misc._compareAndExchange( + get = this._atomicfu::value::get, + cas = this._atomicfu::compareAndSet, + expected = expected, newValue = newValue + ) } \ No newline at end of file