1# AtomicFU 2 3[](https://kotlinlang.org/docs/components-stability.html) 4[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) 5[](https://www.apache.org/licenses/LICENSE-2.0) 6[](https://search.maven.org/artifact/org.jetbrains.kotlinx/atomicfu/0.23.1/pom) 7 8>Note on Beta status: the plugin is in its active development phase and changes from release to release. 9>We do provide a compatibility of atomicfu-transformed artifacts between releases, but we do not provide 10>strict compatibility guarantees on plugin API and its general stability between Kotlin versions. 11 12**Atomicfu** is a multiplatform library that provides the idiomatic and efficient way of using atomic operations in Kotlin. 13 14## Table of contents 15- [Requirements](#requirements) 16- [Features](#features) 17- [Example](#example) 18- [Quickstart](#quickstart) 19 - [Apply plugin to a project](#apply-plugin) 20 - [Gradle configuration](#gradle-configuration) 21 - [Maven configuration](#maven-configuration) 22- [Usage constraints](#usage-constraints) 23- [Transformation modes](#transformation-modes) 24 - [Atomicfu compiler plugin](#atomicfu-compiler-plugin) 25- [Options for post-compilation transformation](#options-for-post-compilation-transformation) 26 - [JVM options](#jvm-options) 27 - [JS options](#js-options) 28- [More features](#more-features) 29 - [Arrays of atomic values](#arrays-of-atomic-values) 30 - [User-defined extensions on atomics](#user-defined-extensions-on-atomics) 31 - [Locks](#locks) 32 - [Tracing operations](#tracing-operations) 33- [Kotlin/Native support](#kotlin-native-support) 34 35## Requirements 36 37Starting from version `0.23.1` of the library your project is required to use: 38 39* Gradle `7.0` or newer 40 41* Kotlin `1.7.0` or newer 42 43## Features 44 45* Complete multiplatform support: JVM, Native, JS and Wasm (since Kotlin 1.9.20). 46* Code it like a boxed value `atomic(0)`, but run it in production efficiently: 47 * For **JVM**: an atomic value is represented as a plain value atomically updated with `java.util.concurrent.atomic.AtomicXxxFieldUpdater` from the Java standard library. 48 * For **JS**: an atomic value is represented as a plain value. 49 * For **Native**: atomic operations are delegated to Kotlin/Native atomic intrinsics. 50 * For **Wasm**: an atomic value is not transformed, it remains boxed, and `kotlinx-atomicfu` library is used as a runtime dependency. 51* Use Kotlin-specific extensions (e.g. inline `loop`, `update`, `updateAndGet` functions). 52* Use atomic arrays, user-defined extensions on atomics and locks (see [more features](#more-features)). 53* [Tracing operations](#tracing-operations) for debugging. 54 55## Example 56 57Let us declare a `top` variable for a lock-free stack implementation: 58 59```kotlin 60import kotlinx.atomicfu.* // import top-level functions from kotlinx.atomicfu 61 62private val top = atomic<Node?>(null) 63``` 64 65Use `top.value` to perform volatile reads and writes: 66 67```kotlin 68fun isEmpty() = top.value == null // volatile read 69fun clear() { top.value = null } // volatile write 70``` 71 72Use `compareAndSet` function directly: 73 74```kotlin 75if (top.compareAndSet(expect, update)) ... 76``` 77 78Use higher-level looping primitives (inline extensions), for example: 79 80```kotlin 81top.loop { cur -> // while(true) loop that volatile-reads current value 82 ... 83} 84``` 85 86Use high-level `update`, `updateAndGet`, and `getAndUpdate`, 87when possible, for idiomatic lock-free code, for example: 88 89```kotlin 90fun push(v: Value) = top.update { cur -> Node(v, cur) } 91fun pop(): Value? = top.getAndUpdate { cur -> cur?.next } ?.value 92``` 93 94Declare atomic integers and longs using type inference: 95 96```kotlin 97val myInt = atomic(0) // note: integer initial value 98val myLong = atomic(0L) // note: long initial value 99``` 100 101Integer and long atomics provide all the usual `getAndIncrement`, `incrementAndGet`, `getAndAdd`, `addAndGet`, and etc 102operations. They can be also atomically modified via `+=` and `-=` operators. 103 104## Quickstart 105### Apply plugin 106#### Gradle configuration 107 108Gradle configuration is supported for all platforms, minimal version is Gradle 6.8. 109 110In top-level build file: 111 112<details open> 113<summary>Kotlin</summary> 114 115```kotlin 116buildscript { 117 repositories { 118 mavenCentral() 119 } 120 121 dependencies { 122 classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.23.1") 123 } 124} 125 126apply(plugin = "kotlinx-atomicfu") 127``` 128</details> 129 130<details> 131<summary>Groovy</summary> 132 133```groovy 134buildscript { 135 repositories { 136 mavenCentral() 137 } 138 dependencies { 139 classpath 'org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.23.1' 140 } 141} 142 143apply plugin: 'kotlinx-atomicfu' 144``` 145</details> 146 147#### Maven configuration 148 149Maven configuration is supported for JVM projects. 150 151 152<details open> 153<summary>Declare atomicfu version</summary> 154 155```xml 156<properties> 157 <atomicfu.version>0.23.1</atomicfu.version> 158</properties> 159``` 160 161</details> 162 163<details> 164<summary>Declare provided dependency on the AtomicFU library</summary> 165 166```xml 167<dependencies> 168 <dependency> 169 <groupId>org.jetbrains.kotlinx</groupId> 170 <artifactId>atomicfu</artifactId> 171 <version>${atomicfu.version}</version> 172 <scope>provided</scope> 173 </dependency> 174</dependencies> 175``` 176 177</details> 178 179Configure build steps so that Kotlin compiler puts classes into a different `classes-pre-atomicfu` directory, 180which is then transformed to a regular `classes` directory to be used later by tests and delivery. 181 182<details> 183<summary>Build steps</summary> 184 185```xml 186<build> 187 <plugins> 188 <!-- compile Kotlin files to staging directory --> 189 <plugin> 190 <groupId>org.jetbrains.kotlin</groupId> 191 <artifactId>kotlin-maven-plugin</artifactId> 192 <version>${kotlin.version}</version> 193 <executions> 194 <execution> 195 <id>compile</id> 196 <phase>compile</phase> 197 <goals> 198 <goal>compile</goal> 199 </goals> 200 <configuration> 201 <output>${project.build.directory}/classes-pre-atomicfu</output> 202 </configuration> 203 </execution> 204 </executions> 205 </plugin> 206 <!-- transform classes with AtomicFU plugin --> 207 <plugin> 208 <groupId>org.jetbrains.kotlinx</groupId> 209 <artifactId>atomicfu-maven-plugin</artifactId> 210 <version>${atomicfu.version}</version> 211 <executions> 212 <execution> 213 <goals> 214 <goal>transform</goal> 215 </goals> 216 <configuration> 217 <input>${project.build.directory}/classes-pre-atomicfu</input> 218 <!-- "VH" to use Java 9 VarHandle, "BOTH" to produce multi-version code --> 219 <variant>FU</variant> 220 </configuration> 221 </execution> 222 </executions> 223 </plugin> 224 </plugins> 225</build> 226``` 227 228</details> 229 230## Usage constraints 231 232* Declare atomic variables as `private val` or `internal val`. You can use just (public) `val`, 233 but make sure they are not directly accessed outside of your Kotlin module (outside of the source set). 234 Access to the atomic variable itself shall be encapsulated. 235* To expose the value of an atomic property to the public, use a delegated property declared in the same scope 236 (see [atomic delegates](#atomic-delegates) section for details): 237 238```kotlin 239private val _foo = atomic<T>(initial) // private atomic, convention is to name it with leading underscore 240public var foo: T by _foo // public delegated property (val/var) 241``` 242* Only simple operations on atomic variables _directly_ are supported. 243 * Do not read references on atomic variables into local variables, 244 e.g. `top.compareAndSet(...)` is ok, while `val tmp = top; tmp...` is not. 245 * Do not leak references on atomic variables in other way (return, pass as params, etc). 246* Do not introduce complex data flow in parameters to atomic variable operations, 247 i.e. `top.value = complex_expression` and `top.compareAndSet(cur, complex_expression)` are not supported 248 (more specifically, `complex_expression` should not have branches in its compiled representation). 249 Extract `complex_expression` into a variable when needed. 250 251## Atomicfu compiler plugin 252 253To provide a user-friendly atomic API on the frontend and efficient usage of atomic values on the backend kotlinx-atomicfu library uses the compiler plugin to transform 254IR for all the target backends: 255* **JVM**: atomics are replaced with `java.util.concurrent.atomic.AtomicXxxFieldUpdater`. 256* **Native**: atomics are implemented via atomic intrinsics on Kotlin/Native. 257* **JS**: atomics are unboxed and represented as plain values. 258 259To turn on IR transformation set these properties in your `gradle.properties` file: 260 261<details open> 262<summary>For Kotlin >= 1.7.20</summary> 263 264```groovy 265kotlinx.atomicfu.enableJvmIrTransformation=true // for JVM IR transformation 266kotlinx.atomicfu.enableNativeIrTransformation=true // for Native IR transformation 267kotlinx.atomicfu.enableJsIrTransformation=true // for JS IR transformation 268``` 269 270</details> 271 272<details> 273 274 275<summary> For Kotlin >= 1.6.20 and Kotlin < 1.7.20</summary> 276 277```groovy 278kotlinx.atomicfu.enableIrTransformation=true // only JS IR transformation is supported 279``` 280 281</details> 282 283Also for JS backend make sure that `ir` or `both` compiler mode is set: 284 285```groovy 286kotlin.js.compiler=ir // or both 287``` 288 289 290## Options for post-compilation transformation 291 292Some configuration options are available for _post-compilation transform tasks_ on JVM and JS. 293 294To set configuration options you should create `atomicfu` section in a `build.gradle` file, 295like this: 296```groovy 297atomicfu { 298 dependenciesVersion = '0.23.1' 299} 300``` 301 302### JVM options 303 304To turn off transformation for Kotlin/JVM set option `transformJvm` to `false`. 305 306Configuration option `jvmVariant` defines the Java class that replaces atomics during bytecode transformation. 307Here are the valid options: 308- `FU` – atomics are replaced with [AtomicXxxFieldUpdater](https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.html). 309- `VH` – atomics are replaced with [VarHandle](https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html), 310 this option is supported for JDK 9+. 311- `BOTH` – [multi-release jar file](https://openjdk.java.net/jeps/238) will be created with both `AtomicXxxFieldUpdater` for JDK <= 8 and `VarHandle` for JDK 9+. 312 313### JS options 314 315To turn off transformation for Kotlin/JS set option `transformJs` to `false`. 316 317Here are all available configuration options (with their defaults): 318```groovy 319atomicfu { 320 dependenciesVersion = '0.23.1' // set to null to turn-off auto dependencies 321 transformJvm = true // set to false to turn off JVM transformation 322 jvmVariant = "FU" // JVM transformation variant: FU,VH, or BOTH 323 transformJs = true // set to false to turn off JVM transformation 324} 325``` 326 327## More features 328 329AtomicFU provides some additional features that you can use. 330 331### Arrays of atomic values 332 333You can declare arrays of all supported atomic value types. 334By default arrays are transformed into the corresponding `java.util.concurrent.atomic.Atomic*Array` instances. 335 336If you configure `variant = "VH"` an array will be transformed to plain array using 337[VarHandle](https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html) to support atomic operations. 338 339```kotlin 340val a = atomicArrayOfNulls<T>(size) // similar to Array constructor 341 342val x = a[i].value // read value 343a[i].value = x // set value 344a[i].compareAndSet(expect, update) // do atomic operations 345``` 346 347### Atomic delegates 348 349You can expose the value of an atomic property to the public, using a delegated property 350declared in the same scope: 351 352```kotlin 353private val _foo = atomic<T>(initial) // private atomic, convention is to name it with leading underscore 354public var foo: T by _foo // public delegated property (val/var) 355``` 356 357You can also delegate a property to the atomic factory invocation, that is equal to declaring a volatile property: 358 359```kotlin 360public var foo: T by atomic(0) 361``` 362 363This feature is only supported for the IR transformation mode, see the [atomicfu compiler plugin](#atomicfu-compiler-plugin) section for details. 364 365### User-defined extensions on atomics 366 367You can define you own extension functions on `AtomicXxx` types but they must be `inline` and they cannot 368be public and be used outside of the module they are defined in. For example: 369 370```kotlin 371@Suppress("NOTHING_TO_INLINE") 372private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true) 373``` 374 375### Locks 376 377This project includes `kotlinx.atomicfu.locks` package providing multiplatform locking primitives that 378require no additional runtime dependencies on Kotlin/JVM and Kotlin/JS with a library implementation for 379Kotlin/Native. 380 381* `SynchronizedObject` is designed for inheritance. You write `class MyClass : SynchronizedObject()` and then 382use `synchronized(instance) { ... }` extension function similarly to the 383[synchronized](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/synchronized.html) 384function from the standard library that is available for JVM. The `SynchronizedObject` superclass gets erased 385(transformed to `Any`) on JVM and JS, with `synchronized` leaving no trace in the code on JS and getting 386replaced with built-in monitors for locking on JVM. 387 388* `ReentrantLock` is designed for delegation. You write `val lock = reentrantLock()` to construct its instance and 389use `lock`/`tryLock`/`unlock` functions or `lock.withLock { ... }` extension function similarly to the way 390[jucl.ReentrantLock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html) 391is used on JVM. On JVM it is a typealias to the later class, erased on JS. 392 393> Note that package `kotlinx.atomicfu.locks` is experimental explicitly even while atomicfu is experimental itself, 394> meaning that no ABI guarantees are provided whatsoever. API from this package is not recommended to use in libraries 395> that other projects depend on. 396 397### Tracing operations 398 399You can debug your tests tracing atomic operations with a special trace object: 400 401```kotlin 402private val trace = Trace() 403private val current = atomic(0, trace) 404 405fun update(x: Int): Int { 406 // custom trace message 407 trace { "calling update($x)" } 408 // automatic tracing of modification operations 409 return current.getAndAdd(x) 410} 411``` 412 413All trace messages are stored in a cyclic array inside `trace`. 414 415You can optionally set the size of trace's message array and format function. For example, 416you can add a current thread name to the traced messages: 417 418```kotlin 419private val trace = Trace(size = 64) { 420 index, // index of a trace message 421 text // text passed when invoking trace { text } 422 -> "$index: [${Thread.currentThread().name}] $text" 423} 424``` 425 426`trace` is only seen before transformation and completely erased after on Kotlin/JVM and Kotlin/JS. 427 428## Kotlin Native support 429 430Atomic references for Kotlin/Native are based on 431[FreezableAtomicReference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concurrent/-freezable-atomic-reference/-init-.html) 432and every reference that is stored to the previously 433[frozen](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concurrent/freeze.html) 434(shared with another thread) atomic is automatically frozen, too. 435 436Since Kotlin/Native does not generally provide binary compatibility between versions, 437you should use the same version of Kotlin compiler as was used to build AtomicFU. 438See [gradle.properties](gradle.properties) in AtomicFU project for its `kotlin_version`. 439 440Available Kotlin/Native targets are based on non-deprecated official targets [Tier list](https://kotlinlang.org/docs/native-target-support.html) 441 with the corresponding compatibility guarantees. 442