Skip to content

Commit 02bc03d

Browse files
authored
fix(ZBytes): publish the lazy byte cache safely (#517)
The lazy materialization in ZBytes is double-checked locking: the fast path reads `eager` outside the monitor while the slow path writes it under it. Without volatile, a reader observing the non-null reference has no happens-before edge to the copy that filled the array. Mark `eager` volatile. It moves out of the primary constructor because @volatile targets fields. The synchronized slow path stays: it is what guarantees exactly one thread copies out of and closes the native handle. `handle` stays non-volatile — it is only touched inside the monitor. Closes #516
1 parent 102fb8c commit 02bc03d

1 file changed

Lines changed: 10 additions & 1 deletion

File tree

  • zenoh-java/src/commonMain/kotlin/io/zenoh/bytes

zenoh-java/src/commonMain/kotlin/io/zenoh/bytes/ZBytes.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,19 @@ import io.zenoh.jni.bytes.ZBytes as JniZBytes
4848
* dropped samples are the one place native memory can be retained.
4949
*/
5050
class ZBytes private constructor(
51-
private var eager: ByteArray?,
51+
initialBytes: ByteArray?,
5252
private var handle: JniZBytes?,
5353
) : IntoZBytes {
5454

55+
/**
56+
* The materialized bytes, `null` until a handle-backed ZBytes is read.
57+
* Volatile: the [bytes] getter reads it outside the monitor, so the write
58+
* under the monitor must be safely published to that unlocked fast path.
59+
* `handle` needs no such treatment — it is only touched inside the monitor.
60+
*/
61+
@Volatile
62+
private var eager: ByteArray? = initialBytes
63+
5564
internal constructor(bytes: ByteArray) : this(bytes, null)
5665

5766
/**

0 commit comments

Comments
 (0)