[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:
mvicsokolova
2023-04-25 16:55:42 +00:00
committed by Space Team
parent 1c56a71b14
commit 75b4469757
33 changed files with 1162 additions and 493 deletions
@@ -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
/**