• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..--

atomicfu/23-Nov-2023-3,8812,748

atomicfu-gradle-plugin/23-Nov-2023-1,090849

atomicfu-maven-plugin/23-Nov-2023-560390

atomicfu-native/23-Nov-2023-76

atomicfu-transformer/23-Nov-2023-2,5012,131

gradle/23-Nov-2023-332274

license/23-Nov-2023-2522

.gitignoreD23-Nov-202336 55

Android.bpD23-Nov-20231.5 KiB5551

CHANGES.mdD23-Nov-20236 KiB247152

CODE_OF_CONDUCT.mdD23-Nov-2023269 52

LICENSED23-Nov-2023596 1615

METADATAD23-Nov-2023389 2019

MODULE_LICENSE_APACHE2D23-Nov-20230

NOTICED23-Nov-2023596 1615

OWNERSD23-Nov-202319 21

README.mdD23-Nov-202313.1 KiB341269

RELEASE.mdD23-Nov-20232.4 KiB6950

build.gradleD23-Nov-20234.5 KiB113100

bump-version.shD23-Nov-2023762 3123

gradle.propertiesD23-Nov-2023835 3425

gradlewD23-Nov-20235.2 KiB173128

gradlew.batD23-Nov-20232.1 KiB8561

settings.gradleD23-Nov-2023256 96

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