AtomicFU JVM-specific artifact providing idiomatic and efficient atomic operations optimized for the JVM platform using AtomicXxxFieldUpdater or VarHandle.
—
Comprehensive tracing system for monitoring and debugging atomic operations in concurrent code, providing detailed operation logs and custom formatting support.
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): TraceBaseUsage 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())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): TraceBaseUsage 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())
}
}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()
}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): TraceFormatUsage 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()
}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: TraceFormatUsage 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()
}
}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"
}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()
}trace { ... }) only evaluates when tracing is enabledInstall with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlinx--atomicfu-jvm