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
This commit is contained in:
Claude
2026-03-30 17:48:58 +00:00
parent ef8b9be0e8
commit 13bd492e32
@@ -20,37 +20,43 @@
*/ */
package com.vitorpamplona.quartz.nipBEBle.protocol 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 * Accumulates incoming BLE chunks for a single message and reassembles
* them once all chunks have arrived. * 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 { class BleChunkAssembler {
private val lock = Any() private val receivedChunks = AtomicReference(emptyList<ByteArray>())
private val receivedChunks = mutableListOf<ByteArray>()
/** /**
* Adds a received chunk. If this completes the message, returns the * Adds a received chunk. If this completes the message, returns the
* reassembled JSON string. Otherwise returns null. * reassembled JSON string. Otherwise returns null.
*/ */
fun addChunk(chunk: ByteArray): String? = fun addChunk(chunk: ByteArray): String? {
synchronized(lock) { while (true) {
receivedChunks.add(chunk) val current = receivedChunks.load()
if (BleMessageChunker.isComplete(receivedChunks)) { val updated = current + chunk
val message = BleMessageChunker.joinChunks(receivedChunks.toTypedArray()) if (BleMessageChunker.isComplete(updated)) {
receivedChunks.clear() if (receivedChunks.compareAndSet(current, emptyList())) {
message return BleMessageChunker.joinChunks(updated.toTypedArray())
}
} else { } else {
null if (receivedChunks.compareAndSet(current, updated)) {
return null
}
} }
} }
}
/** /**
* Discards any partially received chunks. * Discards any partially received chunks.
*/ */
fun reset() = fun reset() {
synchronized(lock) { receivedChunks.store(emptyList())
receivedChunks.clear() }
}
} }