CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlin--kotlin-atomicfu-compiler-plugin

Atomicfu is a multiplatform Kotlin compiler plugin and library for atomic operations across JVM, Native, JS, and Wasm platforms

Pending
Overview
Eval results
Files

atomic-operations.mddocs/

Atomic Operations

Core atomic operations for thread-safe programming with compare-and-swap, arithmetic operations, and high-level update patterns.

Capabilities

Factory Functions

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())

AtomicInt Operations

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 value

AtomicLong Operations

Atomic 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
}

AtomicBoolean Operations

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()
}

AtomicRef Operations

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
}

Trace API

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 entries

Delegated Properties

Atomic 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 atomicValue

Usage 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
}

Property Declaration Constraints

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
}

Usage Pattern Constraints

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
        )
    }
}

Platform-Specific Behavior

Different platforms have different transformation behaviors and performance characteristics.

JVM Platform:

  • Transforms to java.util.concurrent.atomic.* classes
  • Best performance due to native JVM atomic support
  • Full feature support including all operations

JavaScript Platform:

  • Transforms to runtime functions with lock-free algorithms
  • Uses atomicfu_* runtime functions for operations
  • Good performance but not as optimal as JVM

Native Platform:

  • Uses Kotlin/Native atomic intrinsics
  • Direct memory operations for best native performance
  • Platform-specific atomic implementations

WebAssembly Platform:

  • Runtime implementations adapted for Wasm
  • Performance depends on Wasm runtime capabilities

Diagnostic Messages

The compiler provides specific error messages for improper usage:

  • PUBLIC_ATOMICS_ARE_FORBIDDEN: Atomic properties cannot be public
  • PUBLISHED_API_ATOMICS_ARE_FORBIDDEN: Atomic properties cannot use @PublishedApi
  • ATOMIC_PROPERTIES_SHOULD_BE_VAL: Atomic properties should be declared as val

Recommended Patterns

Pattern 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

docs

atomic-arrays.md

atomic-operations.md

index.md

lock-free-synchronization.md

plugin-configuration.md

tile.json