diff --git a/src/future/worker-bootstrap.ts b/src/future/worker-bootstrap.ts index 0da500a..3392d14 100644 --- a/src/future/worker-bootstrap.ts +++ b/src/future/worker-bootstrap.ts @@ -7,6 +7,7 @@ import { parentPort, workerData } from "node:worker_threads"; import { Ok, Err, safeTry } from "slang-ts"; +import { parseBoxSize } from "./memory-pool"; import type { Result } from "slang-ts"; import type { ActorSelf, ActorContext, ActorCallback, ActorRef, FormatUtils, @@ -154,7 +155,7 @@ function createActorContext(): ActorContext { const { msg, type, data, share = "owner" } = params; pendingWrite = { resolve: (lock) => { - const boxSize = Number(config.memory.boxSize); + const boxSize = parseBoxSize(config.memory.boxSize); const offset = lock.boxIndex * boxSize; const view = new Uint8Array(sab, offset, Math.min(data.length, boxSize)); view.set(data.subarray(0, boxSize)); @@ -169,7 +170,7 @@ function createActorContext(): ActorContext { /** Sync read from SAB. Returns null for Tier 1 (no handle). */ read: (msg: Message) => { if (!msg.handle) return null; - const boxSize = Number(config.memory.boxSize); + const boxSize = parseBoxSize(config.memory.boxSize); const offset = msg.handle.boxIndex * boxSize; const view = new Uint8Array(sab, offset, boxSize); /** Strip trailing null bytes for text/JSON decoding (SAB is zero-initialized) */ diff --git a/tests/future/supervisor.spec.ts b/tests/future/supervisor.spec.ts index fe44816..55baac5 100644 --- a/tests/future/supervisor.spec.ts +++ b/tests/future/supervisor.spec.ts @@ -281,6 +281,63 @@ describe("tier 2 shared memory", () => { expect(stripNulls(data)).toBe("hello world"); }); + + it("supports string boxSize in worker write/read operations", async () => { + const sup = createSupervisor({ + maxActors: 5, + memory: { poolSize: 2, boxSize: "1KB" }, + timeouts: { defaultLeaseMs: 2000 }, + }); + + const actor = sup.spawn(async (self, _msg, ctx) => { + const result = await ctx.write({ + msg: "string-box-size", + type: "json", + data: ctx.fmt.json.encode({ ok: true, value: 42 }), + share: "owner", + }); + + if (result.isOk) { + self.send("string-box-size", { handle: result.value }); + } else { + self.send("write-error", { error: result.error }); + } + }); + + const data = await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error("Timeout waiting for string boxSize write")); + }, 3000); + + actor.subscribe((msg) => { + const m = msg as Message; + + if (m.msg === "write-error" && m.data) { + clearTimeout(timeout); + reject(new Error((m.data as { error: string }).error)); + } + + if (m.msg === "string-box-size" && m.data) { + clearTimeout(timeout); + const handle = (m.data as { handle: Lock }).handle; + const reader = actor.read({ ...m, handle }); + + if (!reader) { + reject(new Error("Expected reader for string boxSize message")); + return; + } + + resolve(reader.json()); + } + }); + + actor.receive("write"); + }); + + expect(data).toEqual({ ok: true, value: 42 }); + + await sup.shutdown(); + }); }); // ============================================================================