diff --git a/CHANGES.md b/CHANGES.md
index 7b6cb5e6..d9f62161 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,14 @@
# Change log for kotlinx.atomicfu
+# Version 0.21.0
+
+* Updated Kotlin to 1.8.20.
+* Updated Gradle to 7.3 (#300).
+* Updated kotlinx.metadata version to 0.6.0 (#281).
+* Removed JS Legacy configurations for KGP >= 1.9.0 (#296).
+* Fixed class duplication (from original and transformed directories) in Jar (#301).
+* Minimal supported KGP(1.7.0) and Gradle(7.0) versions are set since this release.
+
# Version 0.20.2
* Fix for unresolved `kotlinx-atomicfu-runtime` dependency error (https://youtrack.jetbrains.com/issue/KT-57235),
diff --git a/README.md b/README.md
index aa71758e..da6e5c91 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[](https://kotlinlang.org/docs/components-stability.html)
[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
[](https://www.apache.org/licenses/LICENSE-2.0)
-[](https://search.maven.org/artifact/org.jetbrains.kotlinx/atomicfu/0.20.2/pom)
+[](https://search.maven.org/artifact/org.jetbrains.kotlinx/atomicfu/0.21.0/pom)
>Note on Beta status: the plugin is in its active development phase and changes from release to release.
>We do provide a compatibility of atomicfu-transformed artifacts between releases, but we do not provide
@@ -12,6 +12,7 @@
**Atomicfu** is a multiplatform library that provides the idiomatic and effective way of using atomic operations in Kotlin.
## Table of contents
+- [Requirements](#requirements)
- [Features](#features)
- [Example](#example)
- [Quickstart](#quickstart)
@@ -31,6 +32,13 @@
- [Tracing operations](#tracing-operations)
- [Kotlin/Native support](#kotlin-native-support)
+## Requirements
+
+Starting from version `0.21.0` of the library your project is required to use:
+
+* Gradle `7.0` or newer
+
+* Kotlin `1.7.0` or newer
## Features
@@ -111,7 +119,7 @@ buildscript {
}
dependencies {
- classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.20.2")
+ classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.21.0")
}
}
@@ -128,7 +136,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.20.2'
+ classpath 'org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.21.0'
}
}
@@ -146,7 +154,7 @@ Maven configuration is supported for JVM projects.
```xml
- 0.20.2
+ 0.21.0
```
@@ -290,7 +298,7 @@ To set configuration options you should create `atomicfu` section in a `build.gr
like this:
```groovy
atomicfu {
- dependenciesVersion = '0.20.2'
+ dependenciesVersion = '0.21.0'
}
```
@@ -312,7 +320,7 @@ To turn off transformation for Kotlin/JS set option `transformJs` to `false`.
Here are all available configuration options (with their defaults):
```groovy
atomicfu {
- dependenciesVersion = '0.20.2' // set to null to turn-off auto dependencies
+ dependenciesVersion = '0.21.0' // set to null to turn-off auto dependencies
transformJvm = true // set to false to turn off JVM transformation
jvmVariant = "FU" // JVM transformation variant: FU,VH, or BOTH
transformJs = true // set to false to turn off JVM transformation
diff --git a/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt b/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt
index d5f2e7b0..d683e69d 100644
--- a/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt
+++ b/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt
@@ -9,10 +9,13 @@ import org.gradle.api.*
import org.gradle.api.file.*
import org.gradle.api.internal.*
import org.gradle.api.plugins.*
+import org.gradle.api.provider.Provider
+import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.*
import org.gradle.api.tasks.testing.*
import org.gradle.jvm.tasks.*
+import org.gradle.util.*
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.*
@@ -23,6 +26,7 @@ import org.jetbrains.kotlin.gradle.targets.js.*
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlinx.atomicfu.gradle.*
+import javax.inject.Inject
private const val EXTENSION_NAME = "atomicfu"
private const val ORIGINAL_DIR_NAME = "originalClassesDir"
@@ -34,9 +38,12 @@ private const val TEST_IMPLEMENTATION_CONFIGURATION = "testImplementation"
private const val ENABLE_JS_IR_TRANSFORMATION_LEGACY = "kotlinx.atomicfu.enableIrTransformation"
private const val ENABLE_JS_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJsIrTransformation"
private const val ENABLE_JVM_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJvmIrTransformation"
+private const val MIN_SUPPORTED_GRADLE_VERSION = "7.0"
+private const val MIN_SUPPORTED_KGP_VERSION = "1.7.0"
open class AtomicFUGradlePlugin : Plugin {
override fun apply(project: Project) = project.run {
+ checkCompatibility()
val pluginVersion = rootProject.buildscript.configurations.findByName("classpath")
?.allDependencies?.find { it.name == "atomicfu-gradle-plugin" }?.version
extensions.add(EXTENSION_NAME, AtomicFUPluginExtension(pluginVersion))
@@ -46,6 +53,24 @@ open class AtomicFUGradlePlugin : Plugin {
}
}
+private fun Project.checkCompatibility() {
+ val currentGradleVersion = GradleVersion.current()
+ val kotlinVersion = getKotlinVersion()
+ val minSupportedVersion = GradleVersion.version(MIN_SUPPORTED_GRADLE_VERSION)
+ if (currentGradleVersion < minSupportedVersion) {
+ throw GradleException(
+ "The current Gradle version is not compatible with Atomicfu gradle plugin. " +
+ "Please use Gradle $MIN_SUPPORTED_GRADLE_VERSION or newer, or the previous version of Atomicfu gradle plugin."
+ )
+ }
+ if (!kotlinVersion.atLeast(1, 7, 0)) {
+ throw GradleException(
+ "The current Kotlin gradle plugin version is not compatible with Atomicfu gradle plugin. " +
+ "Please use Kotlin $MIN_SUPPORTED_KGP_VERSION or newer, or the previous version of Atomicfu gradle plugin."
+ )
+ }
+}
+
private fun Project.configureDependencies() {
withPluginWhenEvaluatedDependencies("kotlin") { version ->
dependencies.add(
@@ -242,68 +267,79 @@ private fun Project.configureTransformationForTarget(target: KotlinTarget) {
?: return@compilations // skip unknown compilations
val classesDirs = compilation.output.classesDirs
// make copy of original classes directory
- val originalClassesDirs: FileCollection =
- project.files(classesDirs.from.toTypedArray()).filter { it.exists() }
+ @Suppress("UNCHECKED_CAST")
+ val compilationTask = compilation.compileTaskProvider as TaskProvider
+ val originalDestinationDirectory = project.layout.buildDirectory
+ .dir("classes/atomicfu-orig/${target.name}/${compilation.name}")
+ compilationTask.configure {
+ if (it is Kotlin2JsCompile) {
+ @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_PARAMETER_TYPE")
+ it.defaultDestinationDirectory.value(originalDestinationDirectory)
+ } else {
+ it.destinationDirectory.value(originalDestinationDirectory)
+ }
+ }
+ val originalClassesDirs: FileCollection = project.objects.fileCollection().from(
+ compilationTask.flatMap { it.destinationDirectory }
+ )
originalDirsByCompilation[compilation] = originalClassesDirs
- val transformedClassesDir =
- project.buildDir.resolve("classes/atomicfu/${target.name}/${compilation.name}")
+ val transformedClassesDir = project.layout.buildDirectory
+ .dir("classes/atomicfu/${target.name}/${compilation.name}")
val transformTask = when (target.platformType) {
KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> {
// skip transformation task if transformation is turned off or ir transformation is enabled
if (!config.transformJvm || rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION)) return@compilations
- project.createJvmTransformTask(compilation).configureJvmTask(
- compilation.compileDependencyFiles,
- compilation.compileAllTaskName,
- transformedClassesDir,
- originalClassesDirs,
- config
- )
+ project.registerJvmTransformTask(compilation)
+ .configureJvmTask(
+ compilation.compileDependencyFiles,
+ compilation.compileAllTaskName,
+ transformedClassesDir,
+ originalClassesDirs,
+ config
+ )
+ .also {
+ compilation.defaultSourceSet.kotlin.compiledBy(it, AtomicFUTransformTask::destinationDirectory)
+ }
}
KotlinPlatformType.js -> {
// skip when js transformation is not needed or when IR is transformed
if (!config.transformJs || (needsJsIrTransformation(target))) {
return@compilations
}
- project.createJsTransformTask(compilation).configureJsTask(
- compilation.compileAllTaskName,
- transformedClassesDir,
- originalClassesDirs,
- config
- )
+ project.registerJsTransformTask(compilation)
+ .configureJsTask(
+ compilation.compileAllTaskName,
+ transformedClassesDir,
+ originalClassesDirs,
+ config
+ )
+ .also {
+ compilation.defaultSourceSet.kotlin.compiledBy(it, AtomicFUTransformJsTask::destinationDirectory)
+ }
}
else -> error("Unsupported transformation platform '${target.platformType}'")
}
//now transformTask is responsible for compiling this source set into the classes directory
+ compilation.defaultSourceSet.kotlin.destinationDirectory.value(transformedClassesDir)
classesDirs.setFrom(transformedClassesDir)
- classesDirs.builtBy(transformTask)
- (tasks.findByName(target.artifactsTaskName) as? Jar)?.apply {
- setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant.BOTH)
+ classesDirs.setBuiltBy(listOf(transformTask))
+ tasks.withType(Jar::class.java).configureEach {
+ if (name == target.artifactsTaskName) {
+ it.setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant.BOTH)
+ }
}
// test should compile and run against original production binaries
if (compilationType == CompilationType.TEST) {
val mainCompilation =
compilation.target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
- val originalMainClassesDirs = project.files(
- // use Callable because there is no guarantee that main is configured before test
- Callable { originalDirsByCompilation[mainCompilation]!! }
+ val originalMainClassesDirs = project.objects.fileCollection().from(
+ mainCompilation.compileTaskProvider.flatMap { (it as KotlinCompileTool).destinationDirectory }
)
-
- // KGP >= 1.7.0 has breaking changes in task hierarchy:
- // https://youtrack.jetbrains.com/issue/KT-32805#focus=Comments-27-5915479.0-0
- val (majorVersion, minorVersion) = getKotlinPluginVersion()
- .split('.')
- .take(2)
- .map { it.toInt() }
- if (majorVersion == 1 && minorVersion < 7) {
- (tasks.findByName(compilation.compileKotlinTaskName) as? AbstractCompile)?.classpath =
- originalMainClassesDirs + compilation.compileDependencyFiles - mainCompilation.output.classesDirs
- } else {
- (tasks.findByName(compilation.compileKotlinTaskName) as? AbstractKotlinCompileTool<*>)
- ?.libraries
- ?.setFrom(
- originalMainClassesDirs + compilation.compileDependencyFiles - mainCompilation.output.classesDirs
- )
- }
+ (tasks.findByName(compilation.compileKotlinTaskName) as? AbstractKotlinCompileTool<*>)
+ ?.libraries
+ ?.setFrom(
+ originalMainClassesDirs + compilation.compileDependencyFiles
+ )
(tasks.findByName("${target.name}${compilation.name.capitalize()}") as? Test)?.classpath =
originalMainClassesDirs + (compilation as KotlinCompilationToRunnableFiles).runtimeDependencyFiles - mainCompilation.output.classesDirs
@@ -381,48 +417,49 @@ fun Project.configureMultiplatformPluginDependencies(version: String) {
fun String.toJvmVariant(): JvmVariant = enumValueOf(toUpperCase(Locale.US))
-fun Project.createJvmTransformTask(compilation: KotlinCompilation<*>): AtomicFUTransformTask =
- tasks.create(
+fun Project.registerJvmTransformTask(compilation: KotlinCompilation<*>): TaskProvider =
+ tasks.register(
"transform${compilation.target.name.capitalize()}${compilation.name.capitalize()}Atomicfu",
AtomicFUTransformTask::class.java
)
-fun Project.createJsTransformTask(compilation: KotlinCompilation<*>): AtomicFUTransformJsTask =
- tasks.create(
+fun Project.registerJsTransformTask(compilation: KotlinCompilation<*>): TaskProvider =
+ tasks.register(
"transform${compilation.target.name.capitalize()}${compilation.name.capitalize()}Atomicfu",
AtomicFUTransformJsTask::class.java
)
-fun Project.createJvmTransformTask(sourceSet: SourceSet): AtomicFUTransformTask =
- tasks.create(sourceSet.getTaskName("transform", "atomicfuClasses"), AtomicFUTransformTask::class.java)
-
-fun AtomicFUTransformTask.configureJvmTask(
+fun TaskProvider.configureJvmTask(
classpath: FileCollection,
classesTaskName: String,
- transformedClassesDir: File,
+ transformedClassesDir: Provider,
originalClassesDir: FileCollection,
config: AtomicFUPluginExtension
-): ConventionTask =
+): TaskProvider =
apply {
- dependsOn(classesTaskName)
- classPath = classpath
- inputFiles = originalClassesDir
- outputDir = transformedClassesDir
- jvmVariant = config.jvmVariant
- verbose = config.verbose
+ configure {
+ it.dependsOn(classesTaskName)
+ it.classPath = classpath
+ it.inputFiles = originalClassesDir
+ it.destinationDirectory.value(transformedClassesDir)
+ it.jvmVariant = config.jvmVariant
+ it.verbose = config.verbose
+ }
}
-fun AtomicFUTransformJsTask.configureJsTask(
+fun TaskProvider.configureJsTask(
classesTaskName: String,
- transformedClassesDir: File,
+ transformedClassesDir: Provider,
originalClassesDir: FileCollection,
config: AtomicFUPluginExtension
-): ConventionTask =
+): TaskProvider =
apply {
- dependsOn(classesTaskName)
- inputFiles = originalClassesDir
- outputDir = transformedClassesDir
- verbose = config.verbose
+ configure {
+ it.dependsOn(classesTaskName)
+ it.inputFiles = originalClassesDir
+ it.destinationDirectory.value(transformedClassesDir)
+ it.verbose = config.verbose
+ }
}
fun Jar.setupJarManifest(multiRelease: Boolean) {
@@ -445,13 +482,29 @@ class AtomicFUPluginExtension(pluginVersion: String?) {
}
@CacheableTask
-open class AtomicFUTransformTask : ConventionTask() {
+abstract class AtomicFUTransformTask : ConventionTask() {
+ @get:Inject
+ internal abstract val providerFactory: ProviderFactory
+
+ @get:Inject
+ internal abstract val projectLayout: ProjectLayout
+
@PathSensitive(PathSensitivity.RELATIVE)
@InputFiles
lateinit var inputFiles: FileCollection
- @OutputDirectory
- lateinit var outputDir: File
+ @Suppress("unused")
+ @Deprecated(
+ message = "Replaced with 'destinationDirectory'",
+ replaceWith = ReplaceWith("destinationDirectory")
+ )
+ @get:Internal
+ var outputDir: File
+ get() = destinationDirectory.get().asFile
+ set(value) { destinationDirectory.value(projectLayout.dir(providerFactory.provider { value })) }
+
+ @get:OutputDirectory
+ abstract val destinationDirectory: DirectoryProperty
@Classpath
@InputFiles
@@ -467,7 +520,7 @@ open class AtomicFUTransformTask : ConventionTask() {
fun transform() {
val cp = classPath.files.map { it.absolutePath }
inputFiles.files.forEach { inputDir ->
- AtomicFUTransformer(cp, inputDir, outputDir).let { t ->
+ AtomicFUTransformer(cp, inputDir, destinationDirectory.get().asFile).let { t ->
t.jvmVariant = jvmVariant.toJvmVariant()
t.verbose = verbose
t.transform()
@@ -477,13 +530,30 @@ open class AtomicFUTransformTask : ConventionTask() {
}
@CacheableTask
-open class AtomicFUTransformJsTask : ConventionTask() {
+abstract class AtomicFUTransformJsTask : ConventionTask() {
+
+ @get:Inject
+ internal abstract val providerFactory: ProviderFactory
+
+ @get:Inject
+ internal abstract val projectLayout: ProjectLayout
+
@PathSensitive(PathSensitivity.RELATIVE)
@InputFiles
lateinit var inputFiles: FileCollection
- @OutputDirectory
- lateinit var outputDir: File
+ @Suppress("unused")
+ @Deprecated(
+ message = "Replaced with 'destinationDirectory'",
+ replaceWith = ReplaceWith("destinationDirectory")
+ )
+ @get:Internal
+ var outputDir: File
+ get() = destinationDirectory.get().asFile
+ set(value) { destinationDirectory.value(projectLayout.dir(providerFactory.provider { value })) }
+
+ @get:OutputDirectory
+ abstract val destinationDirectory: DirectoryProperty
@Input
var verbose = false
@@ -491,7 +561,7 @@ open class AtomicFUTransformJsTask : ConventionTask() {
@TaskAction
fun transform() {
inputFiles.files.forEach { inputDir ->
- AtomicFUTransformerJS(inputDir, outputDir).let { t ->
+ AtomicFUTransformerJS(inputDir, destinationDirectory.get().asFile).let { t ->
t.verbose = verbose
t.transform()
}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/Assert.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/Assert.kt
index f55e38aa..191dbac0 100644
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/Assert.kt
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/Assert.kt
@@ -28,5 +28,9 @@ internal fun BuildResult.assertTaskUpToDate(task: String) {
}
private fun BuildResult.assertTaskOutcome(taskOutcome: TaskOutcome, taskName: String) {
- assertEquals(taskOutcome, task(taskName)?.outcome)
+ assertEquals(
+ taskOutcome,
+ task(taskName)?.outcome,
+ "Task $taskName does not have ${taskOutcome.name} outcome"
+ )
}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JvmProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JvmProjectTest.kt
index ac1a4876..bdc84d21 100644
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JvmProjectTest.kt
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JvmProjectTest.kt
@@ -35,7 +35,7 @@ class JvmLegacyTransformationTest : BaseKotlinGradleTest("jvm-simple") {
checkTaskOutcomes(
executedTasks = listOf(
":compileKotlin",
- ":transformAtomicfu",
+ ":transformMainAtomicfu",
":compileTestKotlin",
":transformTestAtomicfu"
),
@@ -46,7 +46,7 @@ class JvmLegacyTransformationTest : BaseKotlinGradleTest("jvm-simple") {
fun testClasspath() {
runner.build()
checkJvmCompilationClasspath(
- originalClassFile = "build/classes/kotlin/main/IntArithmetic.class",
+ originalClassFile = "build/classes/atomicfu-orig/main/IntArithmetic.class",
transformedClassFile = "build/classes/atomicfu/main/IntArithmetic.class"
)
}
@@ -103,6 +103,6 @@ class JvmIrTransformationTest : BaseKotlinGradleTest("jvm-simple") {
@Test
fun testAtomicfuReferences() {
runner.build()
- checkBytecode("build/classes/kotlin/main/IntArithmetic.class")
+ checkBytecode("build/classes/atomicfu-orig/main/IntArithmetic.class")
}
}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/MppProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/MppProjectTest.kt
index 8e0aa502..fcc8a66c 100644
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/MppProjectTest.kt
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/MppProjectTest.kt
@@ -50,7 +50,7 @@ class MppLegacyTransformationTest : BaseKotlinGradleTest("mpp-simple") {
fun testClasspath() {
runner.build()
checkJvmCompilationClasspath(
- originalClassFile = "build/classes/kotlin/jvm/main/IntArithmetic.class",
+ originalClassFile = "build/classes/atomicfu-orig/jvm/main/IntArithmetic.class",
transformedClassFile = "build/classes/atomicfu/jvm/main/IntArithmetic.class"
)
checkJsCompilationClasspath()
@@ -112,7 +112,7 @@ class MppJvmIrTransformationTest : BaseKotlinGradleTest("mpp-simple") {
@Test
fun testAtomicfuReferences() {
runner.build()
- checkBytecode("build/classes/kotlin/jvm/main/IntArithmetic.class")
+ checkBytecode("build/classes/atomicfu-orig/jvm/main/IntArithmetic.class")
}
}
@@ -217,6 +217,6 @@ class MppBothIrTransformationTest : BaseKotlinGradleTest("mpp-simple") {
@Test
fun testAtomicfuReferences() {
runner.build()
- checkBytecode("build/classes/kotlin/jvm/main/IntArithmetic.class")
+ checkBytecode("build/classes/atomicfu-orig/jvm/main/IntArithmetic.class")
}
}
diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt
index 39950e60..03ea62e3 100644
--- a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt
+++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt
@@ -59,6 +59,30 @@ public expect fun atomic(initial: Int, trace: TraceBase = None): AtomicInt
*/
public expect fun atomic(initial: Int): AtomicInt
+/**
+ * Creates atomic [UInt] with a given [initial] value and a [trace] object to [trace modifications][Trace] of the value.
+ *
+ * It can only be used to initialize a private or internal read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialUInt, trace)
+ * ```
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public expect fun atomic(initial: UInt, trace: TraceBase = None): AtomicUInt
+
+/**
+ * Creates atomic [UInt] with a given [initial] value.
+ *
+ * It can only be used to initialize a private or internal read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialInt)
+ * ```
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public expect fun atomic(initial: UInt): AtomicUInt
+
/**
* Creates atomic [Long] with a given [initial] value and a [trace] object to [trace modifications][Trace] of the value.
*
@@ -83,6 +107,30 @@ public expect fun atomic(initial: Long, trace: TraceBase = None): AtomicLong
*/
public expect fun atomic(initial: Long): AtomicLong
+/**
+ * Creates atomic [ULong] with a given [initial] value and a [trace] object to [trace modifications][Trace] of the value.
+ *
+ * It can only be used to initialize a private or internal read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialUInt, trace)
+ * ```
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public expect fun atomic(initial: ULong, trace: TraceBase = None): AtomicULong
+
+/**
+ * Creates atomic [ULong] with a given [initial] value.
+ *
+ * It can only be used to initialize a private or internal read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialInt)
+ * ```
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public expect fun atomic(initial: ULong): AtomicULong
+
/**
* Creates atomic [Boolean] with a given [initial] value and a [trace] object to [trace modifications][Trace] of the value.
*
@@ -386,6 +434,141 @@ public inline fun AtomicInt.updateAndGet(function: (Int) -> Int): Int {
}
}
+// ==================================== AtomicUInt ====================================
+
+/**
+ * Atomic reference to an [UInt] variable with volatile reads/writes via
+ * [value] property and various atomic read-modify-write operations
+ * like [compareAndSet] and others.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public expect class AtomicUInt {
+ /**
+ * Reads/writes of this property maps to read/write of volatile variable.
+ */
+ public var value: UInt
+
+ @InlineOnly
+ public inline operator fun getValue(thisRef: Any?, property: KProperty<*>): UInt
+
+ @InlineOnly
+ public inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: UInt)
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.lazySet].
+ */
+ public fun lazySet(value: UInt)
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.compareAndSet].
+ */
+ public fun compareAndSet(expect: UInt, update: UInt): Boolean
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndSet].
+ */
+ public fun getAndSet(value: UInt): UInt
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndIncrement].
+ */
+ public fun getAndIncrement(): UInt
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndDecrement].
+ */
+ public fun getAndDecrement(): UInt
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndAdd].
+ */
+ public fun getAndAdd(delta: UInt): UInt
+
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.addAndGet].
+ */
+ public fun addAndGet(delta: UInt): UInt
+
+ /**
+ * Atomically subtracts the given value to the current value
+ * of the field of the given object managed by this updater.
+ */
+ public fun getAndSub(delta: UInt): UInt
+
+ /**
+ * Atomically subtracts the given value to the current value
+ * of the field of the given object managed by this updater.
+ */
+ public fun subAndGet(delta: UInt): UInt
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.incrementAndGet].
+ */
+ public fun incrementAndGet(): UInt
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.decrementAndGet].
+ */
+ public fun decrementAndGet(): UInt
+
+ /**
+ * Performs atomic addition of [delta].
+ */
+ public inline operator fun plusAssign(delta: UInt)
+
+ /**
+ * Performs atomic subtraction of [delta].
+ */
+ public inline operator fun minusAssign(delta: UInt)
+}
+
+/**
+ * Infinite loop that reads this atomic variable and performs the specified [action] on its value.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public inline fun AtomicUInt.loop(action: (UInt) -> Unit): Nothing {
+ while (true) {
+ action(value)
+ }
+}
+
+/**
+ * Updates variable atomically using the specified [function] of its value.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public inline fun AtomicUInt.update(function: (UInt) -> UInt) {
+ while (true) {
+ val cur = value
+ val upd = function(cur)
+ if (compareAndSet(cur, upd)) return
+ }
+}
+
+/**
+ * Updates variable atomically using the specified [function] of its value and returns its old value.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public inline fun AtomicUInt.getAndUpdate(function: (UInt) -> UInt): UInt {
+ while (true) {
+ val cur = value
+ val upd = function(cur)
+ if (compareAndSet(cur, upd)) return cur
+ }
+}
+
+/**
+ * Updates variable atomically using the specified [function] of its value and returns its new value.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public inline fun AtomicUInt.updateAndGet(function: (UInt) -> UInt): UInt {
+ while (true) {
+ val cur = value
+ val upd = function(cur)
+ if (compareAndSet(cur, upd)) return upd
+ }
+}
+
// ==================================== AtomicLong ====================================
/**
@@ -503,6 +686,141 @@ public inline fun AtomicLong.updateAndGet(function: (Long) -> Long): Long {
}
}
+// ==================================== AtomicULong ====================================
+
+/**
+ * Atomic reference to an [ULong] variable with volatile reads/writes via
+ * [value] property and various atomic read-modify-write operations
+ * like [compareAndSet] and others.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public expect class AtomicULong {
+ /**
+ * Reads/writes of this property maps to read/write of volatile variable.
+ */
+ public var value: ULong
+
+ @InlineOnly
+ public inline operator fun getValue(thisRef: Any?, property: KProperty<*>): ULong
+
+ @InlineOnly
+ public inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: ULong)
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.lazySet].
+ */
+ public fun lazySet(value: ULong)
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.compareAndSet].
+ */
+ public fun compareAndSet(expect: ULong, update: ULong): Boolean
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndSet].
+ */
+ public fun getAndSet(value: ULong): ULong
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndIncrement].
+ */
+ public fun getAndIncrement(): ULong
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndDecrement].
+ */
+ public fun getAndDecrement(): ULong
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndAdd].
+ */
+ public fun getAndAdd(delta: ULong): ULong
+
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.addAndGet].
+ */
+ public fun addAndGet(delta: ULong): ULong
+
+ /**
+ * Atomically subtracts the given value to the current value
+ * of the field of the given object managed by this updater.
+ */
+ public fun getAndSub(delta: ULong): ULong
+
+ /**
+ * Atomically subtracts the given value to the current value
+ * of the field of the given object managed by this updater.
+ */
+ public fun subAndGet(delta: ULong): ULong
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.incrementAndGet].
+ */
+ public fun incrementAndGet(): ULong
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.decrementAndGet].
+ */
+ public fun decrementAndGet(): ULong
+
+ /**
+ * Performs atomic addition of [delta].
+ */
+ public inline operator fun plusAssign(delta: ULong)
+
+ /**
+ * Performs atomic subtraction of [delta].
+ */
+ public inline operator fun minusAssign(delta: ULong)
+}
+
+/**
+ * Infinite loop that reads this atomic variable and performs the specified [action] on its value.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public inline fun AtomicULong.loop(action: (ULong) -> Unit): Nothing {
+ while (true) {
+ action(value)
+ }
+}
+
+/**
+ * Updates variable atomically using the specified [function] of its value.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public inline fun AtomicULong.update(function: (ULong) -> ULong) {
+ while (true) {
+ val cur = value
+ val upd = function(cur)
+ if (compareAndSet(cur, upd)) return
+ }
+}
+
+/**
+ * Updates variable atomically using the specified [function] of its value and returns its old value.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public inline fun AtomicULong.getAndUpdate(function: (ULong) -> ULong): ULong {
+ while (true) {
+ val cur = value
+ val upd = function(cur)
+ if (compareAndSet(cur, upd)) return cur
+ }
+}
+
+/**
+ * Updates variable atomically using the specified [function] of its value and returns its new value.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public inline fun AtomicULong.updateAndGet(function: (ULong) -> ULong): ULong {
+ while (true) {
+ val cur = value
+ val upd = function(cur)
+ if (compareAndSet(cur, upd)) return upd
+ }
+}
+
// ==================================== AtomicIntArray ====================================
/**
@@ -537,6 +855,42 @@ public class AtomicLongArray(size: Int) {
public operator fun get(index: Int): AtomicLong = array[index]
}
+// ==================================== AtomicIntArray ====================================
+
+/**
+ * Creates a new array of AtomicInt values of the specified size, where each element is initialised with 0
+ */
+@JsName(ATOMIC_UINT_ARRAY)
+@OptIn(ExperimentalUnsignedTypes::class)
+public class AtomicUIntArray(size: Int) {
+ private val array = Array(size) { atomic(0u) }
+
+ @JsName(ARRAY_SIZE)
+ public val size: Int
+ get() = array.size
+
+ @JsName(ARRAY_ELEMENT_GET)
+ public operator fun get(index: Int): AtomicUInt = array[index]
+}
+
+// ==================================== AtomicLongArray ====================================
+
+/**
+ * Creates a new array of AtomicLong values of the specified size, where each element is initialised with 0L
+ */
+@JsName(ATOMIC_ULONG_ARRAY)
+@OptIn(ExperimentalUnsignedTypes::class)
+public class AtomicULongArray(size: Int) {
+ private val array = Array(size) { atomic(0UL) }
+
+ @JsName(ARRAY_SIZE)
+ public val size: Int
+ get() = array.size
+
+ @JsName(ARRAY_ELEMENT_GET)
+ public operator fun get(index: Int): AtomicULong = array[index]
+}
+
// ==================================== AtomicBooleanArray ====================================
/**
diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt
index 99b42981..205c7edd 100644
--- a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt
+++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt
@@ -10,8 +10,12 @@ internal const val ATOMIC_REF_FACTORY = "atomic\$ref\$"
internal const val ATOMIC_REF_FACTORY_BINARY_COMPATIBILITY = "atomic\$ref\$1"
internal const val ATOMIC_INT_FACTORY = "atomic\$int\$"
internal const val ATOMIC_INT_FACTORY_BINARY_COMPATIBILITY = "atomic\$int\$1"
+internal const val ATOMIC_UINT_FACTORY = "atomic\$uint\$"
+internal const val ATOMIC_UINT_FACTORY_BINARY_COMPATIBILITY = "atomic\$uint\$1"
internal const val ATOMIC_LONG_FACTORY = "atomic\$long\$"
internal const val ATOMIC_LONG_FACTORY_BINARY_COMPATIBILITY = "atomic\$long\$1"
+internal const val ATOMIC_ULONG_FACTORY = "atomic\$ulong\$"
+internal const val ATOMIC_ULONG_FACTORY_BINARY_COMPATIBILITY = "atomic\$ulong\$1"
internal const val ATOMIC_BOOLEAN_FACTORY = "atomic\$boolean\$"
internal const val ATOMIC_BOOLEAN_FACTORY_BINARY_COMPATIBILITY = "atomic\$boolean\$1"
@@ -22,22 +26,40 @@ internal const val ATOMIC_VALUE = "kotlinx\$atomicfu\$value"
internal const val COMPARE_AND_SET = "atomicfu\$compareAndSet"
internal const val GET_AND_SET = "atomicfu\$getAndSet"
internal const val GET_AND_INCREMENT = "atomicfu\$getAndIncrement"
+internal const val GET_AND_INCREMENT_UINT = "atomicfu\$getAndIncrement\$uint"
internal const val GET_AND_INCREMENT_LONG = "atomicfu\$getAndIncrement\$long"
+internal const val GET_AND_INCREMENT_ULONG = "atomicfu\$getAndIncrement\$ulong"
internal const val GET_AND_DECREMENT = "atomicfu\$getAndDecrement"
+internal const val GET_AND_DECREMENT_UINT = "atomicfu\$getAndDecrement\$uint"
internal const val GET_AND_DECREMENT_LONG = "atomicfu\$getAndDecrement\$long"
+internal const val GET_AND_DECREMENT_ULONG = "atomicfu\$getAndDecrement\$ulong"
internal const val INCREMENT_AND_GET = "atomicfu\$incrementAndGet"
+internal const val INCREMENT_AND_GET_UINT = "atomicfu\$incrementAndGet\$uint"
internal const val INCREMENT_AND_GET_LONG = "atomicfu\$incrementAndGet\$long"
+internal const val INCREMENT_AND_GET_ULONG = "atomicfu\$incrementAndGet\$ulong"
internal const val DECREMENT_AND_GET = "atomicfu\$decrementAndGet"
+internal const val DECREMENT_AND_GET_UINT = "atomicfu\$decrementAndGet\$uint"
internal const val DECREMENT_AND_GET_LONG = "atomicfu\$decrementAndGet\$long"
+internal const val DECREMENT_AND_GET_ULONG = "atomicfu\$decrementAndGet\$ulong"
internal const val GET_AND_ADD = "atomicfu\$getAndAdd"
+internal const val GET_AND_ADD_UINT = "atomicfu\$getAndAdd\$uint"
internal const val GET_AND_ADD_LONG = "atomicfu\$getAndAdd\$long"
+internal const val GET_AND_ADD_ULONG = "atomicfu\$getAndAdd\$ulong"
+internal const val GET_AND_SUB = "atomicfu\$getAndSub"
+internal const val GET_AND_SUB_ULONG = "atomicfu\$getAndSub\$ulong"
internal const val ADD_AND_GET = "atomicfu\$addAndGet"
+internal const val ADD_AND_GET_UINT = "atomicfu\$addAndGet\$uint"
internal const val ADD_AND_GET_LONG = "atomicfu\$addAndGet\$long"
+internal const val ADD_AND_GET_ULONG = "atomicfu\$addAndGet\$ulong"
+internal const val SUB_AND_GET = "atomicfu\$subAndGet"
+internal const val SUB_AND_GET_ULONG = "atomicfu\$subAndGet\$ulong"
// Atomic arrays constructors
internal const val ATOMIC_ARRAY_OF_NULLS = "atomicfu\$AtomicRefArray\$ofNulls"
internal const val ATOMIC_INT_ARRAY = "atomicfu\$AtomicIntArray\$int"
+internal const val ATOMIC_UINT_ARRAY = "atomicfu\$AtomicUIntArray\$uint"
internal const val ATOMIC_LONG_ARRAY = "atomicfu\$AtomicLongArray\$long"
+internal const val ATOMIC_ULONG_ARRAY = "atomicfu\$AtomicULongArray\$ulong"
internal const val ATOMIC_BOOLEAN_ARRAY = "atomicfu\$AtomicBooleanArray\$boolean"
internal const val ATOMIC_REF_ARRAY = "atomicfu\$AtomicRefArray\$ref"
diff --git a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
index da1e7b67..14167d4b 100644
--- a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
+++ b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
@@ -21,12 +21,28 @@ public actual fun atomic(initial: Int, trace: TraceBase): AtomicInt = AtomicInt(
@JsName(ATOMIC_INT_FACTORY_BINARY_COMPATIBILITY)
public actual fun atomic(initial: Int): AtomicInt = atomic(initial, None)
+@OptIn(ExperimentalUnsignedTypes::class)
+@JsName(ATOMIC_UINT_FACTORY)
+public actual fun atomic(initial: UInt, trace: TraceBase): AtomicUInt = AtomicUInt(initial)
+
+@OptIn(ExperimentalUnsignedTypes::class)
+@JsName(ATOMIC_UINT_FACTORY_BINARY_COMPATIBILITY)
+public actual fun atomic(initial: UInt): AtomicUInt = atomic(initial, None)
+
@JsName(ATOMIC_LONG_FACTORY)
public actual fun atomic(initial: Long, trace: TraceBase): AtomicLong = AtomicLong(initial)
@JsName(ATOMIC_LONG_FACTORY_BINARY_COMPATIBILITY)
public actual fun atomic(initial: Long): AtomicLong = atomic(initial, None)
+@OptIn(ExperimentalUnsignedTypes::class)
+@JsName(ATOMIC_ULONG_FACTORY)
+public actual fun atomic(initial: ULong, trace: TraceBase): AtomicULong = AtomicULong(initial)
+
+@OptIn(ExperimentalUnsignedTypes::class)
+@JsName(ATOMIC_ULONG_FACTORY_BINARY_COMPATIBILITY)
+public actual fun atomic(initial: ULong): AtomicULong = atomic(initial, None)
+
@JsName(ATOMIC_BOOLEAN_FACTORY)
public actual fun atomic(initial: Boolean, trace: TraceBase): AtomicBoolean = AtomicBoolean(initial)
@@ -151,6 +167,78 @@ public actual class AtomicInt internal constructor(value: Int) {
override fun toString(): String = value.toString()
}
+// ==================================== AtomicUInt ====================================
+
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual class AtomicUInt internal constructor(value: UInt) {
+ @JsName(ATOMIC_VALUE)
+ public actual var value: UInt = value
+
+ actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): UInt = value
+
+ public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: UInt) { this.value = value }
+
+ public actual inline fun lazySet(value: UInt) { this.value = value }
+
+ @JsName(COMPARE_AND_SET)
+ public actual fun compareAndSet(expect: UInt, update: UInt): Boolean {
+ if (value != expect) return false
+ value = update
+ return true
+ }
+
+ @JsName(GET_AND_SET)
+ public actual fun getAndSet(value: UInt): UInt {
+ val oldValue = this.value
+ this.value = value
+ return oldValue
+ }
+
+ @JsName(GET_AND_INCREMENT_UINT)
+ public actual fun getAndIncrement(): UInt = value++
+
+ @JsName(GET_AND_DECREMENT_UINT)
+ public actual fun getAndDecrement(): UInt = value--
+
+ @JsName(GET_AND_ADD_UINT)
+ public actual fun getAndAdd(delta: UInt): UInt {
+ val oldValue = value
+ value += delta
+ return oldValue
+ }
+
+ @JsName(ADD_AND_GET_UINT)
+ public actual fun addAndGet(delta: UInt): UInt {
+ value += delta
+ return value
+ }
+
+ @JsName(GET_AND_SUB)
+ actual fun getAndSub(delta: UInt): UInt {
+ val oldValue = value
+ value -= delta
+ return oldValue
+ }
+
+ @JsName(SUB_AND_GET)
+ actual fun subAndGet(delta: UInt): UInt {
+ value -= delta
+ return value
+ }
+
+ @JsName(INCREMENT_AND_GET_UINT)
+ public actual fun incrementAndGet(): UInt = ++value
+
+ @JsName(DECREMENT_AND_GET_UINT)
+ public actual fun decrementAndGet(): UInt = --value
+
+ public actual inline operator fun plusAssign(delta: UInt) { getAndAdd(delta) }
+
+ public actual inline operator fun minusAssign(delta: UInt) { getAndSub(delta) }
+
+ override fun toString(): String = value.toString()
+}
+
// ==================================== AtomicLong ====================================
public actual class AtomicLong internal constructor(value: Long) {
@@ -206,5 +294,77 @@ public actual class AtomicLong internal constructor(value: Long) {
public actual inline operator fun minusAssign(delta: Long) { getAndAdd(-delta) }
+ override fun toString(): String = value.toString()
+}
+
+// ==================================== AtomicUInt ====================================
+
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual class AtomicULong internal constructor(value: ULong) {
+ @JsName(ATOMIC_VALUE)
+ public actual var value: ULong = value
+
+ actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): ULong = value
+
+ public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: ULong) { this.value = value }
+
+ public actual inline fun lazySet(value: ULong) { this.value = value }
+
+ @JsName(COMPARE_AND_SET)
+ public actual fun compareAndSet(expect: ULong, update: ULong): Boolean {
+ if (value != expect) return false
+ value = update
+ return true
+ }
+
+ @JsName(GET_AND_SET)
+ public actual fun getAndSet(value: ULong): ULong {
+ val oldValue = this.value
+ this.value = value
+ return oldValue
+ }
+
+ @JsName(GET_AND_INCREMENT_ULONG)
+ public actual fun getAndIncrement(): ULong = value++
+
+ @JsName(GET_AND_DECREMENT_ULONG)
+ public actual fun getAndDecrement(): ULong = value--
+
+ @JsName(GET_AND_ADD_ULONG)
+ public actual fun getAndAdd(delta: ULong): ULong {
+ val oldValue = value
+ value += delta
+ return oldValue
+ }
+
+ @JsName(ADD_AND_GET_ULONG)
+ public actual fun addAndGet(delta: ULong): ULong {
+ value += delta
+ return value
+ }
+
+ @JsName(GET_AND_SUB_ULONG)
+ actual fun getAndSub(delta: ULong): ULong {
+ val oldValue = value
+ value -= delta
+ return oldValue
+ }
+
+ @JsName(SUB_AND_GET_ULONG)
+ actual fun subAndGet(delta: ULong): ULong {
+ value -= delta
+ return value
+ }
+
+ @JsName(INCREMENT_AND_GET_ULONG)
+ public actual fun incrementAndGet(): ULong = ++value
+
+ @JsName(DECREMENT_AND_GET_ULONG)
+ public actual fun decrementAndGet(): ULong = --value
+
+ public actual inline operator fun plusAssign(delta: ULong) { getAndAdd(delta) }
+
+ public actual inline operator fun minusAssign(delta: ULong) { getAndSub(delta) }
+
override fun toString(): String = value.toString()
}
\ No newline at end of file
diff --git a/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/AtomicFU.kt b/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
index ddaf1dc8..cda74d27 100644
--- a/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
+++ b/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
@@ -39,6 +39,21 @@ public actual fun atomic(initial: Int, trace: TraceBase): AtomicInt = AtomicInt(
public actual fun atomic(initial: Int): AtomicInt = atomic(initial, None)
+/**
+ * Creates atomic [UInt] with a given [initial] value.
+ *
+ * It can only be used in initialize of private read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialInt)
+ * ```
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual fun atomic(initial: UInt, trace: TraceBase): AtomicUInt = AtomicUInt(initial, trace)
+
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual fun atomic(initial: UInt): AtomicUInt = atomic(initial, None)
+
/**
* Creates atomic [Long] with a given [initial] value.
*
@@ -52,6 +67,21 @@ public actual fun atomic(initial: Long, trace: TraceBase): AtomicLong = AtomicLo
public actual fun atomic(initial: Long): AtomicLong = atomic(initial, None)
+/**
+ * Creates atomic [ULong] with a given [initial] value.
+ *
+ * It can only be used in initialize of private read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialLong)
+ * ```
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual fun atomic(initial: ULong, trace: TraceBase): AtomicULong = AtomicULong(initial, trace)
+
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual fun atomic(initial: ULong): AtomicULong = atomic(initial, None)
+
/**
* Creates atomic [Boolean] with a given [initial] value.
*
@@ -309,6 +339,149 @@ public actual class AtomicInt internal constructor(value: Int, val trace: TraceB
}
}
+// ==================================== AtomicUInt ====================================
+
+/**
+ * Atomic reference to an [UInt] variable with volatile reads/writes via
+ * [value] property and various atomic read-modify-write operations
+ * like [compareAndSet] and others.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual class AtomicUInt internal constructor(value: UInt, val trace: TraceBase) {
+ /**
+ * Reads/writes of this property maps to read/write of volatile variable.
+ */
+ @Volatile
+ public actual var value: UInt = value
+ set(value) {
+ field = value
+ if (trace !== None) trace { "set($value)" }
+ }
+
+ public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): UInt = value
+
+ public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: UInt) { this.value = value }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.lazySet].
+ */
+ public actual fun lazySet(value: UInt) {
+ FU.lazySet(this, value.toInt())
+ if (trace !== None) trace { "lazySet($value)" }
+ }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.compareAndSet].
+ */
+ public actual fun compareAndSet(expect: UInt, update: UInt): Boolean {
+ val result = FU.compareAndSet(this, expect.toInt(), update.toInt())
+ if (result && trace !== None) trace { "CAS($expect, $update)" }
+ return result
+ }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndSet].
+ */
+ public actual fun getAndSet(value: UInt): UInt {
+ val oldValue = FU.getAndSet(this, value.toInt())
+ if (trace !== None) trace { "getAndSet($value):$oldValue" }
+ return oldValue.toUInt()
+ }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndIncrement].
+ */
+ public actual fun getAndIncrement(): UInt {
+ val oldValue = this.getAndAdd(1U)
+ if (trace !== None) trace { "getAndInc():$oldValue" }
+ return oldValue
+ }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndDecrement].
+ */
+ public actual fun getAndDecrement(): UInt {
+ val oldValue = this.getAndSub(1U)
+ if (trace !== None) trace { "getAndDec():$oldValue" }
+ return oldValue
+ }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.getAndAdd].
+ */
+ public actual fun getAndAdd(delta: UInt): UInt {
+ val oldValue = FU.getAndUpdate(this) { (it.toUInt() + delta).toInt() }
+ if (trace !== None) trace { "getAndAdd($delta):$oldValue" }
+ return oldValue.toUInt()
+ }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.addAndGet].
+ */
+ public actual fun addAndGet(delta: UInt): UInt {
+ val newValue = FU.updateAndGet(this) { (it.toUInt() + delta).toInt() }
+ if (trace !== None) trace { "addAndGet($delta):$newValue" }
+ return newValue.toUInt()
+ }
+ /**
+ * Atomically subtracts the given value to the current value
+ * of the field of the given object managed by this updater.
+ */
+ actual fun getAndSub(delta: UInt): UInt {
+ val oldValue = FU.getAndUpdate(this) { (it.toUInt() - delta).toInt() }
+ if (trace !== None) trace { "getAndAdd($delta):$oldValue" }
+ return oldValue.toUInt()
+ }
+
+ /**
+ * Atomically subtracts the given value to the current value
+ * of the field of the given object managed by this updater.
+ */
+ actual fun subAndGet(delta: UInt): UInt {
+ val newValue = FU.updateAndGet(this) { (it.toUInt() - delta).toInt() }
+ if (trace !== None) trace { "addAndGet($delta):$newValue" }
+ return newValue.toUInt()
+ }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.incrementAndGet].
+ */
+ public actual fun incrementAndGet(): UInt {
+ val newValue = this.addAndGet(1U)
+ if (trace !== None) trace { "incAndGet():$newValue" }
+ return newValue
+ }
+
+ /**
+ * Maps to [AtomicIntegerFieldUpdater.decrementAndGet].
+ */
+ public actual fun decrementAndGet(): UInt {
+ val newValue = this.subAndGet(1U)
+ if (trace !== None) trace { "decAndGet():$newValue" }
+ return newValue
+ }
+
+ /**
+ * Performs atomic addition of [delta].
+ */
+ public actual inline operator fun plusAssign(delta: UInt) {
+ getAndAdd(delta)
+ }
+
+ /**
+ * Performs atomic subtraction of [delta].
+ */
+ public actual inline operator fun minusAssign(delta: UInt) {
+ getAndSub(delta)
+ }
+
+ override fun toString(): String = value.toString()
+
+ private companion object {
+ private val FU = AtomicIntegerFieldUpdater.newUpdater(AtomicUInt::class.java, "value")
+ }
+}
+
// ==================================== AtomicLong ====================================
/**
@@ -431,3 +604,147 @@ public actual class AtomicLong internal constructor(value: Long, val trace: Trac
private val FU = AtomicLongFieldUpdater.newUpdater(AtomicLong::class.java, "value")
}
}
+
+
+// ==================================== AtomicULong ====================================
+
+/**
+ * Atomic reference to an [ULong] variable with volatile reads/writes via
+ * [value] property and various atomic read-modify-write operations
+ * like [compareAndSet] and others.
+ */
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual class AtomicULong internal constructor(value: ULong, val trace: TraceBase) {
+ /**
+ * Reads/writes of this property maps to read/write of volatile variable.
+ */
+ @Volatile
+ public actual var value: ULong = value
+ set(value) {
+ field = value
+ if (trace !== None) trace { "set($value)" }
+ }
+
+ public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): ULong = value
+
+ public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: ULong) { this.value = value }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.lazySet].
+ */
+ public actual fun lazySet(value: ULong) {
+ FU.lazySet(this, value.toLong())
+ if (trace !== None) trace { "lazySet($value)" }
+ }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.compareAndSet].
+ */
+ public actual fun compareAndSet(expect: ULong, update: ULong): Boolean {
+ val result = FU.compareAndSet(this, expect.toLong(), update.toLong())
+ if (result && trace !== None) trace { "CAS($expect, $update)" }
+ return result
+ }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.getAndSet].
+ */
+ public actual fun getAndSet(value: ULong): ULong {
+ val oldValue = FU.getAndSet(this, value.toLong())
+ if (trace !== None) trace { "getAndSet($value):$oldValue" }
+ return oldValue.toULong()
+ }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.getAndIncrement].
+ */
+ public actual fun getAndIncrement(): ULong {
+ val oldValue = this.getAndAdd(1U)
+ if (trace !== None) trace { "getAndInc():$oldValue" }
+ return oldValue
+ }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.getAndDecrement].
+ */
+ public actual fun getAndDecrement(): ULong {
+ val oldValue = this.getAndSub(1U)
+ if (trace !== None) trace { "getAndDec():$oldValue" }
+ return oldValue
+ }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.getAndAdd].
+ */
+ public actual fun getAndAdd(delta: ULong): ULong {
+ val oldValue = FU.getAndUpdate(this) { (it.toULong() + delta).toLong() }
+ if (trace !== None) trace { "getAndAdd($delta):$oldValue" }
+ return oldValue.toULong()
+ }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.addAndGet].
+ */
+ public actual fun addAndGet(delta: ULong): ULong {
+ val newValue = FU.updateAndGet(this) { (it.toULong() + delta).toLong() }
+ if (trace !== None) trace { "addAndGet($delta):$newValue" }
+ return newValue.toULong()
+ }
+ /**
+ * Atomically subtracts the given value to the current value
+ * of the field of the given object managed by this updater.
+ */
+ actual fun getAndSub(delta: ULong): ULong {
+ val oldValue = FU.getAndUpdate(this) { (it.toULong() - delta).toLong() }
+ if (trace !== None) trace { "getAndAdd($delta):$oldValue" }
+ return oldValue.toULong()
+ }
+
+ /**
+ * Atomically subtracts the given value to the current value
+ * of the field of the given object managed by this updater.
+ */
+ actual fun subAndGet(delta: ULong): ULong {
+ val newValue = FU.updateAndGet(this) { (it.toULong() - delta).toLong() }
+ if (trace !== None) trace { "addAndGet($delta):$newValue" }
+ return newValue.toULong()
+ }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.incrementAndGet].
+ */
+ public actual fun incrementAndGet(): ULong {
+ val newValue = this.addAndGet(1U)
+ if (trace !== None) trace { "incAndGet():$newValue" }
+ return newValue
+ }
+
+ /**
+ * Maps to [AtomicLongFieldUpdater.decrementAndGet].
+ */
+ public actual fun decrementAndGet(): ULong {
+ val newValue = this.subAndGet(1U)
+ if (trace !== None) trace { "decAndGet():$newValue" }
+ return newValue
+ }
+
+ /**
+ * Performs atomic addition of [delta].
+ */
+ public actual inline operator fun plusAssign(delta: ULong) {
+ getAndAdd(delta)
+ }
+
+ /**
+ * Performs atomic subtraction of [delta].
+ */
+ public actual inline operator fun minusAssign(delta: ULong) {
+ getAndSub(delta)
+ }
+
+ override fun toString(): String = value.toString()
+
+ private companion object {
+ private val FU = AtomicLongFieldUpdater.newUpdater(AtomicULong::class.java, "value")
+ }
+}
diff --git a/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/AtomicFU.kt b/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
index b3695404..1c20c533 100644
--- a/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
+++ b/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
@@ -130,6 +130,46 @@ public actual value class AtomicInt internal constructor(@PublishedApi internal
override fun toString(): String = value.toString()
}
+// ==================================== AtomicInt ====================================
+
+@Suppress("ACTUAL_WITHOUT_EXPECT")
+@OptIn(ExperimentalUnsignedTypes::class)
+public actual value class AtomicUInt internal constructor(@PublishedApi internal val a: KAtomicInt) {
+ public actual inline var value: UInt
+ get() = a.value.toUInt()
+ set(value) { a.value = value.toInt() }
+
+ actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): UInt = value
+
+ public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: UInt) { this.value = value }
+
+ public actual inline fun lazySet(value: UInt) { a.value = value.toInt() }
+
+ public actual inline fun compareAndSet(expect: UInt, update: UInt): Boolean =
+ a.compareAndSet(expect.toInt(), update.toInt())
+
+ public actual fun getAndSet(value: UInt): UInt {
+ while (true) {
+ val value = value.toInt()
+ val cur = a.value
+ if (cur == value) return cur.toUInt()
+ if (a.compareAndSet(cur, value)) return cur.toUInt()
+ }
+ }
+
+ public actual inline fun getAndIncrement(): UInt = a.addAndGet(1) - 1
+ public actual inline fun getAndDecrement(): UInt = a.addAndGet(-1) + 1
+ public actual inline fun getAndAdd(delta: UInt): UInt = a.addAndGet(delta) - delta
+ public actual inline fun addAndGet(delta: UInt): UInt = a.addAndGet(delta)
+ public actual inline fun incrementAndGet(): UInt = a.addAndGet(1)
+ public actual inline fun decrementAndGet(): UInt = a.addAndGet(-1)
+
+ public actual inline operator fun plusAssign(delta: UInt) { getAndAdd(delta) }
+ public actual inline operator fun minusAssign(delta: UInt) { getAndAdd(-delta) }
+
+ override fun toString(): String = value.toString()
+}
+
// ==================================== AtomicLong ====================================
@Suppress("ACTUAL_WITHOUT_EXPECT")
diff --git a/gradle.properties b/gradle.properties
index a6c4dbc8..f2525559 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,7 +2,7 @@
# Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
#
-version=0.20.2-SNAPSHOT
+version=0.21.0-SNAPSHOT
group=org.jetbrains.kotlinx
kotlin_version=1.8.20