UNPKG

sabcom

Version:

A TypeScript/Node.js library for inter-thread communication using SharedArrayBuffer with atomic operations and V8 serialization

1 lines 10.9 kB
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { serialize, deserialize } from \"node:v8\";\n\nexport const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES =\n 1 +\n Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2;\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n timeout?: number;\n}\n\nexport interface WaitRequest {\n target: Int32Array;\n index: number;\n value: number;\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\nexport function* writeGenerator(\n data: unknown,\n buffer: SharedArrayBuffer,\n { timeout = 5000 }: Options = {},\n): Generator<WaitRequest, void, WaitResponse> {\n const serialized = serialize(data);\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n const totalSize = serialized.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === \"timed-out\") {\n throw new Error(\"Reader handshake timeout\");\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(serialized.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === \"timed-out\") {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n }\n}\n\nexport function* readGenerator(\n buffer: SharedArrayBuffer,\n { timeout = 5000 }: Options = {},\n): Generator<WaitRequest, unknown, WaitResponse> {\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === \"timed-out\") {\n throw new Error(\"Handshake timeout\");\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error(\"Invalid handshake state\");\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === \"timed-out\") {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(\n `Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`,\n );\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(\n `Reader integrity failure for chunk ${chunkIndex} expected ${i}`,\n );\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return deserialize(data) as unknown;\n}\n\nexport const writeSync = (\n data: unknown,\n buffer: SharedArrayBuffer,\n options?: Options,\n) => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(\n result.value.target,\n result.value.index,\n result.value.value,\n result.value.timeout,\n );\n result = gen.next(waitResult);\n }\n};\n\nexport const write = async (\n data: unknown,\n buffer: SharedArrayBuffer,\n options?: Options,\n) => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(\n request.target,\n request.index,\n request.value,\n request.timeout,\n ).value;\n result = gen.next(waitResult);\n }\n};\n\nexport const readSync = (\n buffer: SharedArrayBuffer,\n options?: Options,\n): unknown => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(\n result.value.target,\n result.value.index,\n result.value.value,\n result.value.timeout,\n );\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\nexport const read = async (\n buffer: SharedArrayBuffer,\n options?: Options,\n): Promise<unknown> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(\n request.target,\n request.index,\n request.value,\n request.timeout,\n ).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["HEADER_SIZE","HEADER_VALUES","Handshake","Header","SEMAPHORE","Semaphore","read","readGenerator","readSync","write","writeGenerator","writeSync","Math","max","Object","values","length","Uint32Array","BYTES_PER_ELEMENT","data","buffer","timeout","serialized","serialize","chunkSize","byteLength","totalSize","totalChunks","ceil","header","Int32Array","Atomics","store","notify","handshakeResult","target","index","value","Error","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","chunkIndex","offset","deserialize","options","gen","result","next","done","waitResult","wait","request","waitAsync"],"mappings":"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"}