CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlinx--atomicfu-jvm

AtomicFU JVM-specific artifact providing idiomatic and efficient atomic operations optimized for the JVM platform using AtomicXxxFieldUpdater or VarHandle.

Pending
Overview
Eval results
Files

tracing.mddocs/

Tracing and Debugging

Comprehensive tracing system for monitoring and debugging atomic operations in concurrent code, providing detailed operation logs and custom formatting support.

Capabilities

Trace Factory Function

Creates trace objects for monitoring atomic operations with configurable size and formatting.

/**
 * Creates Trace object for tracing atomic operations.
 * @param size - Size of the circular trace buffer (default: 32)
 * @param format - Custom trace formatter (default: traceFormatDefault)
 * @returns TraceBase instance for tracing operations
 */
fun Trace(size: Int = 32, format: TraceFormat = traceFormatDefault): TraceBase

Usage Examples:

import kotlinx.atomicfu.*

class TracedCounter {
    private val trace = Trace(64) // Larger buffer for more history
    private val count = atomic(0, trace.named("count"))
    
    fun increment(): Int {
        trace { "Incrementing counter" }
        return count.incrementAndGet()
    }
    
    fun add(delta: Int): Int {
        trace { "Adding $delta to counter" }
        return count.addAndGet(delta)
    }
    
    fun getTraceHistory(): String = trace.toString()
}

// Usage
val counter = TracedCounter()
counter.increment()
counter.add(5)
println(counter.getTraceHistory())

Named Tracing

Adds meaningful names to traces for better debugging context.

/**
 * Adds a name to the trace for better identification.
 * @param name - Name prefix for all trace messages
 * @returns Named TraceBase instance
 */
fun TraceBase.named(name: String): TraceBase

Usage Examples:

import kotlinx.atomicfu.*

class ConnectionPool {
    private val trace = Trace(128)
    private val activeConnections = atomic(0, trace.named("active"))
    private val totalRequests = atomic(0L, trace.named("requests"))
    
    fun borrowConnection(): Boolean {
        totalRequests.incrementAndGet()
        
        val current = activeConnections.value
        if (current < 10) { // Max 10 connections
            return activeConnections.compareAndSet(current, current + 1)
        }
        
        trace { "Connection pool exhausted, current: $current" }
        return false
    }
    
    fun returnConnection() {
        activeConnections.decrementAndGet()
        trace { "Connection returned to pool" }
    }
    
    fun printTraceHistory() {
        println("Connection Pool Trace:")
        println(trace.toString())
    }
}

TraceBase Class

Base class for all trace implementations providing multiple append methods.

open class TraceBase {
    /** Appends single event to the trace */
    open fun append(event: Any)
    
    /** Appends two events to the trace */
    open fun append(event1: Any, event2: Any)
    
    /** Appends three events to the trace */
    open fun append(event1: Any, event2: Any, event3: Any)
    
    /** Appends four events to the trace */
    open fun append(event1: Any, event2: Any, event3: Any, event4: Any)
    
    /** Functional style event appending */
    inline operator fun invoke(event: () -> Any)
    
    /** NOP tracing singleton */
    object None : TraceBase()
}

Usage Examples:

import kotlinx.atomicfu.*

class MessageProcessor {
    private val trace = Trace(256)
    private val messagesProcessed = atomic(0L)
    private val errors = atomic(0)
    
    fun processMessage(messageId: String, content: String) {
        val startTime = System.currentTimeMillis()
        
        try {
            // Multi-append for garbage-free logging
            trace.append("Processing message", messageId, content.length, Thread.currentThread())
            
            // Simulate processing
            Thread.sleep(10)
            
            messagesProcessed.incrementAndGet()
            val endTime = System.currentTimeMillis()
            
            trace.append("Message processed", messageId, endTime - startTime)
            
        } catch (e: Exception) {
            errors.incrementAndGet()
            trace.append("Message failed", messageId, e.message ?: "Unknown error")
        }
    }
    
    fun getStats(): String {
        return "Processed: ${messagesProcessed.value}, Errors: ${errors.value}"
    }
    
    fun getTrace(): String = trace.toString()
}

TraceFormat Class

Custom trace formatting for specialized logging requirements.

/**
 * Trace string formatter for customizing trace output.
 */
open class TraceFormat {
    /**
     * Formats trace entry with index and event information.
     * @param index - Sequential index of the trace entry
     * @param event - Event object to format
     * @returns Formatted string representation
     */
    open fun format(index: Int, event: Any): String
}

/**
 * Creates trace string formatter with custom format function.
 * @param format - Lambda function for custom formatting
 * @returns TraceFormat instance with custom formatting
 */
inline fun TraceFormat(crossinline format: (index: Int, event: Any) -> String): TraceFormat

Usage Examples:

import kotlinx.atomicfu.*

class CustomTracedService {
    // Custom formatter with timestamp and thread info
    private val customFormat = TraceFormat { index, event ->
        val timestamp = System.currentTimeMillis()
        val threadName = Thread.currentThread().name
        "[$timestamp][$threadName][$index] $event"
    }
    
    private val trace = Trace(100, customFormat)
    private val state = atomic("IDLE", trace.named("state"))
    
    fun start() {
        trace { "Service starting" }
        state.value = "STARTING"
        
        // Simulate startup work
        Thread.sleep(100)
        
        state.value = "RUNNING"
        trace { "Service started successfully" }
    }
    
    fun stop() {
        trace { "Service stopping" }
        state.value = "STOPPING"
        
        // Simulate cleanup work
        Thread.sleep(50)
        
        state.value = "STOPPED"
        trace { "Service stopped" }
    }
    
    fun getDetailedTrace(): String = trace.toString()
}

Default Trace Format

Pre-configured trace formatters with optional thread information.

/**
 * The default trace string formatter.
 * On JVM, when 'kotlinx.atomicfu.trace.thread' system property is set,
 * the default format also includes thread name for each operation.
 */
val traceFormatDefault: TraceFormat

Usage Examples:

import kotlinx.atomicfu.*

// Enable thread names in trace (set system property)
// -Dkotlinx.atomicfu.trace.thread=true

class ThreadAwareService {
    private val trace = Trace() // Uses traceFormatDefault
    private val workItems = atomic(0, trace.named("work"))
    
    fun doWork() {
        trace { "Starting work on ${Thread.currentThread().name}" }
        workItems.incrementAndGet()
        
        // Simulate work
        Thread.sleep(50)
        
        trace { "Work completed" }
    }
    
    fun printTrace() {
        println("Service Trace (with thread info if enabled):")
        println(trace.toString())
    }
}

// Usage with multiple threads
val service = ThreadAwareService()

// Create multiple worker threads
repeat(3) { threadId ->
    Thread {
        repeat(2) {
            service.doWork()
        }
    }.apply {
        name = "Worker-$threadId"
        start()
    }
}

Advanced Tracing Patterns

Conditional Tracing

Tracing that can be enabled/disabled at runtime:

import kotlinx.atomicfu.*

class ConditionalTracer {
    private val debugEnabled = atomic(false)
    private val trace = Trace(512)
    private val operations = atomic(0L)
    
    fun enableDebug() = debugEnabled.compareAndSet(false, true)
    fun disableDebug() = debugEnabled.compareAndSet(true, false)
    
    private fun debugTrace(event: () -> Any) {
        if (debugEnabled.value) {
            trace(event)
        }
    }
    
    fun performOperation(operationName: String) {
        debugTrace { "Starting operation: $operationName" }
        
        operations.incrementAndGet()
        
        // Simulate work
        Thread.sleep(10)
        
        debugTrace { "Completed operation: $operationName" }
    }
    
    fun getTrace(): String = if (debugEnabled.value) trace.toString() else "Tracing disabled"
}

Performance Monitoring

Using traces for performance analysis:

import kotlinx.atomicfu.*

class PerformanceTracer {
    private val performanceFormat = TraceFormat { index, event ->
        val memoryUsage = Runtime.getRuntime().let { rt ->
            (rt.totalMemory() - rt.freeMemory()) / 1024 / 1024
        }
        "[$index][${System.nanoTime()}][${memoryUsage}MB] $event"
    }
    
    private val trace = Trace(1000, performanceFormat)
    private val operationCount = atomic(0L, trace.named("ops"))
    
    fun timedOperation(name: String, block: () -> Unit) {
        val startTime = System.nanoTime()
        trace { "START: $name" }
        
        try {
            block()
            operationCount.incrementAndGet()
        } finally {
            val duration = (System.nanoTime() - startTime) / 1_000_000 // Convert to ms
            trace { "END: $name (${duration}ms)" }
        }
    }
    
    fun getPerformanceReport(): String = trace.toString()
}

Implementation Notes

Trace Buffer Management

  • Traces use circular buffers with configurable size
  • Older entries are overwritten when buffer is full
  • Buffer size should be power of 2 for optimal performance
  • Larger buffers provide more history but use more memory

Thread Safety

  • All trace operations are thread-safe
  • Multiple threads can append to the same trace concurrently
  • Trace formatting is applied during toString() call
  • No synchronization overhead during trace appending

Performance Considerations

  • Trace operations have minimal overhead when tracing is disabled (TraceBase.None)
  • Lambda-based tracing (trace { ... }) only evaluates when tracing is enabled
  • Multi-append methods are garbage-free for performance-critical code
  • Custom formatters are only called during trace output, not during appending

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlinx--atomicfu-jvm

docs

atomic-arrays.md

atomic-operations.md

index.md

locks.md

thread-parking.md

tracing.md

tile.json