1# AtomicFU 2 3[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) 4[](https://www.apache.org/licenses/LICENSE-2.0) 5[ ](https://bintray.com/kotlin/kotlinx/kotlinx.atomicfu/_latestVersion) 6 7The idiomatic way to use atomic operations in Kotlin. 8 9* Code it like `AtomicReference/Int/Long`, but run it in production efficiently as `AtomicXxxFieldUpdater` on Kotlin/JVM 10 and as plain unboxed values on Kotlin/JS. 11* Use Kotlin-specific extensions (e.g. inline `updateAndGet` and `getAndUpdate` functions). 12* Compile-time dependency only (no runtime dependencies). 13 * Post-compilation bytecode transformer that declares all the relevant field updaters for you on [Kotlin/JVM](#jvm). 14 * Post-compilation JavaScript files transformer on [Kotlin/JS](#js). 15* Multiplatform: 16 * [Kotlin/Native](#native) is supported. 17 * However, Kotlin/Native works as library dependency at the moment (unlike Kotlin/JVM and Kotlin/JS). 18 * This enables writing [common](#common) Kotlin code with atomics that compiles for JVM, JS, and Native. 19* [Gradle](#gradle-build-setup) for all platforms and [Maven](#maven-build-setup) for JVM are supported. 20* [Additional features](#additional-features) include: 21 * [JDK9 VarHandle](#varhandles-with-java-9). 22 * [Arrays of atomic values](#arrays-of-atomic-values). 23 * [User-defined extensions on atomics](#user-defined-extensions-on-atomics) 24 * [Locks](#locks) 25 * [Testing of lock-free data structures](#testing-lock-free-data-structures-on-jvm). 26 * [Tracing operations](#tracing-operations) 27 28## Example 29 30Let us declare a `top` variable for a lock-free stack implementation: 31 32```kotlin 33import kotlinx.atomicfu.* // import top-level functions from kotlinx.atomicfu 34 35private val top = atomic<Node?>(null) 36``` 37 38Use `top.value` to perform volatile reads and writes: 39 40```kotlin 41fun isEmpty() = top.value == null // volatile read 42fun clear() { top.value = null } // volatile write 43``` 44 45Use `compareAndSet` function directly: 46 47```kotlin 48if (top.compareAndSet(expect, update)) ... 49``` 50 51Use higher-level looping primitives (inline extensions), for example: 52 53```kotlin 54top.loop { cur -> // while(true) loop that volatile-reads current value 55 ... 56} 57``` 58 59Use high-level `update`, `updateAndGet`, and `getAndUpdate`, 60when possible, for idiomatic lock-free code, for example: 61 62```kotlin 63fun push(v: Value) = top.update { cur -> Node(v, cur) } 64fun pop(): Value? = top.getAndUpdate { cur -> cur?.next } ?.value 65``` 66 67Declare atomic integers and longs using type inference: 68 69```kotlin 70val myInt = atomic(0) // note: integer initial value 71val myLong = atomic(0L) // note: long initial value 72``` 73 74Integer and long atomics provide all the usual `getAndIncrement`, `incrementAndGet`, `getAndAdd`, `addAndGet`, and etc 75operations. They can be also atomically modified via `+=` and `-=` operators. 76 77## Dos and Don'ts 78 79* Declare atomic variables as `private val` or `internal val`. You can use just (public) `val`, 80 but make sure they are not directly accessed outside of your Kotlin module (outside of the source set). 81 Access to the atomic variable itself shall be encapsulated. 82* Only simple operations on atomic variables _directly_ are supported. 83 * Do not read references on atomic variables into local variables, 84 e.g. `top.compareAndSet(...)` is Ok, while `val tmp = top; tmp...` is not. 85 * Do not leak references on atomic variables in other way (return, pass as params, etc). 86* Do not introduce complex data flow in parameters to atomic variable operations, 87 i.e. `top.value = complex_expression` and `top.compareAndSet(cur, complex_expression)` are not supported 88 (more specifically, `complex_expression` should not have branches in its compiled representation). 89 Extract `complex_expression` into a variable when needed. 90* Use the following convention if you need to expose the value of atomic property to the public: 91 92```kotlin 93private val _foo = atomic<T>(initial) // private atomic, convention is to name it with leading underscore 94public var foo: T by _foo // public delegated property (val/var) 95``` 96 97## Gradle build setup 98 99Building with Gradle is supported for all platforms. 100 101### JVM 102 103You will need Gradle 4.10 or later. 104Add and apply AtomicFU plugin. It adds all the corresponding dependencies 105and transformations automatically. 106See [additional configuration](#additional-configuration) if that needs tweaking. 107 108```groovy 109buildscript { 110 ext.atomicfu_version = '0.16.0' 111 112 dependencies { 113 classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version" 114 } 115} 116 117apply plugin: 'kotlinx-atomicfu' 118``` 119 120### JS 121 122Configure add apply plugin just like for [JVM](#jvm). 123 124### Native 125 126This library is available for Kotlin/Native (`atomicfu-native`). 127Kotlin/Native uses Gradle metadata and needs Gradle version 5.3 or later. 128See [Gradle Metadata 1.0 announcement](https://blog.gradle.org/gradle-metadata-1.0) for more details. 129Apply the corresponding plugin just like for [JVM](#jvm). 130 131Atomic references for Kotlin/Native are based on 132[FreezableAtomicReference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concurrent/-freezable-atomic-reference/-init-.html) 133and every reference that is stored to the previously 134[frozen](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concurrent/freeze.html) 135(shared with another thread) atomic is automatically frozen, too. 136 137Since Kotlin/Native does not generally provide binary compatibility between versions, 138you should use the same version of Kotlin compiler as was used to build AtomicFU. 139See [gradle.properties](gradle.properties) in AtomicFU project for its `kotlin_version`. 140 141### Common 142 143If you write a common code that should get compiled or different platforms, add `org.jetbrains.kotlinx:atomicfu-common` 144to your common code dependencies or apply `kotlinx-atomicfu` plugin that adds this dependency automatically: 145 146```groovy 147dependencies { 148 compile "org.jetbrains.kotlinx:atomicfu-common:$atomicfu_version" 149} 150``` 151 152### Additional configuration 153 154There are the following additional parameters (with their defaults): 155 156```groovy 157atomicfu { 158 dependenciesVersion = '0.16.0' // set to null to turn-off auto dependencies 159 transformJvm = true // set to false to turn off JVM transformation 160 transformJs = true // set to false to turn off JS transformation 161 variant = "FU" // JVM transformation variant: FU,VH, or BOTH 162 verbose = false // set to true to be more verbose 163} 164``` 165 166## Maven build setup 167 168Declare AtomicFU version: 169 170```xml 171<properties> 172 <atomicfu.version>0.16.0</atomicfu.version> 173</properties> 174``` 175 176Declare _provided_ dependency on the AtomicFU library 177(the users of the resulting artifact will not have a dependency on AtomicFU library): 178 179```xml 180<dependencies> 181 <dependency> 182 <groupId>org.jetbrains.kotlinx</groupId> 183 <artifactId>atomicfu</artifactId> 184 <version>${atomicfu.version}</version> 185 <scope>provided</scope> 186 </dependency> 187</dependencies> 188``` 189 190Configure build steps so that Kotlin compiler puts classes into a different `classes-pre-atomicfu` directory, 191which is then transformed to a regular `classes` directory to be used later by tests and delivery. 192 193```xml 194<build> 195 <plugins> 196 <!-- compile Kotlin files to staging directory --> 197 <plugin> 198 <groupId>org.jetbrains.kotlin</groupId> 199 <artifactId>kotlin-maven-plugin</artifactId> 200 <version>${kotlin.version}</version> 201 <executions> 202 <execution> 203 <id>compile</id> 204 <phase>compile</phase> 205 <goals> 206 <goal>compile</goal> 207 </goals> 208 <configuration> 209 <output>${project.build.directory}/classes-pre-atomicfu</output> 210 <!-- "VH" to use Java 9 VarHandle, "BOTH" to produce multi-version code --> 211 <variant>FU</variant> 212 </configuration> 213 </execution> 214 </executions> 215 </plugin> 216 <!-- transform classes with AtomicFU plugin --> 217 <plugin> 218 <groupId>org.jetbrains.kotlinx</groupId> 219 <artifactId>atomicfu-maven-plugin</artifactId> 220 <version>${atomicfu.version}</version> 221 <executions> 222 <execution> 223 <goals> 224 <goal>transform</goal> 225 </goals> 226 <configuration> 227 <input>${project.build.directory}/classes-pre-atomicfu</input> 228 </configuration> 229 </execution> 230 </executions> 231 </plugin> 232 </plugins> 233</build> 234``` 235 236## Additional features 237 238AtomicFU provides some additional features that you can optionally use. 239 240### VarHandles with Java 9 241 242AtomicFU can produce code that uses Java 9 243[VarHandle](https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html) 244instead of `AtomicXxxFieldUpdater`. Configure transformation `variant` in Gradle build file: 245 246```groovy 247atomicfu { 248 variant = "VH" 249} 250``` 251 252It can also create [JEP 238](https://openjdk.java.net/jeps/238) multi-release jar file with both 253`AtomicXxxFieldUpdater` for JDK<=8 and `VarHandle` for for JDK9+ if you 254set `variant` to `"BOTH"`. 255 256### Arrays of atomic values 257 258You can declare arrays of all supported atomic value types. 259By default arrays are transformed into the corresponding `java.util.concurrent.atomic.Atomic*Array` instances. 260 261If you configure `variant = "VH"` an array will be transformed to plain array using 262[VarHandle](https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html) to support atomic operations. 263 264```kotlin 265val a = atomicArrayOfNulls<T>(size) // similar to Array constructor 266 267val x = a[i].value // read value 268a[i].value = x // set value 269a[i].compareAndSet(expect, update) // do atomic operations 270``` 271 272### User-defined extensions on atomics 273 274You can define you own extension functions on `AtomicXxx` types but they must be `inline` and they cannot 275be public and be used outside of the module they are defined in. For example: 276 277```kotlin 278@Suppress("NOTHING_TO_INLINE") 279private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true) 280``` 281 282### Locks 283 284This project includes `kotlinx.atomicfu.locks` package providing multiplatform locking primitives that 285require no additional runtime dependencies on Kotlin/JVM and Kotlin/JS with a library implementation for 286Kotlin/Native. 287 288* `SynchronizedObject` is designed for inheritance. You write `class MyClass : SynchronizedObject()` and then 289use `synchronized(instance) { ... }` extension function similarly to the 290[synchronized](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/synchronized.html) 291function from the standard library that is available for JVM. The `SynchronizedObject` superclass gets erased 292(transformed to `Any`) on JVM and JS, with `synchronized` leaving no trace in the code on JS and getting 293replaced with built-in monitors for locking on JVM. 294 295* `ReentrantLock` is designed for delegation. You write `val lock = reentrantLock()` to construct its instance and 296use `lock`/`tryLock`/`unlock` functions or `lock.withLock { ... }` extension function similarly to the way 297[jucl.ReentrantLock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html) 298is used on JVM. On JVM it is a typealias to the later class, erased on JS. 299 300Condition variables (`notify`/`wait` and `signal`/`await`) are not supported. 301 302### Testing lock-free data structures on JVM 303 304You can optionally test lock-freedomness of lock-free data structures using 305[`LockFreedomTestEnvironment`](atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/LockFreedomTestEnvironment.kt) class. 306See example in [`LockFreeQueueLFTest`](atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/LockFreeQueueLFTest.kt). 307Testing is performed by pausing one (random) thread before or after a random state-update operation and 308making sure that all other threads can still make progress. 309 310In order to make those test to actually perform lock-freedomness testing you need to configure an additional 311execution of tests with the original (non-transformed) classes for Maven: 312 313```xml 314<build> 315 <plugins> 316 <!-- additional test execution with surefire on non-transformed files --> 317 <plugin> 318 <artifactId>maven-surefire-plugin</artifactId> 319 <executions> 320 <execution> 321 <id>lockfree-test</id> 322 <phase>test</phase> 323 <goals> 324 <goal>test</goal> 325 </goals> 326 <configuration> 327 <classesDirectory>${project.build.directory}/classes-pre-atomicfu</classesDirectory> 328 <includes> 329 <include>**/*LFTest.*</include> 330 </includes> 331 </configuration> 332 </execution> 333 </executions> 334 </plugin> 335 </plugins> 336</build> 337``` 338 339For Gradle there is nothing else to add. Tests are always run using original (non-transformed) classes. 340 341### Tracing operations 342 343You can debug your tests tracing atomic operations with a special trace object: 344 345```kotlin 346private val trace = Trace() 347private val current = atomic(0, trace) 348 349fun update(x: Int): Int { 350 // custom trace message 351 trace { "calling update($x)" } 352 // automatic tracing of modification operations 353 return current.getAndAdd(x) 354} 355``` 356 357All trace messages are stored in a cyclic array inside `trace`. 358 359You can optionally set the size of trace's message array and format function. For example, 360you can add a current thread name to the traced messages: 361 362```kotlin 363private val trace = Trace(size = 64) { 364 index, // index of a trace message 365 text // text passed when invoking trace { text } 366 -> "$index: [${Thread.currentThread().name}] $text" 367} 368``` 369 370`trace` is only seen before transformation and completely erased after on Kotlin/JVM and Kotlin/JS. 371 372