UNPKG

sabcom

Version:

A TypeScript/Node.js library for inter-thread communication using SharedArrayBuffer with atomic operations for raw buffer data transfer

196 lines (194 loc) 6.36 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: Object.getOwnPropertyDescriptor(all, name).get }); } _export(exports, { get HEADER_SIZE () { return HEADER_SIZE; }, get HEADER_VALUES () { return HEADER_VALUES; }, get Handshake () { return Handshake; }, get Header () { return Header; }, get SEMAPHORE () { return SEMAPHORE; }, get Semaphore () { return Semaphore; }, get read () { return read; }, get readGenerator () { return readGenerator; }, get readSync () { return readSync; }, get write () { return write; }, get writeGenerator () { return writeGenerator; }, get writeSync () { return writeSync; } }); const SEMAPHORE = 0; var Semaphore = /*#__PURE__*/ function(Semaphore) { Semaphore[Semaphore["READY"] = 0] = "READY"; Semaphore[Semaphore["HANDSHAKE"] = 1] = "HANDSHAKE"; Semaphore[Semaphore["PAYLOAD"] = 2] = "PAYLOAD"; return Semaphore; }({}); var Handshake = /*#__PURE__*/ function(Handshake) { Handshake[Handshake["TOTAL_SIZE"] = 1] = "TOTAL_SIZE"; Handshake[Handshake["TOTAL_CHUNKS"] = 2] = "TOTAL_CHUNKS"; return Handshake; }({}); var Header = /*#__PURE__*/ function(Header) { Header[Header["CHUNK_INDEX"] = 1] = "CHUNK_INDEX"; Header[Header["CHUNK_OFFSET"] = 2] = "CHUNK_OFFSET"; Header[Header["CHUNK_SIZE"] = 3] = "CHUNK_SIZE"; return Header; }({}); const HEADER_VALUES = 1 + Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2; const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES; function* writeGenerator(data, buffer, { timeout = 5000 } = {}) { const chunkSize = buffer.byteLength - HEADER_SIZE; const totalSize = data.length; const totalChunks = Math.ceil(totalSize / chunkSize); const header = new Int32Array(buffer); header[1] = totalSize; header[2] = totalChunks; Atomics.store(header, SEMAPHORE, 1); Atomics.notify(header, SEMAPHORE); try { const handshakeResult = yield { target: header, index: SEMAPHORE, value: 1, timeout }; if (handshakeResult === 'timed-out') { throw new Error('Reader handshake timeout'); } const payload = new Uint8Array(buffer, HEADER_SIZE); for(let i = 0; i < totalChunks; i++){ const start = i * chunkSize; const end = Math.min(start + chunkSize, totalSize); const size = end - start; payload.set(data.subarray(start, end), 0); header[1] = i; header[2] = start; header[3] = size; Atomics.store(header, SEMAPHORE, 2); Atomics.notify(header, SEMAPHORE); const chunkResult = yield { target: header, index: SEMAPHORE, value: 2, timeout }; if (chunkResult === 'timed-out') { throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`); } } } finally{ Atomics.store(header, SEMAPHORE, 0); } } function* readGenerator(buffer, { timeout = 5000 } = {}) { const header = new Int32Array(buffer); const handshakeResult = yield { target: header, index: SEMAPHORE, value: 0, timeout }; if (handshakeResult === 'timed-out') { throw new Error('Handshake timeout'); } if (header[SEMAPHORE] !== 1) { throw new Error('Invalid handshake state'); } const totalSize = header[1]; const totalChunks = header[2]; const data = new Uint8Array(totalSize); Atomics.store(header, SEMAPHORE, 0); Atomics.notify(header, SEMAPHORE); const payload = new Uint8Array(buffer, HEADER_SIZE); for(let i = 0; i < totalChunks; i++){ const chunkResult = yield { target: header, index: SEMAPHORE, value: 0, timeout }; if (chunkResult === 'timed-out') { throw new Error(`Writer timeout waiting for chunk ${i}`); } if (header[SEMAPHORE] !== 2) { throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`); } const chunkIndex = header[1]; if (i !== chunkIndex) { throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`); } const offset = header[2]; const size = header[3]; data.set(payload.subarray(0, size), offset); Atomics.store(header, SEMAPHORE, 0); Atomics.notify(header, SEMAPHORE); } return data; } const writeSync = (data, buffer, options)=>{ const gen = writeGenerator(data, buffer, options); let result = gen.next(); while(!result.done){ const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout); result = gen.next(waitResult); } }; const write = async (data, buffer, options)=>{ const gen = writeGenerator(data, buffer, options); let result = gen.next(); while(!result.done){ const request = result.value; const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value; result = gen.next(waitResult); } }; const readSync = (buffer, options)=>{ const gen = readGenerator(buffer, options); let result = gen.next(); while(!result.done){ const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout); result = gen.next(waitResult); } return result.value; }; const read = async (buffer, options)=>{ const gen = readGenerator(buffer, options); let result = gen.next(); while(!result.done){ const request = result.value; const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value; result = gen.next(waitResult); } return result.value; }; //# sourceMappingURL=index.cjs.map