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 @@ [![Kotlin Beta](https://kotl.in/badges/beta.svg)](https://kotlinlang.org/docs/components-stability.html) [![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0) -[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/atomicfu)](https://search.maven.org/artifact/org.jetbrains.kotlinx/atomicfu/0.20.2/pom) +[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/atomicfu)](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