• Home
Name Date Size #Lines LOC

..--

atomicfu/03-May-2024-4,7933,342

atomicfu-gradle-plugin/03-May-2024-1,6181,251

atomicfu-maven-plugin/03-May-2024-551383

atomicfu-native/03-May-2024-86

atomicfu-transformer/03-May-2024-3,0532,603

buildSrc/03-May-2024-10076

gradle/03-May-2024-302248

license/03-May-2024-2522

.gitignoreD03-May-202436 55

Android.bpD03-May-20241.1 KiB4037

CHANGES.mdD03-May-20248.5 KiB342215

CODE_OF_CONDUCT.mdD03-May-2024269 52

LICENSED03-May-2024596 1615

METADATAD03-May-2024625 2422

MODULE_LICENSE_APACHE2D03-May-20240

NOTICED03-May-2024596 1615

OWNERSD03-May-202419 21

README.mdD03-May-202414.3 KiB416315

RELEASE.mdD03-May-20242.3 KiB6949

build.gradleD03-May-20244.2 KiB10491

bump-version.shD03-May-2024762 3123

gradle.propertiesD03-May-2024867 3526

gradlewD03-May-20245.2 KiB173128

gradlew.batD03-May-20242.1 KiB8561

settings.gradleD03-May-2024255 86

README.md

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