From 13bd492e32b4ee617ce035530236d149e7acfa27 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 30 Mar 2026 17:48:58 +0000 Subject: [PATCH] refactor: replace synchronized with lock-free CAS in BleChunkAssembler Use AtomicReference holding an immutable list with a compare-and-set loop, eliminating the synchronized block while maintaining thread safety. https://claude.ai/code/session_01KLRz7FJgWRtgCcZe3qD1VX --- .../nipBEBle/protocol/BleChunkAssembler.kt | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipBEBle/protocol/BleChunkAssembler.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipBEBle/protocol/BleChunkAssembler.kt index dfd154708..c27f465f5 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipBEBle/protocol/BleChunkAssembler.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipBEBle/protocol/BleChunkAssembler.kt @@ -20,37 +20,43 @@ */ package com.vitorpamplona.quartz.nipBEBle.protocol +import kotlin.concurrent.atomics.AtomicReference +import kotlin.concurrent.atomics.ExperimentalAtomicApi + /** * Accumulates incoming BLE chunks for a single message and reassembles * them once all chunks have arrived. * - * Thread-safe: all access to internal state is synchronized. + * Thread-safe: uses a lock-free compare-and-set loop over an immutable list. */ +@OptIn(ExperimentalAtomicApi::class) class BleChunkAssembler { - private val lock = Any() - private val receivedChunks = mutableListOf() + private val receivedChunks = AtomicReference(emptyList()) /** * Adds a received chunk. If this completes the message, returns the * reassembled JSON string. Otherwise returns null. */ - fun addChunk(chunk: ByteArray): String? = - synchronized(lock) { - receivedChunks.add(chunk) - if (BleMessageChunker.isComplete(receivedChunks)) { - val message = BleMessageChunker.joinChunks(receivedChunks.toTypedArray()) - receivedChunks.clear() - message + fun addChunk(chunk: ByteArray): String? { + while (true) { + val current = receivedChunks.load() + val updated = current + chunk + if (BleMessageChunker.isComplete(updated)) { + if (receivedChunks.compareAndSet(current, emptyList())) { + return BleMessageChunker.joinChunks(updated.toTypedArray()) + } } else { - null + if (receivedChunks.compareAndSet(current, updated)) { + return null + } } } + } /** * Discards any partially received chunks. */ - fun reset() = - synchronized(lock) { - receivedChunks.clear() - } + fun reset() { + receivedChunks.store(emptyList()) + } }