[K/N] Stabilization of Atomics API
`AtomicInt`, `AtomicLong`, `AtomicReference` and `AtomicNativePtr` classes were moved to `kotlin.concurrent` package. The corresponding classes from `kotlin.native.concurrent` were deprecated with warning since Kotlin 1.9.
In order to prepare for further commonization of Atomics API the following changes were made:
* `kotlin.concurrent.AtomicInt`:
* `increment(): Unit` and `decrement(): Unit` methods were deprecated with error
* New methods were added: `incrementAndGet(): Int` , `decrementAndGet(): Int`, `getAndIncrement(): Int`, `getAndDecrement(): Int`, `getAndSet(newValue: Int): Int`
* `kotlin.concurrent.AtomicLong`:
* `increment(): Unit` and `decrement(): Unit` methods were deprecated with error
* New methods were added: `incrementAndGet(): Long`, `decrementAndGet(): Long`, `getAndIncrement(): Long`, `getAndDecrement(): Long`, `getAndSet(newValue: Long): Long`
* Deprecated `AtomicLong()` constructor with default parameter value
* For all atomic classes `compareAndSwap` method was renamed to `compareAndExchange`
See KT-58074 for more details.
Merge-request: KT-MR-9272
Merged-by: Maria Sokolova <maria.sokolova@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package kotlin.concurrent
|
||||
|
||||
import kotlinx.cinterop.NativePtr
|
||||
import kotlin.native.internal.*
|
||||
import kotlin.reflect.*
|
||||
import kotlin.concurrent.*
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
/**
|
||||
* An [Int] value that is always updated atomically.
|
||||
* For additional details about atomicity guarantees for reads and writes see [kotlin.concurrent.Volatile].
|
||||
*
|
||||
* Legacy MM: Atomic values and freezing: this type is unique with regard to freezing.
|
||||
* Namely, it provides mutating operations, while can participate in frozen subgraphs.
|
||||
* So shared frozen objects can have mutable fields of [AtomicInt] type.
|
||||
*/
|
||||
@Frozen
|
||||
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
|
||||
@SinceKotlin("1.9")
|
||||
public class AtomicInt(@Volatile public var value: Int) {
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] and returns the old value.
|
||||
*/
|
||||
public fun getAndSet(newValue: Int): Int = this::value.getAndSetField(newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected],
|
||||
* returns true if the operation was successful and false only if the current value was not equal to the expected value.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*/
|
||||
public fun compareAndSet(expected: Int, newValue: Int): Boolean = this::value.compareAndSetField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*/
|
||||
public fun compareAndExchange(expected: Int, newValue: Int): Int = this::value.compareAndExchangeField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the old value.
|
||||
*/
|
||||
public fun getAndAdd(delta: Int): Int = this::value.getAndAddField(delta)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the new value.
|
||||
*/
|
||||
public fun addAndGet(delta: Int): Int = this::value.getAndAddField(delta) + delta
|
||||
|
||||
/**
|
||||
* Atomically increments the current value by one and returns the old value.
|
||||
*/
|
||||
public fun getAndIncrement(): Int = this::value.getAndAddField(1)
|
||||
|
||||
/**
|
||||
* Atomically increments the current value by one and returns the new value.
|
||||
*/
|
||||
public fun incrementAndGet(): Int = this::value.getAndAddField(1) + 1
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one and returns the new value.
|
||||
*/
|
||||
public fun decrementAndGet(): Int = this::value.getAndAddField(-1) - 1
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one and returns the old value.
|
||||
*/
|
||||
public fun getAndDecrement(): Int = this::value.getAndAddField(-1)
|
||||
|
||||
/**
|
||||
* Atomically increments the current value by one.
|
||||
*/
|
||||
@Deprecated(level = DeprecationLevel.ERROR, message = "Use incrementAndGet() or getAndIncrement() instead.",
|
||||
replaceWith = ReplaceWith("this.incrementAndGet()"))
|
||||
public fun increment(): Unit {
|
||||
addAndGet(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one.
|
||||
*/
|
||||
@Deprecated(level = DeprecationLevel.ERROR, message = "Use decrementAndGet() or getAndDecrement() instead.",
|
||||
replaceWith = ReplaceWith("this.decrementAndGet()"))
|
||||
public fun decrement(): Unit {
|
||||
addAndGet(-1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the current [value].
|
||||
*/
|
||||
public override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A [Long] value that is always updated atomically.
|
||||
* For additional details about atomicity guarantees for reads and writes see [kotlin.concurrent.Volatile].
|
||||
*
|
||||
* Legacy MM: Atomic values and freezing: this type is unique with regard to freezing.
|
||||
* Namely, it provides mutating operations, while can participate in frozen subgraphs.
|
||||
* So shared frozen objects can have mutable fields of [AtomicLong] type.
|
||||
*/
|
||||
@Frozen
|
||||
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
|
||||
@SinceKotlin("1.9")
|
||||
public class AtomicLong(@Volatile public var value: Long) {
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] and returns the old value.
|
||||
*/
|
||||
public fun getAndSet(newValue: Long): Long = this::value.getAndSetField(newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected],
|
||||
* returns true if the operation was successful and false only if the current value was not equal to the expected value.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*/
|
||||
public fun compareAndSet(expected: Long, newValue: Long): Boolean = this::value.compareAndSetField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*/
|
||||
public fun compareAndExchange(expected: Long, newValue: Long): Long = this::value.compareAndExchangeField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the old value.
|
||||
*/
|
||||
public fun getAndAdd(delta: Long): Long = this::value.getAndAddField(delta)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the new value.
|
||||
*/
|
||||
public fun addAndGet(delta: Long): Long = this::value.getAndAddField(delta) + delta
|
||||
|
||||
/**
|
||||
* Atomically increments the current value by one and returns the old value.
|
||||
*/
|
||||
public fun getAndIncrement(): Long = this::value.getAndAddField(1L)
|
||||
|
||||
/**
|
||||
* Atomically increments the current value by one and returns the new value.
|
||||
*/
|
||||
public fun incrementAndGet(): Long = this::value.getAndAddField(1L) + 1L
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one and returns the new value.
|
||||
*/
|
||||
public fun decrementAndGet(): Long = this::value.getAndAddField(-1L) - 1L
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one and returns the old value.
|
||||
*/
|
||||
public fun getAndDecrement(): Long = this::value.getAndAddField(-1L)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the new value.
|
||||
*/
|
||||
@Deprecated(level = DeprecationLevel.ERROR, message = "Use addAndGet(delta: Long) instead.")
|
||||
public fun addAndGet(delta: Int): Long = addAndGet(delta.toLong())
|
||||
|
||||
/**
|
||||
* Atomically increments the current value by one.
|
||||
*/
|
||||
@Deprecated(level = DeprecationLevel.ERROR, message = "Use incrementAndGet() or getAndIncrement() instead.",
|
||||
replaceWith = ReplaceWith("this.incrementAndGet()"))
|
||||
public fun increment(): Unit {
|
||||
addAndGet(1L)
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one.
|
||||
*/
|
||||
@Deprecated(level = DeprecationLevel.ERROR, message = "Use decrementAndGet() or getAndDecrement() instead.",
|
||||
replaceWith = ReplaceWith("this.decrementAndGet()"))
|
||||
public fun decrement(): Unit {
|
||||
addAndGet(-1L)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the current [value].
|
||||
*/
|
||||
public override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* An object reference that is always updated atomically.
|
||||
*
|
||||
* Legacy MM: An atomic reference to a frozen Kotlin object. Can be used in concurrent scenarious
|
||||
* but frequently shall be of nullable type and be zeroed out once no longer needed.
|
||||
* Otherwise memory leak could happen. To detect such leaks [kotlin.native.internal.GC.detectCycles]
|
||||
* in debug mode could be helpful.
|
||||
*/
|
||||
@FrozenLegacyMM
|
||||
@LeakDetectorCandidate
|
||||
@NoReorderFields
|
||||
@OptIn(FreezingIsDeprecated::class)
|
||||
@SinceKotlin("1.9")
|
||||
public class AtomicReference<T> {
|
||||
private var value_: T
|
||||
|
||||
// A spinlock to fix potential ARC race.
|
||||
private var lock: Int = 0
|
||||
|
||||
// Optimization for speeding up access.
|
||||
private var cookie: Int = 0
|
||||
|
||||
/**
|
||||
* Creates a new atomic reference pointing to the [given value][value].
|
||||
*
|
||||
* @throws InvalidMutabilityException with legacy MM if reference is not frozen.
|
||||
*/
|
||||
constructor(value: T) {
|
||||
if (this.isFrozen) {
|
||||
checkIfFrozen(value)
|
||||
}
|
||||
value_ = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The current value.
|
||||
* Gets the current value or sets to the given [new value][newValue].
|
||||
*
|
||||
* Legacy MM: if the [new value][newValue] value is not null, it must be frozen or permanent object.
|
||||
*
|
||||
* @throws InvalidMutabilityException with legacy MM if the value is not frozen or a permanent object
|
||||
*/
|
||||
public var value: T
|
||||
get() = @Suppress("UNCHECKED_CAST")(getImpl() as T)
|
||||
set(newValue) = setImpl(newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] and returns the old value.
|
||||
*/
|
||||
public fun getAndSet(newValue: T): T {
|
||||
while (true) {
|
||||
val old = value
|
||||
if (old === newValue) {
|
||||
return old
|
||||
}
|
||||
if (compareAndSet(old, newValue)) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected],
|
||||
* returns true if the operation was successful and false only if the current value was not equal to the expected value.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*
|
||||
* Comparison of values is done by reference.
|
||||
*/
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSet")
|
||||
external public fun compareAndSet(expected: T, newValue: T): Boolean
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*
|
||||
* Comparison of values is done by reference.
|
||||
*
|
||||
* Legacy MM: if the [new value][newValue] value is not null, it must be frozen or permanent object.
|
||||
*
|
||||
* @throws InvalidMutabilityException with legacy MM if the value is not frozen or a permanent object
|
||||
*/
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSwap")
|
||||
external public fun compareAndExchange(expected: T, newValue: T): T
|
||||
|
||||
/**
|
||||
* Returns the string representation of the current [value].
|
||||
*/
|
||||
public override fun toString(): String =
|
||||
"${debugString(this)} -> ${debugString(value)}"
|
||||
|
||||
// Implementation details.
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_set")
|
||||
private external fun setImpl(newValue: Any?): Unit
|
||||
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_get")
|
||||
private external fun getImpl(): Any?
|
||||
}
|
||||
|
||||
/**
|
||||
* A [kotlinx.cinterop.NativePtr] value that is always updated atomically.
|
||||
* For additional details about atomicity guarantees for reads and writes see [kotlin.concurrent.Volatile].
|
||||
*
|
||||
* [kotlinx.cinterop.NativePtr] is a value type, hence it is stored in [AtomicNativePtr] without boxing
|
||||
* and [compareAndSet], [compareAndExchange] operations perform comparison by value.
|
||||
*
|
||||
* Legacy MM: Atomic values and freezing: this type is unique with regard to freezing.
|
||||
* Namely, it provides mutating operations, while can participate in frozen subgraphs.
|
||||
* So shared frozen objects can have mutable fields of [AtomicNativePtr] type.
|
||||
*/
|
||||
@Frozen
|
||||
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
|
||||
@SinceKotlin("1.9")
|
||||
public class AtomicNativePtr(@Volatile public var value: NativePtr) {
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] and returns the old value.
|
||||
*/
|
||||
public fun getAndSet(newValue: NativePtr): NativePtr {
|
||||
// Pointer types are allowed for atomicrmw xchg operand since LLVM 15.0,
|
||||
// after LLVM version update, it may be implemented via getAndSetField intrinsic.
|
||||
// Check: https://youtrack.jetbrains.com/issue/KT-57557
|
||||
while (true) {
|
||||
val old = value
|
||||
if (this::value.compareAndSetField(old, newValue)) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected],
|
||||
* returns true if the operation was successful and false only if the current value was not equal to the expected value.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*
|
||||
* Comparison of values is done by value.
|
||||
*/
|
||||
public fun compareAndSet(expected: NativePtr, newValue: NativePtr): Boolean =
|
||||
this::value.compareAndSetField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*
|
||||
* Comparison of values is done by value.
|
||||
*/
|
||||
public fun compareAndExchange(expected: NativePtr, newValue: NativePtr): NativePtr =
|
||||
this::value.compareAndExchangeField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Returns the string representation of the current [value].
|
||||
*/
|
||||
public override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
|
||||
private fun idString(value: Any) = "${value.hashCode().toUInt().toString(16)}"
|
||||
|
||||
private fun debugString(value: Any?): String {
|
||||
if (value == null) return "null"
|
||||
return "${value::class.qualifiedName}: ${idString(value)}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the value of the field referenced by [this] to [expectedValue], and if they are equal,
|
||||
* atomically replaces it with [newValue].
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* Comparison is done by reference or value depending on field representation.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Returns true if the actual field value matched [expectedValue]
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.COMPARE_AND_SET_FIELD)
|
||||
internal external fun <T> KMutableProperty0<T>.compareAndSetField(expectedValue: T, newValue: T): Boolean
|
||||
|
||||
/**
|
||||
* Compares the value of the field referenced by [this] to [expectedValue], and if they are equal,
|
||||
* atomically replaces it with [newValue].
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* Comparison is done by reference or value depending on field representation.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Returns the field value before operation.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.COMPARE_AND_EXCHANGE_FIELD)
|
||||
internal external fun <T> KMutableProperty0<T>.compareAndExchangeField(expectedValue: T, newValue: T): T
|
||||
|
||||
/**
|
||||
* Atomically sets value of the field referenced by [this] to [newValue] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_SET_FIELD)
|
||||
internal external fun <T> KMutableProperty0<T>.getAndSetField(newValue: T): T
|
||||
|
||||
|
||||
/**
|
||||
* Atomically increments value of the field referenced by [this] by [delta] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
|
||||
internal external fun KMutableProperty0<Short>.getAndAddField(delta: Short): Short
|
||||
|
||||
/**
|
||||
* Atomically increments value of the field referenced by [this] by [delta] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
|
||||
internal external fun KMutableProperty0<Int>.getAndAddField(newValue: Int): Int
|
||||
|
||||
/**
|
||||
* Atomically increments value of the field referenced by [this] by [delta] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
|
||||
internal external fun KMutableProperty0<Long>.getAndAddField(newValue: Long): Long
|
||||
|
||||
/**
|
||||
* Atomically increments value of the field referenced by [this] by [delta] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
|
||||
internal external fun KMutableProperty0<Byte>.getAndAddField(newValue: Byte): Byte
|
||||
@@ -25,6 +25,7 @@ internal actual constructor(
|
||||
public actual override val context: CoroutineContext
|
||||
get() = delegate.context
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private var resultRef = FreezableAtomicReference<Any?>(initialResult)
|
||||
|
||||
public actual override fun resumeWith(result: Result<T>) {
|
||||
@@ -54,4 +55,4 @@ internal actual constructor(
|
||||
else -> result // either COROUTINE_SUSPENDED or data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import kotlin.reflect.*
|
||||
import kotlin.concurrent.*
|
||||
|
||||
/**
|
||||
* Wrapper around [Int] with atomic synchronized operations.
|
||||
* An [Int] value that is always updated atomically.
|
||||
* For additional details about atomicity guarantees for reads and writes see [kotlin.concurrent.Volatile].
|
||||
*
|
||||
* Legacy MM: Atomic values and freezing: this type is unique with regard to freezing.
|
||||
* Namely, it provides mutating operations, while can participate in frozen subgraphs.
|
||||
@@ -19,57 +20,85 @@ import kotlin.concurrent.*
|
||||
*/
|
||||
@Frozen
|
||||
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
|
||||
@Deprecated("Use kotlin.concurrent.AtomicInt instead.", ReplaceWith("kotlin.concurrent.AtomicInt"))
|
||||
@DeprecatedSinceKotlin(warningSince = "1.9")
|
||||
public class AtomicInt(public @Volatile var value: Int) {
|
||||
/**
|
||||
* Increments the value by [delta] and returns the new value.
|
||||
* Atomically sets the value to the given [new value][newValue] and returns the old value.
|
||||
*/
|
||||
public fun getAndSet(newValue: Int): Int = this::value.getAndSetField(newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected],
|
||||
* returns true if the operation was successful and false only if the current value was not equal to the expected value.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the new value
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*/
|
||||
public fun compareAndSet(expected: Int, newValue: Int): Boolean = this::value.compareAndSetField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*/
|
||||
public fun compareAndSwap(expected: Int, newValue: Int): Int = this::value.compareAndExchangeField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the old value.
|
||||
*/
|
||||
public fun getAndAdd(delta: Int): Int = this::value.getAndAddField(delta)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the new value.
|
||||
*/
|
||||
public fun addAndGet(delta: Int): Int = this::value.getAndAddField(delta) + delta
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @return the old value
|
||||
* Atomically increments the current value by one and returns the old value.
|
||||
*/
|
||||
public fun compareAndSwap(expected: Int, new: Int): Int = this::value.compareAndSwapField(expected, new)
|
||||
public fun getAndIncrement(): Int = this::value.getAndAddField(1)
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @return true if successful
|
||||
* Atomically increments the current value by one and returns the new value.
|
||||
*/
|
||||
public fun compareAndSet(expected: Int, new: Int): Boolean = this::value.compareAndSetField(expected, new)
|
||||
public fun incrementAndGet(): Int = this::value.getAndAddField(1) + 1
|
||||
|
||||
/**
|
||||
* Increments value by one.
|
||||
* Atomically decrements the current value by one and returns the new value.
|
||||
*/
|
||||
public fun decrementAndGet(): Int = this::value.getAndAddField(-1) - 1
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one and returns the old value.
|
||||
*/
|
||||
public fun getAndDecrement(): Int = this::value.getAndAddField(-1)
|
||||
|
||||
/**
|
||||
* Atomically increments the current value by one.
|
||||
*/
|
||||
@Deprecated("Use incrementAndGet() or getAndIncrement() instead.", ReplaceWith("this.incrementAndGet()"))
|
||||
public fun increment(): Unit {
|
||||
addAndGet(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements value by one.
|
||||
* Atomically decrements the current value by one.
|
||||
*/
|
||||
@Deprecated("Use decrementAndGet() or getAndDecrement() instead.", ReplaceWith("this.decrementAndGet()"))
|
||||
public fun decrement(): Unit {
|
||||
addAndGet(-1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this object.
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
public override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around [Long] with atomic synchronized operations.
|
||||
* A [Long] value that is always updated atomically.
|
||||
* For additional details about atomicity guarantees for reads and writes see [kotlin.concurrent.Volatile].
|
||||
*
|
||||
* Legacy MM: Atomic values and freezing: this type is unique with regard to freezing.
|
||||
* Namely, it provides mutating operations, while can participate in frozen subgraphs.
|
||||
@@ -77,113 +106,90 @@ public class AtomicInt(public @Volatile var value: Int) {
|
||||
*/
|
||||
@Frozen
|
||||
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
|
||||
public class AtomicLong(public @Volatile var value: Long = 0) {
|
||||
@Deprecated("Use kotlin.concurrent.AtomicLong instead.", ReplaceWith("kotlin.concurrent.AtomicLong"))
|
||||
@DeprecatedSinceKotlin(warningSince = "1.9")
|
||||
public class AtomicLong(public @Volatile var value: Long = 0L) {
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] and returns the old value.
|
||||
*/
|
||||
public fun getAndSet(newValue: Long): Long = this::value.getAndSetField(newValue)
|
||||
|
||||
/**
|
||||
* Increments the value by [delta] and returns the new value.
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected],
|
||||
* returns true if the operation was successful and false only if the current value was not equal to the expected value.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the new value
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*/
|
||||
public fun compareAndSet(expected: Long, newValue: Long): Boolean = this::value.compareAndSetField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*/
|
||||
public fun compareAndSwap(expected: Long, newValue: Long): Long = this::value.compareAndExchangeField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the old value.
|
||||
*/
|
||||
public fun getAndAdd(delta: Long): Long = this::value.getAndAddField(delta)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the new value.
|
||||
*/
|
||||
public fun addAndGet(delta: Long): Long = this::value.getAndAddField(delta) + delta
|
||||
|
||||
/**
|
||||
* Increments the value by [delta] and returns the new value.
|
||||
*
|
||||
* @param delta the value to add
|
||||
* @return the new value
|
||||
* Atomically increments the current value by one and returns the old value.
|
||||
*/
|
||||
public fun getAndIncrement(): Long = this::value.getAndAddField(1L)
|
||||
|
||||
/**
|
||||
* Atomically increments the current value by one and returns the new value.
|
||||
*/
|
||||
public fun incrementAndGet(): Long = this::value.getAndAddField(1L) + 1L
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one and returns the new value.
|
||||
*/
|
||||
public fun decrementAndGet(): Long = this::value.getAndAddField(-1L) - 1L
|
||||
|
||||
/**
|
||||
* Atomically decrements the current value by one and returns the old value.
|
||||
*/
|
||||
public fun getAndDecrement(): Long = this::value.getAndAddField(-1L)
|
||||
|
||||
/**
|
||||
* Atomically adds the [given value][delta] to the current value and returns the new value.
|
||||
*/
|
||||
@Deprecated("Use addAndGet(delta: Long) instead.")
|
||||
public fun addAndGet(delta: Int): Long = addAndGet(delta.toLong())
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @return the old value
|
||||
*/
|
||||
public fun compareAndSwap(expected: Long, new: Long): Long = this::value.compareAndSwapField(expected, new)
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @return true if successful, false if state is unchanged
|
||||
*/
|
||||
public fun compareAndSet(expected: Long, new: Long): Boolean = this::value.compareAndSetField(expected, new)
|
||||
|
||||
/**
|
||||
* Increments value by one.
|
||||
* Atomically increments the current value by one.
|
||||
*/
|
||||
@Deprecated("Use incrementAndGet() or getAndIncrement() instead.", ReplaceWith("this.incrementAndGet()"))
|
||||
public fun increment(): Unit {
|
||||
addAndGet(1L)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements value by one.
|
||||
* Atomically decrements the current value by one.
|
||||
*/
|
||||
@Deprecated("Use decrementAndGet() or getAndDecrement() instead.", ReplaceWith("this.decrementAndGet()"))
|
||||
fun decrement(): Unit {
|
||||
addAndGet(-1L)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this object.
|
||||
*
|
||||
* @return the string representation of this object
|
||||
*/
|
||||
public override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around [kotlinx.cinterop.NativePtr] with atomic synchronized operations.
|
||||
*
|
||||
* Legacy MM: Atomic values and freezing: this type is unique with regard to freezing.
|
||||
* Namely, it provides mutating operations, while can participate in frozen subgraphs.
|
||||
* So shared frozen objects can have mutable fields of [AtomicNativePtr] type.
|
||||
*/
|
||||
@Frozen
|
||||
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
|
||||
public class AtomicNativePtr(public @Volatile var value: NativePtr) {
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @return the old value
|
||||
*/
|
||||
public fun compareAndSwap(expected: NativePtr, new: NativePtr): NativePtr =
|
||||
this::value.compareAndSwapField(expected, new)
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @return true if successful
|
||||
*/
|
||||
public fun compareAndSet(expected: NativePtr, new: NativePtr): Boolean =
|
||||
this::value.compareAndSetField(expected, new)
|
||||
|
||||
/**
|
||||
* Returns the string representation of this object.
|
||||
*
|
||||
* @return string representation of this object
|
||||
*/
|
||||
public override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
|
||||
private fun idString(value: Any) = "${value.hashCode().toUInt().toString(16)}"
|
||||
|
||||
private fun debugString(value: Any?): String {
|
||||
if (value == null) return "null"
|
||||
return "${value::class.qualifiedName}: ${idString(value)}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around Kotlin object with atomic operations.
|
||||
* An object reference that is always updated atomically.
|
||||
*
|
||||
* Legacy MM: An atomic reference to a frozen Kotlin object. Can be used in concurrent scenarious
|
||||
* but frequently shall be of nullable type and be zeroed out once no longer needed.
|
||||
@@ -194,6 +200,8 @@ private fun debugString(value: Any?): String {
|
||||
@LeakDetectorCandidate
|
||||
@NoReorderFields
|
||||
@OptIn(FreezingIsDeprecated::class)
|
||||
@Deprecated("Use kotlin.concurrent.AtomicReference instead.", ReplaceWith("kotlin.concurrent.AtomicReference"))
|
||||
@DeprecatedSinceKotlin(warningSince = "1.9")
|
||||
public class AtomicReference<T> {
|
||||
private var value_: T
|
||||
|
||||
@@ -204,7 +212,8 @@ public class AtomicReference<T> {
|
||||
private var cookie: Int = 0
|
||||
|
||||
/**
|
||||
* Creates a new atomic reference pointing to given [ref].
|
||||
* Creates a new atomic reference pointing to the [given value][value].
|
||||
*
|
||||
* @throws InvalidMutabilityException with legacy MM if reference is not frozen.
|
||||
*/
|
||||
constructor(value: T) {
|
||||
@@ -215,70 +224,139 @@ public class AtomicReference<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* The referenced value.
|
||||
* Gets the value or sets the [new] value.
|
||||
* Legacy MM: if [new] value is not null, it must be frozen or permanent object.
|
||||
* The current value.
|
||||
* Gets the current value or sets to the given [new value][newValue].
|
||||
*
|
||||
* Legacy MM: if the [new value][newValue] value is not null, it must be frozen or permanent object.
|
||||
*
|
||||
* @throws InvalidMutabilityException with legacy MM if the value is not frozen or a permanent object
|
||||
*/
|
||||
public var value: T
|
||||
get() = @Suppress("UNCHECKED_CAST")(getImpl() as T)
|
||||
set(new) = setImpl(new)
|
||||
set(newValue) = setImpl(newValue)
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
* Note that comparison is identity-based, not value-based.
|
||||
*
|
||||
* Legacy MM: if [new] value is not null, it must be frozen or permanent object.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @throws InvalidMutabilityException with legacy MM if the value is not frozen or a permanent object
|
||||
* @return the old value
|
||||
* Atomically sets the value to the given [new value][newValue] and returns the old value.
|
||||
*/
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSwap")
|
||||
external public fun compareAndSwap(expected: T, new: T): T
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
* Note that comparison is identity-based, not value-based.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @return true if successful
|
||||
*/
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSet")
|
||||
external public fun compareAndSet(expected: T, new: T): Boolean
|
||||
|
||||
/**
|
||||
* Returns the string representation of this object.
|
||||
*
|
||||
* @return string representation of this object
|
||||
*/
|
||||
public override fun toString(): String =
|
||||
"${debugString(this)} -> ${debugString(value)}"
|
||||
|
||||
// TODO: Consider making this public.
|
||||
internal fun swap(new: T): T {
|
||||
public fun getAndSet(newValue: T): T {
|
||||
while (true) {
|
||||
val old = value
|
||||
if (old === new) {
|
||||
if (old === newValue) {
|
||||
return old
|
||||
}
|
||||
if (compareAndSet(old, new)) {
|
||||
if (compareAndSet(old, newValue)) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected],
|
||||
* returns true if the operation was successful and false only if the current value was not equal to the expected value.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*
|
||||
* Comparison of values is done by reference.
|
||||
*/
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSet")
|
||||
external public fun compareAndSet(expected: T, newValue: T): Boolean
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*
|
||||
* Comparison of values is done by reference.
|
||||
*
|
||||
* Legacy MM: if the [new value][newValue] value is not null, it must be frozen or permanent object.
|
||||
*
|
||||
* @throws InvalidMutabilityException with legacy MM if the value is not frozen or a permanent object
|
||||
*/
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSwap")
|
||||
external public fun compareAndSwap(expected: T, newValue: T): T
|
||||
|
||||
/**
|
||||
* Returns the string representation of this object.
|
||||
*/
|
||||
public override fun toString(): String =
|
||||
"${debugString(this)} -> ${debugString(value)}"
|
||||
|
||||
// Implementation details.
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_set")
|
||||
private external fun setImpl(new: Any?): Unit
|
||||
private external fun setImpl(newValue: Any?): Unit
|
||||
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_get")
|
||||
private external fun getImpl(): Any?
|
||||
}
|
||||
|
||||
/**
|
||||
* A [kotlinx.cinterop.NativePtr] value that is always updated atomically.
|
||||
* For additional details about atomicity guarantees for reads and writes see [kotlin.concurrent.Volatile].
|
||||
*
|
||||
* [kotlinx.cinterop.NativePtr] is a value type, hence it is stored in [AtomicNativePtr] without boxing
|
||||
* and [compareAndSet], [compareAndSwap] operations perform comparison by value.
|
||||
*
|
||||
* Legacy MM: Atomic values and freezing: this type is unique with regard to freezing.
|
||||
* Namely, it provides mutating operations, while can participate in frozen subgraphs.
|
||||
* So shared frozen objects can have mutable fields of [AtomicNativePtr] type.
|
||||
*/
|
||||
@Frozen
|
||||
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
|
||||
@Deprecated("Use kotlin.concurrent.AtomicNativePtr instead.", ReplaceWith("kotlin.concurrent.AtomicNativePtr"))
|
||||
@DeprecatedSinceKotlin(warningSince = "1.9")
|
||||
public class AtomicNativePtr(public @Volatile var value: NativePtr) {
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] and returns the old value.
|
||||
*/
|
||||
public fun getAndSet(newValue: NativePtr): NativePtr {
|
||||
// Pointer types are allowed for atomicrmw xchg operand since LLVM 15.0,
|
||||
// after LLVM version update, it may be implemented via getAndSetField intrinsic.
|
||||
// Check: https://youtrack.jetbrains.com/issue/KT-57557
|
||||
while (true) {
|
||||
val old = value
|
||||
if (this::value.compareAndSetField(old, newValue)) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected],
|
||||
* returns true if the operation was successful and false only if the current value was not equal to the expected value.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*
|
||||
* Comparison of values is done by value.
|
||||
*/
|
||||
public fun compareAndSet(expected: NativePtr, newValue: NativePtr): Boolean =
|
||||
this::value.compareAndSetField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Provides sequential consistent ordering guarantees and cannot fail spuriously.
|
||||
*
|
||||
* Comparison of values is done by value.
|
||||
*/
|
||||
public fun compareAndSwap(expected: NativePtr, newValue: NativePtr): NativePtr =
|
||||
this::value.compareAndExchangeField(expected, newValue)
|
||||
|
||||
/**
|
||||
* Returns the string representation of this object.
|
||||
*/
|
||||
public override fun toString(): String = value.toString()
|
||||
}
|
||||
|
||||
|
||||
private fun idString(value: Any) = "${value.hashCode().toUInt().toString(16)}"
|
||||
|
||||
private fun debugString(value: Any?): String {
|
||||
if (value == null) return "null"
|
||||
return "${value::class.qualifiedName}: ${idString(value)}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this class is useful only with legacy memory manager. Please use [AtomicReference] instead.
|
||||
*
|
||||
@@ -291,6 +369,8 @@ public class AtomicReference<T> {
|
||||
@LeakDetectorCandidate
|
||||
@ExportTypeInfo("theFreezableAtomicReferenceTypeInfo")
|
||||
@FreezingIsDeprecated
|
||||
@Deprecated("Use kotlin.concurrent.AtomicReference instead.", ReplaceWith("kotlin.concurrent.AtomicReference"))
|
||||
@DeprecatedSinceKotlin(warningSince = "1.9")
|
||||
public class FreezableAtomicReference<T>(private var value_: T) {
|
||||
// A spinlock to fix potential ARC race.
|
||||
private var lock: Int = 0
|
||||
@@ -300,53 +380,57 @@ public class FreezableAtomicReference<T>(private var value_: T) {
|
||||
|
||||
/**
|
||||
* The referenced value.
|
||||
* Gets the value or sets the [new] value. If [new] value is not null,
|
||||
* Gets the value or sets to the given [new value][newValue]. If the [new value][newValue] is not null,
|
||||
* and `this` is frozen - it must be frozen or permanent object.
|
||||
*
|
||||
* @throws InvalidMutabilityException if the value is not frozen or a permanent object
|
||||
*/
|
||||
public var value: T
|
||||
get() = @Suppress("UNCHECKED_CAST")(getImpl() as T)
|
||||
set(new) {
|
||||
set(newValue) {
|
||||
if (this.isShareable())
|
||||
setImpl(new)
|
||||
setImpl(newValue)
|
||||
else
|
||||
value_ = new
|
||||
value_ = newValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
* Legacy MM: If [new] value is not null and object is frozen, it must be frozen or permanent object.
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns the old value in any case.
|
||||
*
|
||||
* Legacy MM: If the [new value][newValue] value is not null and object is frozen, it must be frozen or permanent object.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @param newValue the new value
|
||||
* @throws InvalidMutabilityException with legacy MM if the value is not frozen or a permanent object
|
||||
* @return the old value
|
||||
*/
|
||||
public fun compareAndSwap(expected: T, new: T): T {
|
||||
public fun compareAndSwap(expected: T, newValue: T): T {
|
||||
return if (this.isShareable()) {
|
||||
@Suppress("UNCHECKED_CAST")(compareAndSwapImpl(expected, new) as T)
|
||||
@Suppress("UNCHECKED_CAST")(compareAndSwapImpl(expected, newValue) as T)
|
||||
} else {
|
||||
val old = value_
|
||||
if (old === expected) value_ = new
|
||||
if (old === expected) value_ = newValue
|
||||
old
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares value with [expected] and replaces it with [new] value if values matches.
|
||||
* Atomically sets the value to the given [new value][newValue] if the current value equals the [expected value][expected]
|
||||
* and returns true if operation was successful.
|
||||
*
|
||||
* Note that comparison is identity-based, not value-based.
|
||||
*
|
||||
* @param expected the expected value
|
||||
* @param new the new value
|
||||
* @param newValue the new value
|
||||
* @return true if successful
|
||||
*/
|
||||
public fun compareAndSet(expected: T, new: T): Boolean {
|
||||
public fun compareAndSet(expected: T, newValue: T): Boolean {
|
||||
if (this.isShareable())
|
||||
return compareAndSetImpl(expected, new)
|
||||
return compareAndSetImpl(expected, newValue)
|
||||
val old = value_
|
||||
if (old === expected) {
|
||||
value_ = new
|
||||
value_ = newValue
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -362,13 +446,13 @@ public class FreezableAtomicReference<T>(private var value_: T) {
|
||||
"${debugString(this)} -> ${debugString(value)}"
|
||||
|
||||
// TODO: Consider making this public.
|
||||
internal fun swap(new: T): T {
|
||||
internal fun swap(newValue: T): T {
|
||||
while (true) {
|
||||
val old = value
|
||||
if (old === new) {
|
||||
if (old === newValue) {
|
||||
return old
|
||||
}
|
||||
if (compareAndSet(old, new)) {
|
||||
if (compareAndSet(old, newValue)) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
@@ -376,145 +460,14 @@ public class FreezableAtomicReference<T>(private var value_: T) {
|
||||
|
||||
// Implementation details.
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_set")
|
||||
private external fun setImpl(new: Any?): Unit
|
||||
private external fun setImpl(newValue: Any?): Unit
|
||||
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_get")
|
||||
private external fun getImpl(): Any?
|
||||
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSwap")
|
||||
private external fun compareAndSwapImpl(expected: Any?, new: Any?): Any?
|
||||
private external fun compareAndSwapImpl(expected: Any?, newValue: Any?): Any?
|
||||
|
||||
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSet")
|
||||
private external fun compareAndSetImpl(expected: Any?, new: Any?): Boolean
|
||||
private external fun compareAndSetImpl(expected: Any?, newValue: Any?): Boolean
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares the value of the field referenced by [this] to [expectedValue], and if they are equal,
|
||||
* atomically replaces it with [newValue].
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* Comparison is done by reference or value depending on field representation.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Returns true if the actual field value matched [expectedValue]
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.COMPARE_AND_SET_FIELD)
|
||||
internal external fun <T> KMutableProperty0<T>.compareAndSetField(expectedValue: T, newValue: T): Boolean
|
||||
|
||||
/**
|
||||
* Compares the value of the field referenced by [this] to [expectedValue], and if they are equal,
|
||||
* atomically replaces it with [newValue].
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* Comparison is done by reference or value depending on field representation.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Returns true if the actual field value before operation.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.COMPARE_AND_SWAP_FIELD)
|
||||
internal external fun <T> KMutableProperty0<T>.compareAndSwapField(expectedValue: T, newValue: T): T
|
||||
|
||||
/**
|
||||
* Atomically sets value of the field referenced by [this] to [newValue] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_SET_FIELD)
|
||||
internal external fun <T> KMutableProperty0<T>.getAndSetField(newValue: T): T
|
||||
|
||||
|
||||
/**
|
||||
* Atomically increments value of the field referenced by [this] by [delta] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
|
||||
internal external fun KMutableProperty0<Short>.getAndAddField(delta: Short): Short
|
||||
|
||||
/**
|
||||
* Atomically increments value of the field referenced by [this] by [delta] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
|
||||
internal external fun KMutableProperty0<Int>.getAndAddField(newValue: Int): Int
|
||||
|
||||
/**
|
||||
* Atomically increments value of the field referenced by [this] by [delta] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
|
||||
internal external fun KMutableProperty0<Long>.getAndAddField(newValue: Long): Long
|
||||
|
||||
/**
|
||||
* Atomically increments value of the field referenced by [this] by [delta] and returns old field value.
|
||||
*
|
||||
* For now, it can be used only within the same file, where property is defined.
|
||||
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
|
||||
*
|
||||
* If [this] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
|
||||
* would be thrown.
|
||||
*
|
||||
* If property referenced by [this] has nontrivial setter it will not be called.
|
||||
*
|
||||
* Legacy MM: if [this] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
|
||||
*/
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
|
||||
internal external fun KMutableProperty0<Byte>.getAndAddField(newValue: Byte): Byte
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
package kotlin.native.concurrent
|
||||
|
||||
import kotlin.experimental.ExperimentalNativeApi
|
||||
import kotlin.native.internal.Frozen
|
||||
import kotlin.concurrent.AtomicReference
|
||||
|
||||
@FreezingIsDeprecated
|
||||
internal class FreezeAwareLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
||||
@@ -94,7 +96,7 @@ internal class AtomicLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
||||
|
||||
override val value: T
|
||||
get() {
|
||||
if (value_.compareAndSwap(UNINITIALIZED, INITIALIZING) === UNINITIALIZED) {
|
||||
if (value_.compareAndExchange(UNINITIALIZED, INITIALIZING) === UNINITIALIZED) {
|
||||
// We execute exclusively here.
|
||||
val ctor = initializer_.value
|
||||
if (ctor != null && initializer_.compareAndSet(ctor, null)) {
|
||||
@@ -204,4 +206,4 @@ internal class SafePublicationLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
||||
override fun isInitialized(): Boolean = valueRef.value !== UNINITIALIZED
|
||||
|
||||
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package kotlin.native.concurrent
|
||||
|
||||
import kotlin.experimental.ExperimentalNativeApi
|
||||
import kotlin.native.internal.Frozen
|
||||
import kotlin.concurrent.AtomicInt
|
||||
|
||||
@ThreadLocal
|
||||
@OptIn(FreezingIsDeprecated::class)
|
||||
@@ -24,11 +25,11 @@ internal class Lock {
|
||||
fun lock() {
|
||||
val lockData = CurrentThread.id.hashCode()
|
||||
loop@ do {
|
||||
val old = locker_.compareAndSwap(0, lockData)
|
||||
val old = locker_.compareAndExchange(0, lockData)
|
||||
when (old) {
|
||||
lockData -> {
|
||||
// Was locked by us already.
|
||||
reenterCount_.increment()
|
||||
reenterCount_.incrementAndGet()
|
||||
break@loop
|
||||
}
|
||||
0 -> {
|
||||
@@ -42,10 +43,10 @@ internal class Lock {
|
||||
|
||||
fun unlock() {
|
||||
if (reenterCount_.value > 0) {
|
||||
reenterCount_.decrement()
|
||||
reenterCount_.decrementAndGet()
|
||||
} else {
|
||||
val lockData = CurrentThread.id.hashCode()
|
||||
val old = locker_.compareAndSwap(lockData, 0)
|
||||
val old = locker_.compareAndExchange(lockData, 0)
|
||||
assert(old == lockData)
|
||||
}
|
||||
}
|
||||
@@ -58,4 +59,4 @@ internal inline fun <R> locked(lock: Lock, block: () -> R): R {
|
||||
} finally {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package kotlin.native.concurrent
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.native.internal.Frozen
|
||||
import kotlin.concurrent.AtomicNativePtr
|
||||
|
||||
/**
|
||||
* Note: modern Kotlin/Native memory manager allows to share objects between threads without additional ceremonies,
|
||||
|
||||
@@ -83,11 +83,11 @@ internal class IntrinsicType {
|
||||
|
||||
// Atomic
|
||||
const val COMPARE_AND_SET_FIELD = "COMPARE_AND_SET_FIELD"
|
||||
const val COMPARE_AND_SWAP_FIELD = "COMPARE_AND_SWAP_FIELD"
|
||||
const val COMPARE_AND_EXCHANGE_FIELD = "COMPARE_AND_EXCHANGE_FIELD"
|
||||
const val GET_AND_SET_FIELD = "GET_AND_SET_FIELD"
|
||||
const val GET_AND_ADD_FIELD = "GET_AND_ADD_FIELD"
|
||||
const val COMPARE_AND_SET = "COMPARE_AND_SET"
|
||||
const val COMPARE_AND_SWAP = "COMPARE_AND_SWAP"
|
||||
const val COMPARE_AND_EXCHANGE = "COMPARE_AND_EXCHANGE"
|
||||
const val GET_AND_SET = "GET_AND_SET"
|
||||
const val GET_AND_ADD = "GET_AND_ADD"
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION", "DEPRECATION_ERROR") // Char.toInt()
|
||||
package kotlin.native.internal
|
||||
|
||||
import kotlin.experimental.ExperimentalNativeApi
|
||||
import kotlin.internal.getProgressionLastElement
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.native.concurrent.FreezableAtomicReference
|
||||
import kotlin.native.concurrent.freeze
|
||||
import kotlin.native.concurrent.FreezableAtomicReference
|
||||
|
||||
@ExportForCppRuntime
|
||||
@PublishedApi
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
package kotlin.random
|
||||
|
||||
import kotlin.native.concurrent.AtomicLong
|
||||
import kotlin.concurrent.AtomicLong
|
||||
import kotlin.system.getTimeNanos
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user