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
JavaScript
;
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