Atomicfu is a multiplatform Kotlin compiler plugin and library for atomic operations across JVM, Native, JS, and Wasm platforms
—
Core atomic operations for thread-safe programming with compare-and-swap, arithmetic operations, and high-level update patterns.
Creates atomic wrappers for different primitive and reference types.
/**
* Creates an atomic integer wrapper
* @param value - Initial integer value
* @returns AtomicInt instance for atomic integer operations
*/
fun atomic(value: Int): AtomicInt
/**
* Creates an atomic long wrapper
* @param value - Initial long value
* @returns AtomicLong instance for atomic long operations
*/
fun atomic(value: Long): AtomicLong
/**
* Creates an atomic boolean wrapper
* @param value - Initial boolean value
* @returns AtomicBoolean instance for atomic boolean operations
*/
fun atomic(value: Boolean): AtomicBoolean
/**
* Creates an atomic reference wrapper for any type
* @param value - Initial reference value
* @returns AtomicRef<T> instance for atomic reference operations
*/
fun <T> atomic(value: T): AtomicRef<T>
/**
* Creates an atomic integer wrapper with tracing support
* @param value - Initial integer value
* @param trace - Trace instance for debugging atomic operations
* @returns AtomicInt instance for atomic integer operations with tracing
*/
fun atomic(value: Int, trace: Trace): AtomicInt
/**
* Creates an atomic long wrapper with tracing support
* @param value - Initial long value
* @param trace - Trace instance for debugging atomic operations
* @returns AtomicLong instance for atomic long operations with tracing
*/
fun atomic(value: Long, trace: Trace): AtomicLong
/**
* Creates an atomic boolean wrapper with tracing support
* @param value - Initial boolean value
* @param trace - Trace instance for debugging atomic operations
* @returns AtomicBoolean instance for atomic boolean operations with tracing
*/
fun atomic(value: Boolean, trace: Trace): AtomicBoolean
/**
* Creates an atomic reference wrapper with tracing support
* @param value - Initial reference value
* @param trace - Trace instance for debugging atomic operations
* @returns AtomicRef<T> instance for atomic reference operations with tracing
*/
fun <T> atomic(value: T, trace: Trace): AtomicRef<T>Usage Examples:
import kotlinx.atomicfu.*
// Create atomic values
val counter = atomic(0)
val flag = atomic(false)
val name = atomic<String?>(null)
val obj = atomic(MyObject())Atomic operations for integer values with arithmetic support.
interface AtomicInt {
/** Current integer value */
var value: Int
/** Atomically sets the value with lazy semantics */
fun lazySet(newValue: Int): Unit
/** Atomically compares current value with expected and sets to update if equal */
fun compareAndSet(expect: Int, update: Int): Boolean
/** Atomically sets to newValue and returns the old value */
fun getAndSet(newValue: Int): Int
/** Atomically increments and returns the old value */
fun getAndIncrement(): Int
/** Atomically increments and returns the new value */
fun incrementAndGet(): Int
/** Atomically decrements and returns the old value */
fun getAndDecrement(): Int
/** Atomically decrements and returns the new value */
fun decrementAndGet(): Int
/** Atomically adds delta and returns the old value */
fun getAndAdd(delta: Int): Int
/** Atomically adds delta and returns the new value */
fun addAndGet(delta: Int): Int
/** Loops until the action completes successfully */
fun loop(action: (Int) -> Unit): Nothing
/** Updates the value using the transform function */
fun update(function: (Int) -> Int): Unit
/** Updates the value and returns the new value */
fun updateAndGet(function: (Int) -> Int): Int
/** Updates the value and returns the old value */
fun getAndUpdate(function: (Int) -> Int): Int
}Usage Examples:
val counter = atomic(0)
// Basic operations
counter.value = 5
counter.lazySet(10)
counter.compareAndSet(10, 20) // true
// Arithmetic operations
val oldValue = counter.getAndIncrement() // returns 20, sets to 21
val newValue = counter.incrementAndGet() // returns 22
// High-level operations
counter.update { it * 2 } // doubles the current value
val result = counter.updateAndGet { it + 5 } // adds 5 and returns new valueAtomic operations for long values with arithmetic support.
interface AtomicLong {
/** Current long value */
var value: Long
/** Atomically sets the value with lazy semantics */
fun lazySet(newValue: Long): Unit
/** Atomically compares current value with expected and sets to update if equal */
fun compareAndSet(expect: Long, update: Long): Boolean
/** Atomically sets to newValue and returns the old value */
fun getAndSet(newValue: Long): Long
/** Atomically increments and returns the old value */
fun getAndIncrement(): Long
/** Atomically increments and returns the new value */
fun incrementAndGet(): Long
/** Atomically decrements and returns the old value */
fun getAndDecrement(): Long
/** Atomically decrements and returns the new value */
fun decrementAndGet(): Long
/** Atomically adds delta and returns the old value */
fun getAndAdd(delta: Long): Long
/** Atomically adds delta and returns the new value */
fun addAndGet(delta: Long): Long
/** Loops until the action completes successfully */
fun loop(action: (Long) -> Unit): Nothing
/** Updates the value using the transform function */
fun update(function: (Long) -> Long): Unit
/** Updates the value and returns the new value */
fun updateAndGet(function: (Long) -> Long): Long
/** Updates the value and returns the old value */
fun getAndUpdate(function: (Long) -> Long): Long
}Usage Examples:
val largeCounter = atomic(5000000000L)
// Large number operations
largeCounter.addAndGet(1000000000L)
largeCounter.update { value ->
if (value > Long.MAX_VALUE / 2) Long.MAX_VALUE else value * 2
}Atomic operations for boolean values.
interface AtomicBoolean {
/** Current boolean value */
var value: Boolean
/** Atomically sets the value with lazy semantics */
fun lazySet(newValue: Boolean): Unit
/** Atomically compares current value with expected and sets to update if equal */
fun compareAndSet(expect: Boolean, update: Boolean): Boolean
/** Atomically sets to newValue and returns the old value */
fun getAndSet(newValue: Boolean): Boolean
/** Loops until the action completes successfully */
fun loop(action: (Boolean) -> Unit): Nothing
/** Updates the value using the transform function */
fun update(function: (Boolean) -> Boolean): Unit
/** Updates the value and returns the new value */
fun updateAndGet(function: (Boolean) -> Boolean): Boolean
/** Updates the value and returns the old value */
fun getAndUpdate(function: (Boolean) -> Boolean): Boolean
}Usage Examples:
val flag = atomic(false)
// Toggle operations
val wasSet = flag.getAndSet(true) // returns false, sets to true
flag.update { !it } // toggles the value
// Conditional operations
if (flag.compareAndSet(false, true)) {
// Flag was false and is now set to true
performAction()
}Atomic operations for reference types.
interface AtomicRef<T> {
/** Current reference value */
var value: T
/** Atomically sets the value with lazy semantics */
fun lazySet(newValue: T): Unit
/** Atomically compares current value with expected and sets to update if equal */
fun compareAndSet(expect: T, update: T): Boolean
/** Atomically sets to newValue and returns the old value */
fun getAndSet(newValue: T): T
/** Loops until the action completes successfully */
fun loop(action: (T) -> Unit): Nothing
/** Updates the value using the transform function */
fun update(function: (T) -> T): Unit
/** Updates the value and returns the new value */
fun updateAndGet(function: (T) -> T): T
/** Updates the value and returns the old value */
fun getAndUpdate(function: (T) -> T): T
}Usage Examples:
data class Node(val value: Int, val next: Node?)
val head = atomic<Node?>(null)
// Atomic reference operations
val oldHead = head.getAndSet(Node(1, null))
head.update { current -> Node(2, current) } // prepend to list
// Lock-free data structure example
fun push(item: Int) {
head.update { current -> Node(item, current) }
}
fun pop(): Int? {
return head.updateAndGet { current ->
current?.next
}?.value
}Debugging and tracing support for atomic operations.
/**
* Trace class for debugging atomic operations
*/
class Trace {
/** Creates a default trace */
constructor()
/** Creates a trace with specified capacity */
constructor(capacity: Int)
/** Creates a trace with custom format */
constructor(format: TraceFormat)
/** Creates a trace with capacity and format */
constructor(capacity: Int, format: TraceFormat)
/** Creates a named trace for better identification */
fun named(name: String): Trace
/** Appends a trace message */
fun append(message: String)
/** Appends a trace message with value */
fun append(message: String, value: Any)
/** Appends a trace message with ID and value */
fun append(id: Any, value: Any)
/** Appends a trace message with ID, value, and description */
fun append(id: Any, value: Any, description: String)
/** Function call operator for tracing */
operator fun invoke(message: () -> String)
}
/**
* Trace format for customizing trace output
*/
class TraceFormat(val formatter: (id: Any, text: String) -> String)Usage Examples:
// Basic tracing
val trace = Trace()
val counter = atomic(0, trace)
trace { "Initial value: ${counter.value}" }
counter.incrementAndGet()
trace { "After increment: ${counter.value}" }
// Named trace
val namedTrace = Trace().named("worker-1")
val workerCounter = atomic(0, namedTrace)
// Custom format
val customTrace = Trace(format = TraceFormat { id, text -> "[$id]: $text" })
val formattedCounter = atomic(0, customTrace)
// Trace with capacity
val limitedTrace = Trace(10) // Only keeps last 10 entriesAtomic values can be used as delegated properties for seamless integration.
/**
* Atomic values support property delegation
* Allows using atomic values as backing properties with automatic get/set delegation
*/
val atomicValue = atomic(initialValue)
var property: T by atomicValueUsage Examples:
// Delegated property with atomic backing
private val _counter = atomic(0)
var counter: Int by _counter
// Usage is transparent
counter = 5 // Calls _counter.value = 5
val value = counter // Calls _counter.value
// Object-level delegated properties
object GlobalState {
private val _isEnabled = atomic(false)
var isEnabled: Boolean by _isEnabled
private val _name = atomic<String?>(null)
var name: String? by _name
}
// Class-level delegated properties
class Configuration {
var timeout: Int by atomic(30)
var retryCount: Int by atomic(3)
var isDebugMode: Boolean by atomic(false)
}
// Top-level delegated properties
private val _globalCounter = atomic(0)
var globalCounter: Int by _globalCounter
### Usage Constraints and Best Practices
Atomicfu has specific requirements and constraints for proper usage and compiler transformation.
#### **Visibility Constraints**
Atomic properties must follow specific visibility rules for compiler transformation to work correctly.
```kotlin
// ✅ Correct: Private atomic properties
class Counter {
private val count = atomic(0)
fun increment() = count.incrementAndGet()
fun get() = count.value
}
// ✅ Correct: Internal atomic properties
class InternalCounter {
internal val count = atomic(0)
}
// ❌ Incorrect: Public atomic properties are forbidden
class PublicCounter {
val count = atomic(0) // Compiler error: PUBLIC_ATOMICS_ARE_FORBIDDEN
}
// ❌ Incorrect: @PublishedApi atomic properties are forbidden
class ApiCounter {
@PublishedApi
internal val count = atomic(0) // Compiler error: PUBLISHED_API_ATOMICS_ARE_FORBIDDEN
}Atomic properties should be declared as val, not var, for proper transformation.
// ✅ Correct: val declaration
class Counter {
private val count = atomic(0)
}
// ❌ Incorrect: var declaration
class MutableCounter {
private var count = atomic(0) // Compiler error: ATOMIC_PROPERTIES_SHOULD_BE_VAL
}Atomic operations must be called directly on atomic properties, not through intermediate variables.
class Counter {
private val count = atomic(0)
// ✅ Correct: Direct invocation
fun increment() = count.incrementAndGet()
// ❌ Incorrect: Local variable assignment
fun incrementWrong() {
val localRef = count // This breaks transformation
return localRef.incrementAndGet()
}
// ✅ Correct: Simple expressions in parameters
fun addValue(delta: Int) = count.addAndGet(delta)
// ❌ Incorrect: Complex expressions in atomic operations
fun complexOperation() {
count.compareAndSet(
computeExpected(), // Complex expression - avoid
computeNew() // Complex expression - avoid
)
}
}Different platforms have different transformation behaviors and performance characteristics.
JVM Platform:
java.util.concurrent.atomic.* classesJavaScript Platform:
atomicfu_* runtime functions for operationsNative Platform:
WebAssembly Platform:
The compiler provides specific error messages for improper usage:
PUBLIC_ATOMICS_ARE_FORBIDDEN: Atomic properties cannot be publicPUBLISHED_API_ATOMICS_ARE_FORBIDDEN: Atomic properties cannot use @PublishedApiATOMIC_PROPERTIES_SHOULD_BE_VAL: Atomic properties should be declared as valPattern 1: Encapsulated Atomic State
class ThreadSafeCounter {
private val _count = atomic(0)
val count: Int get() = _count.value
fun increment(): Int = _count.incrementAndGet()
fun decrement(): Int = _count.decrementAndGet()
}Pattern 2: Delegated Properties for Public Access
class PublicCounter {
private val _count = atomic(0)
var count: Int by _count // Public access through delegation
fun atomicIncrement() = _count.incrementAndGet()
}Pattern 3: Lock-Free Data Structures
class LockFreeStack<T> {
private val top = atomic<Node<T>?>(null)
private data class Node<T>(val value: T, val next: Node<T>?)
fun push(value: T) {
top.update { current -> Node(value, current) }
}
fun pop(): T? {
return top.updateAndGet { current -> current?.next }?.value
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-atomicfu-compiler-plugin