• Home
Name Date Size #Lines LOC

..--

atomicfu/03-May-2024-5,3333,764

atomicfu-gradle-plugin/03-May-2024-1,113870

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

atomicfu-native/03-May-2024-86

atomicfu-transformer/03-May-2024-2,9422,517

buildSrc/03-May-2024-11488

gradle/03-May-2024-312257

license/03-May-2024-2522

.gitignoreD03-May-202436 55

Android.bpD03-May-20241.1 KiB4037

CHANGES.mdD03-May-20246.6 KiB272169

CODE_OF_CONDUCT.mdD03-May-2024269 52

LICENSED03-May-2024596 1615

METADATAD03-May-2024389 2019

MODULE_LICENSE_APACHE2D03-May-20240

NOTICED03-May-2024596 1615

OWNERSD03-May-202419 21

README.mdD03-May-202414.1 KiB372291

RELEASE.mdD03-May-20242.3 KiB6949

build.gradleD03-May-20245.2 KiB122109

bump-version.shD03-May-2024762 3123

gradle.propertiesD03-May-2024866 3526

gradlewD03-May-20245.2 KiB173128

gradlew.batD03-May-20242.1 KiB8561

settings.gradleD03-May-2024255 86

README.md

1# AtomicFU
2
3[![JetBrains incubator project](https://jb.gg/badges/incubator.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
4[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0)
5[![Download](https://api.bintray.com/packages/kotlin/kotlinx/kotlinx.atomicfu/images/download.svg) ](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