diff --git a/src/lib/json_buffer.ts b/src/lib/json_buffer.ts index 36ec6e1..078b023 100644 --- a/src/lib/json_buffer.ts +++ b/src/lib/json_buffer.ts @@ -604,7 +604,15 @@ class SharedJsonBufferImpl extends Serializable { return Reflect.get(target, prop, receiver); } if (prop === "__ptr") return target.__ptr; - if (prop === "toJSON") return () => this.toJSON(target.__ptr); + if (prop === "toJSON") + return () => { + // Clear caches that may be stale from another thread's GC + // compacting the shared buffer and recycling addresses. + this.stringCache.clear(); + this.proxyCache.clear(); + this.propertyHints.clear(); + return this.toJSON(target.__ptr); + }; const ptr = target.__ptr; if (ptr === 0) return undefined; @@ -1211,7 +1219,13 @@ class SharedJsonBufferImpl extends Serializable { if (prop === CONSOLE_VIEW) return () => this.toConsoleView(target.__ptr); if (prop === toSerialized) return () => this[toSerialized](); if (prop === "__ptr") return target.__ptr; - if (prop === "toJSON") return () => this.toJSON(target.__ptr); + if (prop === "toJSON") + return () => { + this.stringCache.clear(); + this.proxyCache.clear(); + this.propertyHints.clear(); + return this.toJSON(target.__ptr); + }; if (prop === Symbol.iterator) { return () => new ArrayCursor(this, target.__ptr); } diff --git a/src/lib/sync/mpmc.ts b/src/lib/sync/mpmc.ts index 302d010..75d7f35 100644 --- a/src/lib/sync/mpmc.ts +++ b/src/lib/sync/mpmc.ts @@ -289,9 +289,15 @@ export class Receiver extends ChannelHandle { : { ok: false, error: ERR_SPURIOUS }; } + // Deep-copy under sendLock to prevent concurrent GC from + // compacting the SharedArrayBuffer while we traverse the proxy. + const sendToken = await this.internals.sendLock.acquire(); + const copied = typeof (val as any)?.toJSON === "function" ? (val as any).toJSON() : val; + sendToken[Symbol.dispose](); + // Handover: Item token consumed -> Slot token released this.internals.slotsAvailable[INTERNAL_SEMAPHORE_CONTROLLER].release(1); - return { ok: true, value: val }; + return { ok: true, value: copied }; } blockingRecv(): Result { @@ -320,8 +326,14 @@ export class Receiver extends ChannelHandle { : { ok: false, error: ERR_SPURIOUS }; } + // Deep-copy under sendLock to prevent concurrent GC from + // compacting the SharedArrayBuffer while we traverse the proxy. + const sendToken = this.internals.sendLock.blockingAcquire(); + const copied = typeof (val as any)?.toJSON === "function" ? (val as any).toJSON() : val; + sendToken[Symbol.dispose](); + this.internals.slotsAvailable[INTERNAL_SEMAPHORE_CONTROLLER].release(1); - return { ok: true, value: val }; + return { ok: true, value: copied }; } async *[Symbol.asyncIterator](): AsyncGenerator {