1824 lines
84 KiB
JavaScript
1824 lines
84 KiB
JavaScript
//#region src/emnapi/async-work.js
|
|
function asyncWork (emnapiPluginCtx) {
|
|
const { emnapiCtx, emnapiNodeBinding, emnapiAsyncWorkPoolSize } = emnapiPluginCtx;
|
|
var mod = (function (exports, emnapi_shared, emscripten_runtime) {
|
|
|
|
//#region src/async-work.ts
|
|
/**
|
|
* @__deps $emnapiCtx
|
|
* @__deps $emnapiEnv
|
|
* @__deps $emnapiNodeBinding
|
|
* @__deps $emnapiAsyncWorkPoolSize
|
|
* @__postset
|
|
* ```
|
|
* emnapiAWST.init();
|
|
* ```
|
|
*/
|
|
var emnapiAWST = {
|
|
idGen: {},
|
|
values: [undefined],
|
|
queued: new Set(),
|
|
pending: [],
|
|
init: function () {
|
|
const idGen = {
|
|
nextId: 1,
|
|
list: [],
|
|
generate: function () {
|
|
let id;
|
|
if (idGen.list.length) {
|
|
id = idGen.list.shift();
|
|
}
|
|
else {
|
|
id = idGen.nextId;
|
|
idGen.nextId++;
|
|
}
|
|
return id;
|
|
},
|
|
reuse: function (id) {
|
|
idGen.list.push(id);
|
|
}
|
|
};
|
|
emnapiAWST.idGen = idGen;
|
|
emnapiAWST.values = [undefined];
|
|
emnapiAWST.queued = new Set();
|
|
emnapiAWST.pending = [];
|
|
},
|
|
create: function (env, resource, resourceName, execute, complete, data) {
|
|
let asyncId = 0;
|
|
let triggerAsyncId = 0;
|
|
if (emnapiNodeBinding) {
|
|
const asyncContext = emnapiNodeBinding.node.emitAsyncInit(resource, resourceName, -1);
|
|
asyncId = asyncContext.asyncId;
|
|
triggerAsyncId = asyncContext.triggerAsyncId;
|
|
}
|
|
const id = emnapiAWST.idGen.generate();
|
|
emnapiAWST.values[id] = {
|
|
env,
|
|
id,
|
|
resource,
|
|
asyncId,
|
|
triggerAsyncId,
|
|
status: 0,
|
|
execute,
|
|
complete,
|
|
data
|
|
};
|
|
return id;
|
|
},
|
|
callComplete: function (work, status) {
|
|
const complete = work.complete;
|
|
const env = work.env;
|
|
const data = work.data;
|
|
const callback = () => {
|
|
if (!complete)
|
|
return;
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
const scope = emnapiCtx.openScope(envObject);
|
|
try {
|
|
envObject.callbackIntoModule(true, () => {
|
|
(emnapiPluginCtx.wasmTable.get(complete))(env, status, data);
|
|
});
|
|
}
|
|
finally {
|
|
emnapiCtx.closeScope(envObject, scope);
|
|
}
|
|
};
|
|
if (emnapiNodeBinding) {
|
|
emnapiNodeBinding.node.makeCallback(work.resource, callback, [], {
|
|
asyncId: work.asyncId,
|
|
triggerAsyncId: work.triggerAsyncId
|
|
});
|
|
}
|
|
else {
|
|
callback();
|
|
}
|
|
},
|
|
queue: function (id) {
|
|
const work = emnapiAWST.values[id];
|
|
if (!work)
|
|
return;
|
|
if (work.status === 0) {
|
|
work.status = 1;
|
|
if (emnapiAWST.queued.size >= (Math.abs(emnapiAsyncWorkPoolSize) || 4)) {
|
|
emnapiAWST.pending.push(id);
|
|
return;
|
|
}
|
|
emnapiAWST.queued.add(id);
|
|
const env = work.env;
|
|
const data = work.data;
|
|
const execute = work.execute;
|
|
work.status = 2;
|
|
emnapiCtx.features.setImmediate(() => {
|
|
(emnapiPluginCtx.wasmTable.get(execute))(env, data);
|
|
emnapiAWST.queued.delete(id);
|
|
work.status = 3;
|
|
emnapiCtx.features.setImmediate(() => {
|
|
emnapiAWST.callComplete(work, 0 /* napi_status.napi_ok */);
|
|
});
|
|
if (emnapiAWST.pending.length > 0) {
|
|
const nextWorkId = emnapiAWST.pending.shift();
|
|
emnapiAWST.values[nextWorkId].status = 0;
|
|
emnapiAWST.queue(nextWorkId);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
cancel: function (id) {
|
|
const index = emnapiAWST.pending.indexOf(id);
|
|
if (index !== -1) {
|
|
const work = emnapiAWST.values[id];
|
|
if (work && (work.status === 1)) {
|
|
work.status = 4;
|
|
emnapiAWST.pending.splice(index, 1);
|
|
emnapiCtx.features.setImmediate(() => {
|
|
emnapiAWST.callComplete(work, 11 /* napi_status.napi_cancelled */);
|
|
});
|
|
return 0 /* napi_status.napi_ok */;
|
|
}
|
|
else {
|
|
return 9 /* napi_status.napi_generic_failure */;
|
|
}
|
|
}
|
|
return 9 /* napi_status.napi_generic_failure */;
|
|
},
|
|
remove: function (id) {
|
|
const work = emnapiAWST.values[id];
|
|
if (!work)
|
|
return;
|
|
if (emnapiNodeBinding) {
|
|
emnapiNodeBinding.node.emitAsyncDestroy({
|
|
asyncId: work.asyncId,
|
|
triggerAsyncId: work.triggerAsyncId
|
|
});
|
|
}
|
|
emnapiAWST.values[id] = undefined;
|
|
emnapiAWST.idGen.reuse(id);
|
|
}
|
|
};
|
|
|
|
//#endregion src/async-work.ts
|
|
|
|
//#region src/macro.ts
|
|
|
|
//#endregion src/macro.ts
|
|
|
|
//#region src/core/async-work.ts
|
|
const {
|
|
// onCreateWorker, napiModule, singleThreadAsyncWork, _emnapi_async_work_pool_size,
|
|
// PThread, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, wasmInstance, _free, wasmMemory, _malloc,
|
|
_emnapi_node_emit_async_init, _emnapi_node_emit_async_destroy, _emnapi_runtime_keepalive_pop, _emnapi_runtime_keepalive_push } = emnapiPluginCtx;
|
|
var emnapiAWMT = {
|
|
pool: [],
|
|
workerReady: null,
|
|
globalAddress: 0,
|
|
globalOffset: {
|
|
idle_threads: 0,
|
|
q: 1 * 4,
|
|
next: 1 * 4,
|
|
prev: 2 * 4,
|
|
mutex: 3 * 4,
|
|
cond: 4 * 4,
|
|
exit_message: 5 * 4,
|
|
end: 7 * 4
|
|
},
|
|
offset: {
|
|
/* napi_ref */ resource: 0,
|
|
/* double */ async_id: 8,
|
|
/* double */ trigger_async_id: 16,
|
|
/* napi_env */ env: 24,
|
|
/* int32_t */ status: 1 * 4 + 24, // 0 for pending, 1 for cancelled, 2 for completed
|
|
queue: 2 * 4 + 24,
|
|
queue_next: 2 * 4 + 24,
|
|
queue_prev: 3 * 4 + 24,
|
|
/* void* */ data: 4 * 4 + 24,
|
|
/* napi_async_execute_callback */ execute: 5 * 4 + 24,
|
|
/* napi_async_complete_callback */ complete: 6 * 4 + 24,
|
|
end: 7 * 4 + 24
|
|
},
|
|
/**
|
|
* When another thread grows the shared WebAssembly.Memory, this agent's
|
|
* cached `wasmMemory.buffer` may still have the old shorter length
|
|
* (V8 refreshes it lazily). If a pointer derived from shared memory lies
|
|
* beyond the cached length, `wasmMemory.grow(0)` forces the agent to
|
|
* observe the current memory size and refreshes the buffer.
|
|
*/
|
|
ensureBufferFor(end) {
|
|
let buffer = emscripten_runtime.wasmMemory.buffer;
|
|
if (end > buffer.byteLength) {
|
|
emscripten_runtime.wasmMemory.grow(0);
|
|
buffer = emscripten_runtime.wasmMemory.buffer;
|
|
}
|
|
return buffer;
|
|
},
|
|
init() {
|
|
emnapiAWMT.pool = [];
|
|
emnapiAWMT.workerReady = null;
|
|
if (typeof emscripten_runtime.PThread !== 'undefined') {
|
|
emscripten_runtime.PThread.unusedWorkers.forEach(emnapiAWMT.addListener);
|
|
Object.values(emscripten_runtime.PThread.pthreads).forEach(emnapiAWMT.addListener);
|
|
const __original_getNewWorker = emscripten_runtime.PThread.getNewWorker;
|
|
emscripten_runtime.PThread.getNewWorker = function () {
|
|
const r = __original_getNewWorker.apply(this, arguments);
|
|
emnapiAWMT.addListener(r);
|
|
return r;
|
|
};
|
|
}
|
|
},
|
|
addListener(worker) {
|
|
if (!worker)
|
|
return false;
|
|
if (worker._emnapiAWMTListener)
|
|
return true;
|
|
const handler = function (e) {
|
|
const data = emscripten_runtime.ENVIRONMENT_IS_NODE ? e : e.data;
|
|
const __emnapi__ = data.__emnapi__;
|
|
if (__emnapi__) {
|
|
const type = __emnapi__.type;
|
|
const payload = __emnapi__.payload;
|
|
if (type === 'async-work-complete') {
|
|
emnapiAWMT.callComplete(payload.work, 0 /* napi_status.napi_ok */);
|
|
}
|
|
}
|
|
};
|
|
const dispose = function () {
|
|
if (emscripten_runtime.ENVIRONMENT_IS_NODE) {
|
|
worker.off('message', handler);
|
|
}
|
|
else {
|
|
worker.removeEventListener('message', handler, false);
|
|
}
|
|
delete worker._emnapiAWMTListener;
|
|
};
|
|
worker._emnapiAWMTListener = { handler, dispose };
|
|
if (emscripten_runtime.ENVIRONMENT_IS_NODE) {
|
|
worker.on('message', handler);
|
|
}
|
|
else {
|
|
worker.addEventListener('message', handler, false);
|
|
}
|
|
return true;
|
|
},
|
|
initGlobal() {
|
|
if (!emnapiAWMT.globalAddress) {
|
|
emnapiAWMT.globalAddress = emscripten_runtime._malloc(emnapiAWMT.globalOffset.end);
|
|
emnapiAWMT.globalAddress >>>= 0;
|
|
const size = emnapiAWMT.globalOffset.end;
|
|
const addr = emnapiAWMT.globalAddress;
|
|
new Uint8Array(emnapiAWMT.ensureBufferFor(addr + size), addr, size).fill(0);
|
|
emnapiAWMT.queueInit(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.q);
|
|
emnapiAWMT.queueInit(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.exit_message);
|
|
}
|
|
},
|
|
terminateWorkers() {
|
|
emnapiAWMT.pool.forEach(w => {
|
|
w._emnapiAWMTListener?.dispose();
|
|
w._emnapiTSFNListener?.dispose();
|
|
w.terminate();
|
|
});
|
|
emnapiAWMT.pool.length = 0;
|
|
},
|
|
initWorkers(n) {
|
|
if (emscripten_runtime.ENVIRONMENT_IS_PTHREAD) {
|
|
return emnapiAWMT.workerReady || (emnapiAWMT.workerReady = Promise.resolve());
|
|
}
|
|
if (emnapiAWMT.workerReady)
|
|
return emnapiAWMT.workerReady;
|
|
if (!('emnapi_async_worker_create' in emscripten_runtime.wasmInstance.exports)) {
|
|
throw new TypeError('`emnapi_async_worker_create` is not exported, please try to add `--export=emnapi_async_worker_create` to linker flags');
|
|
}
|
|
const emnapi_async_worker_create = emscripten_runtime.wasmInstance.exports.emnapi_async_worker_create;
|
|
const args = [];
|
|
emnapiAWMT.initGlobal();
|
|
for (let i = 0; i < n; ++i) {
|
|
args.push(emnapi_async_worker_create(1, emnapiAWMT.globalAddress));
|
|
}
|
|
const promises = args.map(index => {
|
|
if (index === 0) {
|
|
return Promise.reject(new Error('Failed to create async worker'));
|
|
}
|
|
let worker;
|
|
if (index < 0) {
|
|
worker = emnapiAWMT.pool[-index - 1];
|
|
if (worker)
|
|
return worker.whenLoaded;
|
|
}
|
|
index >>>= 0;
|
|
const tidOffset = 20;
|
|
const view = new DataView(emnapiAWMT.ensureBufferFor(index + tidOffset + 4));
|
|
const tid = view.getInt32(index + tidOffset, true);
|
|
worker = emscripten_runtime.PThread.pthreads[tid];
|
|
return worker.whenLoaded;
|
|
});
|
|
emnapiAWMT.workerReady = Promise.all(promises);
|
|
return emnapiAWMT.workerReady;
|
|
},
|
|
getResource(work) {
|
|
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.resource + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.resource, true);
|
|
},
|
|
getExecute(work) {
|
|
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.execute + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.execute, true);
|
|
},
|
|
getComplete(work) {
|
|
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.complete + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.complete, true);
|
|
},
|
|
getEnv(work) {
|
|
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.env + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.env, true);
|
|
},
|
|
getData(work) {
|
|
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.data + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
return GET_HEAP_DATA_VIEW().getUint32(work + emnapiAWMT.offset.data, true);
|
|
},
|
|
getMutex() {
|
|
const index = emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.mutex;
|
|
const mutex = {
|
|
lock() {
|
|
const isBrowserMain = typeof window !== 'undefined' && typeof document !== 'undefined' && !emscripten_runtime.ENVIRONMENT_IS_NODE;
|
|
const i32a = new Int32Array(emnapiAWMT.ensureBufferFor(index + 4), index, 1);
|
|
if (isBrowserMain) {
|
|
while (true) {
|
|
const oldValue = Atomics.compareExchange(i32a, 0, 0, 10);
|
|
if (oldValue === 0) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
while (true) {
|
|
const oldValue = Atomics.compareExchange(i32a, 0, 0, 10);
|
|
if (oldValue === 0) {
|
|
return;
|
|
}
|
|
Atomics.wait(i32a, 0, 10);
|
|
}
|
|
}
|
|
},
|
|
unlock() {
|
|
const i32a = new Int32Array(emnapiAWMT.ensureBufferFor(index + 4), index, 1);
|
|
const oldValue = Atomics.compareExchange(i32a, 0, 10, 0);
|
|
if (oldValue !== 10) {
|
|
throw new Error('Tried to unlock while not holding the mutex');
|
|
}
|
|
Atomics.notify(i32a, 0, 1);
|
|
},
|
|
execute(fn) {
|
|
mutex.lock();
|
|
try {
|
|
return fn();
|
|
}
|
|
finally {
|
|
mutex.unlock();
|
|
}
|
|
}
|
|
};
|
|
return mutex;
|
|
},
|
|
getCond() {
|
|
const index = emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.cond;
|
|
const mutex = emnapiAWMT.getMutex();
|
|
const cond = {
|
|
wait() {
|
|
const i32a = new Int32Array(emnapiAWMT.ensureBufferFor(index + 4), index, 1);
|
|
const value = Atomics.load(i32a, 0);
|
|
mutex.unlock();
|
|
Atomics.wait(i32a, 0, value);
|
|
mutex.lock();
|
|
},
|
|
signal() {
|
|
const i32a = new Int32Array(emnapiAWMT.ensureBufferFor(index + 4), index, 1);
|
|
Atomics.add(i32a, 0, 1);
|
|
Atomics.notify(i32a, 0, 1);
|
|
}
|
|
};
|
|
return cond;
|
|
},
|
|
queueInit(q) {
|
|
emnapiAWMT.ensureBufferFor(q + 4 + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
GET_HEAP_DATA_VIEW().setUint32(q, q, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(q + 4, q, true);
|
|
},
|
|
queueInsertTail(h, q) {
|
|
emnapiAWMT.ensureBufferFor(h + 4 + 4);
|
|
emnapiAWMT.ensureBufferFor(q + 4 + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
GET_HEAP_DATA_VIEW().setUint32(q, h, true);
|
|
const tempValue = GET_HEAP_DATA_VIEW().getUint32(h + 4, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(q + 4, tempValue, true);
|
|
const qprev = GET_HEAP_DATA_VIEW().getUint32(q + 4, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(qprev, q, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(h + 4, q, true);
|
|
},
|
|
queueRemove(q) {
|
|
emnapiAWMT.ensureBufferFor(q + 4 + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
const qprev = GET_HEAP_DATA_VIEW().getUint32(q + 4, true);
|
|
const qnext = GET_HEAP_DATA_VIEW().getUint32(q, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(qprev, qnext, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(qnext + 4, qprev, true);
|
|
},
|
|
queueEmpty(q) {
|
|
emnapiAWMT.ensureBufferFor(q + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
// eslint-disable-next-line eqeqeq
|
|
return q == GET_HEAP_DATA_VIEW().getUint32(q, true);
|
|
},
|
|
scheduleWork: function (work) {
|
|
if (!emnapiAWMT.workerReady?.ready) {
|
|
emnapiAWMT.initWorkers(emnapi_shared._emnapi_async_work_pool_size()).then(() => {
|
|
emnapiAWMT.workerReady.ready = true;
|
|
}).catch((err) => {
|
|
emnapiAWMT.workerReady = null;
|
|
throw err;
|
|
});
|
|
}
|
|
_emnapi_runtime_keepalive_push();
|
|
emnapiCtx.increaseWaitingRequestCounter();
|
|
const statusBuffer = new Int32Array(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.status + 4), work + emnapiAWMT.offset.status, 1);
|
|
Atomics.store(statusBuffer, 0, 0 /* AsyncWorkStatus.Pending */);
|
|
const mutex = emnapiAWMT.getMutex();
|
|
const cond = emnapiAWMT.getCond();
|
|
mutex.lock();
|
|
try {
|
|
emnapiAWMT.queueInsertTail(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.q, work + emnapiAWMT.offset.queue);
|
|
}
|
|
catch (err) {
|
|
_emnapi_runtime_keepalive_pop();
|
|
emnapiCtx.decreaseWaitingRequestCounter();
|
|
mutex.unlock();
|
|
throw err;
|
|
}
|
|
emnapiAWMT.ensureBufferFor(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.idle_threads + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
if (GET_HEAP_DATA_VIEW().getUint32(emnapiAWMT.globalAddress + emnapiAWMT.globalOffset.idle_threads, true) > 0) {
|
|
cond.signal();
|
|
}
|
|
mutex.unlock();
|
|
},
|
|
cancelWork(work) {
|
|
let cancelled = false;
|
|
emnapiAWMT.getMutex().execute(() => {
|
|
emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.status + 4);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
cancelled = !emnapiAWMT.queueEmpty(work + emnapiAWMT.offset.queue) && GET_HEAP_DATA_VIEW().getInt32(work + emnapiAWMT.offset.status, true) !== 2 /* AsyncWorkStatus.Completed */;
|
|
if (cancelled) {
|
|
emnapiAWMT.queueRemove(work + emnapiAWMT.offset.queue);
|
|
}
|
|
});
|
|
if (!cancelled) {
|
|
return 9 /* napi_status.napi_generic_failure */;
|
|
}
|
|
if (Atomics.compareExchange(new Int32Array(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.status + 4), work + emnapiAWMT.offset.status, 1), 0, 0 /* AsyncWorkStatus.Pending */, 1 /* AsyncWorkStatus.Cancelled */) !== 0 /* AsyncWorkStatus.Pending */) {
|
|
return 9 /* napi_status.napi_generic_failure */;
|
|
}
|
|
emnapiCtx.features.setImmediate(() => {
|
|
emnapiAWMT.callComplete(work, 11 /* napi_status.napi_cancelled */);
|
|
});
|
|
return 0 /* napi_status.napi_ok */;
|
|
},
|
|
callComplete: function (work, status) {
|
|
_emnapi_runtime_keepalive_pop();
|
|
emnapiCtx.decreaseWaitingRequestCounter();
|
|
const complete = emnapiAWMT.getComplete(work);
|
|
const env = emnapiAWMT.getEnv(work);
|
|
const data = emnapiAWMT.getData(work);
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
const scope = emnapiCtx.openScope(envObject);
|
|
const callback = () => {
|
|
if (!complete)
|
|
return;
|
|
envObject.callbackIntoModule(true, () => {
|
|
(emnapiPluginCtx.wasmTable.get(complete))(env, status, data);
|
|
});
|
|
};
|
|
try {
|
|
if (emnapiNodeBinding) {
|
|
const resource = emnapiAWMT.getResource(work);
|
|
const resource_value = emnapiCtx.getRef(resource).get();
|
|
const resourceObject = emnapiCtx.jsValueFromNapiValue(resource_value);
|
|
const view = new DataView(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.trigger_async_id + 8));
|
|
const asyncId = view.getFloat64(work + emnapiAWMT.offset.async_id, true);
|
|
const triggerAsyncId = view.getFloat64(work + emnapiAWMT.offset.trigger_async_id, true);
|
|
emnapiNodeBinding.node.makeCallback(resourceObject, callback, [], {
|
|
asyncId,
|
|
triggerAsyncId
|
|
});
|
|
}
|
|
else {
|
|
callback();
|
|
}
|
|
}
|
|
finally {
|
|
emnapiCtx.closeScope(envObject, scope);
|
|
}
|
|
}
|
|
};
|
|
emnapiAWST.init();
|
|
emnapiAWMT.init();
|
|
/** @__sig ippppppp */
|
|
var napi_create_async_work = emnapi_shared.singleThreadAsyncWork
|
|
? function (env, resource, resource_name, execute, complete, data, result) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
// @ts-expect-error
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
envObject.checkGCAccess();
|
|
if (!execute)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
if (!result)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
let resourceObject;
|
|
if (resource) {
|
|
resourceObject = Object(emnapiCtx.jsValueFromNapiValue(resource));
|
|
}
|
|
else {
|
|
resourceObject = {};
|
|
}
|
|
if (!resource_name)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
const resourceName = String(emnapiCtx.jsValueFromNapiValue(resource_name));
|
|
const id = emnapiAWST.create(env, resourceObject, resourceName, execute, complete, data);
|
|
result >>>= 0;
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
GET_HEAP_DATA_VIEW().setUint32(result, id, true);
|
|
return envObject.clearLastError();
|
|
}
|
|
: function (env, resource, resource_name, execute, complete, data, result) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
// @ts-expect-error
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
envObject.checkGCAccess();
|
|
if (!execute)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
if (!result)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
let resourceObject;
|
|
if (resource) {
|
|
resourceObject = Object(emnapiCtx.jsValueFromNapiValue(resource));
|
|
}
|
|
else {
|
|
resourceObject = {};
|
|
}
|
|
if (!resource_name)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
const sizeofAW = emnapiAWMT.offset.end;
|
|
let aw = emscripten_runtime._malloc(sizeofAW);
|
|
if (!aw)
|
|
return envObject.setLastError(9 /* napi_status.napi_generic_failure */);
|
|
aw >>>= 0;
|
|
new Uint8Array(emnapiAWMT.ensureBufferFor(aw + sizeofAW)).subarray(aw, aw + sizeofAW).fill(0);
|
|
const s = emnapiCtx.napiValueFromJsValue(resourceObject);
|
|
const resourceRef = emnapiCtx.createReference(envObject, s, 1, 1 /* ReferenceOwnership.kUserland */);
|
|
const resource_ = resourceRef.id;
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
GET_HEAP_DATA_VIEW().setUint32(aw, resource_, true);
|
|
_emnapi_node_emit_async_init(s, resource_name, -1, aw + emnapiAWMT.offset.async_id);
|
|
GET_HEAP_DATA_VIEW().setUint32(aw + emnapiAWMT.offset.env, env, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(aw + emnapiAWMT.offset.execute, execute, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(aw + emnapiAWMT.offset.complete, complete, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(aw + emnapiAWMT.offset.data, data, true);
|
|
emnapiAWMT.queueInit(aw + emnapiAWMT.offset.queue);
|
|
result >>>= 0;
|
|
GET_HEAP_DATA_VIEW().setUint32(result, aw, true);
|
|
return envObject.clearLastError();
|
|
};
|
|
/** @__sig ipp */
|
|
var napi_delete_async_work = emnapi_shared.singleThreadAsyncWork
|
|
? function (env, work) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
// @ts-expect-error
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
envObject.checkGCAccess();
|
|
if (!work)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
work >>>= 0;
|
|
emnapiAWST.remove(work);
|
|
return envObject.clearLastError();
|
|
}
|
|
: function (env, work) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
// @ts-expect-error
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
envObject.checkGCAccess();
|
|
if (!work)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
work >>>= 0;
|
|
const resource = emnapiAWMT.getResource(work);
|
|
emnapiCtx.getRef(resource).dispose();
|
|
if (emnapiNodeBinding) {
|
|
const view = new DataView(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.trigger_async_id + 8));
|
|
const asyncId = view.getFloat64(work + emnapiAWMT.offset.async_id, true);
|
|
const triggerAsyncId = view.getFloat64(work + emnapiAWMT.offset.trigger_async_id, true);
|
|
_emnapi_node_emit_async_destroy(asyncId, triggerAsyncId);
|
|
}
|
|
emscripten_runtime._free(work);
|
|
return envObject.clearLastError();
|
|
};
|
|
/** @__sig ipp */
|
|
var napi_queue_async_work = emnapi_shared.singleThreadAsyncWork
|
|
? function (env, work) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
if (!work)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
work >>>= 0;
|
|
emnapiAWST.queue(work);
|
|
return envObject.clearLastError();
|
|
}
|
|
: function (env, work) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
if (!work)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
work >>>= 0;
|
|
emnapiAWMT.scheduleWork(work);
|
|
return envObject.clearLastError();
|
|
};
|
|
/** @__sig ipp */
|
|
var napi_cancel_async_work = emnapi_shared.singleThreadAsyncWork
|
|
? function (env, work) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
if (!work)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
work >>>= 0;
|
|
const status = emnapiAWST.cancel(work);
|
|
if (status === 0 /* napi_status.napi_ok */)
|
|
return envObject.clearLastError();
|
|
return envObject.setLastError(status);
|
|
}
|
|
: function (env, work) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
if (!work)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
work >>>= 0;
|
|
const status = emnapiAWMT.cancelWork(work);
|
|
if (status === 0 /* napi_status.napi_ok */)
|
|
return envObject.clearLastError();
|
|
return envObject.setLastError(status);
|
|
};
|
|
/** @__sig pp */
|
|
function _emnapi_async_worker(globalAddress) {
|
|
globalAddress >>>= 0;
|
|
emnapiAWMT.globalAddress = globalAddress;
|
|
const mutex = emnapiAWMT.getMutex();
|
|
const cond = emnapiAWMT.getCond();
|
|
mutex.lock();
|
|
const exitMessageAddr = globalAddress + emnapiAWMT.globalOffset.exit_message;
|
|
const idleThreadsAddr = globalAddress + emnapiAWMT.globalOffset.idle_threads;
|
|
const workerQueueAddr = globalAddress + emnapiAWMT.globalOffset.q;
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
for (;;) {
|
|
emnapiAWMT.ensureBufferFor(workerQueueAddr + 4);
|
|
while (emnapiAWMT.queueEmpty(workerQueueAddr)) {
|
|
Atomics.add(new Int32Array(emnapiAWMT.ensureBufferFor(idleThreadsAddr + 4), idleThreadsAddr, 1), 0, 1);
|
|
cond.wait();
|
|
Atomics.sub(new Int32Array(emnapiAWMT.ensureBufferFor(idleThreadsAddr + 4), idleThreadsAddr, 1), 0, 1);
|
|
}
|
|
const q = GET_HEAP_DATA_VIEW().getUint32(workerQueueAddr, true);
|
|
if (q === exitMessageAddr) {
|
|
cond.signal();
|
|
mutex.unlock();
|
|
break;
|
|
}
|
|
const work = q - emnapiAWMT.offset.queue;
|
|
emnapiAWMT.queueRemove(q);
|
|
emnapiAWMT.queueInit(q);
|
|
mutex.unlock();
|
|
const statusBuffer = new Int32Array(emnapiAWMT.ensureBufferFor(work + emnapiAWMT.offset.status + 4), work + emnapiAWMT.offset.status, 1);
|
|
if (Atomics.load(statusBuffer, 0) === 1 /* AsyncWorkStatus.Cancelled */) {
|
|
emscripten_runtime.abort('unreachable');
|
|
}
|
|
const execute = emnapiAWMT.getExecute(work);
|
|
const env = emnapiAWMT.getEnv(work);
|
|
const data = emnapiAWMT.getData(work);
|
|
(emnapiPluginCtx.wasmTable.get(execute))(env, data);
|
|
Atomics.store(statusBuffer, 0, 2 /* AsyncWorkStatus.Completed */);
|
|
const postMessage = emnapi_shared.napiModule.postMessage;
|
|
postMessage({
|
|
__emnapi__: {
|
|
type: 'async-work-complete',
|
|
payload: { work }
|
|
}
|
|
});
|
|
mutex.lock();
|
|
}
|
|
return 0;
|
|
}
|
|
/** @__sig ipp */
|
|
function _emnapi_spawn_worker(f, globalAddress) {
|
|
if (typeof emnapi_shared.onCreateWorker !== 'function') {
|
|
throw new TypeError('`options.onCreateWorker` is not a function');
|
|
}
|
|
const promises = [];
|
|
const args = [];
|
|
if (!('emnapi_async_worker_create' in emscripten_runtime.wasmInstance.exports)) {
|
|
throw new TypeError('`emnapi_async_worker_create` is not exported, please try to add `--export=emnapi_async_worker_create` to linker flags');
|
|
}
|
|
args.push(emscripten_runtime.wasmInstance.exports.emnapi_async_worker_create(0, 0));
|
|
const handleError = (e) => {
|
|
if ('message' in e && (e.message.indexOf('RuntimeError') !== -1 || e.message.indexOf('unreachable') !== -1)) {
|
|
emnapiAWMT.terminateWorkers();
|
|
}
|
|
};
|
|
let ret;
|
|
try {
|
|
const worker = emnapi_shared.onCreateWorker({ type: 'async-work', name: 'emnapi-async-worker' });
|
|
const p = emscripten_runtime.PThread.loadWasmModuleToWorker(worker);
|
|
if (emscripten_runtime.ENVIRONMENT_IS_NODE) {
|
|
worker.on('error', handleError);
|
|
}
|
|
else {
|
|
worker.addEventListener('error', handleError, false);
|
|
}
|
|
emnapiAWMT.addListener(worker);
|
|
if (typeof emnapiPluginCtx.emnapiTSFN !== 'undefined') {
|
|
emnapiPluginCtx.emnapiTSFN.addListener(worker);
|
|
}
|
|
promises.push(p.then(() => {
|
|
if (typeof worker.unref === 'function') {
|
|
worker.unref();
|
|
}
|
|
}));
|
|
ret = emnapiAWMT.pool.push(worker) - 1;
|
|
const arg = args[0];
|
|
worker.threadBlockBase = arg;
|
|
worker.postMessage({
|
|
__emnapi__: {
|
|
type: 'async-worker-init',
|
|
payload: { arg, func: [f, globalAddress] }
|
|
}
|
|
});
|
|
}
|
|
catch (err) {
|
|
const arg = args[0];
|
|
emscripten_runtime._free(arg);
|
|
throw err;
|
|
}
|
|
return ret;
|
|
}
|
|
function initWorker(startArg, func) {
|
|
if (emnapi_shared.napiModule.childThread) {
|
|
if (typeof emscripten_runtime.wasmInstance.exports.emnapi_async_worker_init !== 'function') {
|
|
throw new TypeError('`emnapi_async_worker_init` is not exported, please try to add `--export=emnapi_async_worker_init` to linker flags');
|
|
}
|
|
emscripten_runtime.wasmInstance.exports.emnapi_async_worker_init(startArg);
|
|
(emnapiPluginCtx.wasmTable.get(func[0]))(func[1]);
|
|
}
|
|
else {
|
|
throw new Error('startThread is only available in child threads');
|
|
}
|
|
}
|
|
emnapi_shared.napiModule.initWorker = initWorker;
|
|
|
|
//#endregion src/core/async-work.ts
|
|
|
|
exports._emnapi_async_worker = _emnapi_async_worker;
|
|
exports._emnapi_spawn_worker = _emnapi_spawn_worker;
|
|
exports.napi_cancel_async_work = napi_cancel_async_work;
|
|
exports.napi_create_async_work = napi_create_async_work;
|
|
exports.napi_delete_async_work = napi_delete_async_work;
|
|
exports.napi_queue_async_work = napi_queue_async_work;
|
|
|
|
return exports;
|
|
|
|
})({}, emnapiPluginCtx, emnapiPluginCtx);
|
|
return {
|
|
importObject: (original) => {
|
|
|
|
Object.keys(mod).forEach(key => {
|
|
if (key.startsWith('napi_') || key.startsWith('node_api_')) {
|
|
original.napi[key] = mod[key];
|
|
} else {
|
|
original.env[key] = mod[key];
|
|
}
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
//#region src/emnapi/threadsafe-function.js
|
|
function threadsafeFunction (emnapiPluginCtx) {
|
|
const { emnapiCtx, emnapiNodeBinding, _emnapi_node_emit_async_destroy: __emnapi_node_emit_async_destroy, _emnapi_node_emit_async_init: __emnapi_node_emit_async_init, _emnapi_runtime_keepalive_pop: __emnapi_runtime_keepalive_pop, _emnapi_runtime_keepalive_push: __emnapi_runtime_keepalive_push } = emnapiPluginCtx;
|
|
var mod = (function (exports, emnapi_shared, emscripten_runtime) {
|
|
|
|
//#region src/macro.ts
|
|
|
|
//#endregion src/macro.ts
|
|
|
|
//#region src/threadsafe-function.ts
|
|
/* eslint-disable @stylistic/indent */
|
|
/**
|
|
* @__deps malloc
|
|
* @__deps free
|
|
* @__deps $emnapiCtx
|
|
* @__deps $emnapiEnv
|
|
* @__deps $emnapiNodeBinding
|
|
* @__deps _emnapi_node_emit_async_destroy
|
|
* @__deps _emnapi_runtime_keepalive_pop
|
|
* @__postset
|
|
* ```
|
|
* emnapiTSFN.init();
|
|
* ```
|
|
*/
|
|
const emnapiTSFN = {
|
|
_liveSet: {},
|
|
offset: {
|
|
__size__: 0,
|
|
/* napi_ref */ resource: 0,
|
|
/* double */ async_id: 0,
|
|
/* double */ trigger_async_id: 0,
|
|
/* size_t */ queue_size: 0,
|
|
/* bool */ is_some: 0,
|
|
/* void* */ queue: 0,
|
|
// Reuse uv_async_t storage as JS-side wakeup state: pending event + scheduled drain.
|
|
async_pending: 0,
|
|
async_u_fd: 0,
|
|
/* size_t */ thread_count: 0,
|
|
/* int32_t */ state: 0,
|
|
/* atomic_uchar */ dispatch_state: 0,
|
|
/* void* */ context: 0,
|
|
/* size_t */ max_queue_size: 0,
|
|
/* napi_ref */ ref: 0,
|
|
/* napi_env */ env: 0,
|
|
/* void* */ finalize_data: 0,
|
|
/* napi_finalize */ finalize_cb: 0,
|
|
/* napi_threadsafe_function_call_js */ call_js_cb: 0,
|
|
/* bool */ handles_closing: 0,
|
|
/* bool */ async_ref: 0,
|
|
/* int32_t */ mutex: 0,
|
|
/* int32_t */ cond: 0,
|
|
},
|
|
init() {
|
|
emnapiTSFN._liveSet = new Set();
|
|
emnapiTSFN.offset.__size__ = 184 /* NapiTSFNOffset32.__size__ */;
|
|
emnapiTSFN.offset.resource = 0 /* NapiTSFNOffset32.async_resource_resource */;
|
|
emnapiTSFN.offset.async_id = 8 /* NapiTSFNOffset32.async_resource_async_context_async_id */;
|
|
emnapiTSFN.offset.trigger_async_id = 16 /* NapiTSFNOffset32.async_resource_async_context_trigger_async_id */;
|
|
emnapiTSFN.offset.queue_size = 60 /* NapiTSFNOffset32.queue_size */;
|
|
emnapiTSFN.offset.is_some = 24 /* NapiTSFNOffset32.async_resource_is_some */;
|
|
emnapiTSFN.offset.queue = 64 /* NapiTSFNOffset32.queue */;
|
|
emnapiTSFN.offset.async_pending = 132 /* NapiTSFNOffset32.async_pending */;
|
|
emnapiTSFN.offset.async_u_fd = 96 /* NapiTSFNOffset32.async_u_fd */;
|
|
emnapiTSFN.offset.thread_count = 136 /* NapiTSFNOffset32.thread_count */;
|
|
emnapiTSFN.offset.state = 140 /* NapiTSFNOffset32.state */;
|
|
emnapiTSFN.offset.dispatch_state = 144 /* NapiTSFNOffset32.dispatch_state */;
|
|
emnapiTSFN.offset.context = 148 /* NapiTSFNOffset32.context */;
|
|
emnapiTSFN.offset.max_queue_size = 152 /* NapiTSFNOffset32.max_queue_size */;
|
|
emnapiTSFN.offset.ref = 156 /* NapiTSFNOffset32.ref */;
|
|
emnapiTSFN.offset.env = 160 /* NapiTSFNOffset32.env */;
|
|
emnapiTSFN.offset.finalize_data = 164 /* NapiTSFNOffset32.finalize_data */;
|
|
emnapiTSFN.offset.finalize_cb = 168 /* NapiTSFNOffset32.finalize_cb */;
|
|
emnapiTSFN.offset.call_js_cb = 172 /* NapiTSFNOffset32.call_js_cb */;
|
|
emnapiTSFN.offset.handles_closing = 176 /* NapiTSFNOffset32.handles_closing */;
|
|
emnapiTSFN.offset.async_ref = 180 /* NapiTSFNOffset32.async_ref */;
|
|
emnapiTSFN.offset.mutex = 32 /* NapiTSFNOffset32.mutex */;
|
|
emnapiTSFN.offset.cond = 56 /* NapiTSFNOffset32.cond */;
|
|
emnapiTSFN.offset.mutex = emnapiTSFN.offset.mutex + 4;
|
|
if (typeof emscripten_runtime.PThread !== 'undefined') {
|
|
emscripten_runtime.PThread.unusedWorkers.forEach(emnapiTSFN.addListener);
|
|
Object.values(emscripten_runtime.PThread.pthreads).forEach(emnapiTSFN.addListener);
|
|
const __original_getNewWorker = emscripten_runtime.PThread.getNewWorker;
|
|
emscripten_runtime.PThread.getNewWorker = function () {
|
|
const r = __original_getNewWorker.apply(this, arguments);
|
|
emnapiTSFN.addListener(r);
|
|
return r;
|
|
};
|
|
}
|
|
},
|
|
addListener(worker) {
|
|
if (!worker)
|
|
return false;
|
|
if (worker._emnapiTSFNListener)
|
|
return true;
|
|
const handler = function (e) {
|
|
const data = emscripten_runtime.ENVIRONMENT_IS_NODE ? e : e.data;
|
|
const __emnapi__ = data.__emnapi__;
|
|
if (__emnapi__) {
|
|
const type = __emnapi__.type;
|
|
const payload = __emnapi__.payload;
|
|
if (type === 'tsfn-send') {
|
|
const pendng = payload.tsfn + emnapiTSFN.offset.async_pending;
|
|
if (Atomics.load(new Int32Array(emnapiTSFN.ensureBufferFor(pendng + 4)), pendng >>> 2) !== 0) {
|
|
emnapiTSFN.enqueue(payload.tsfn);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
const dispose = function () {
|
|
if (emscripten_runtime.ENVIRONMENT_IS_NODE) {
|
|
worker.off('message', handler);
|
|
}
|
|
else {
|
|
worker.removeEventListener('message', handler, false);
|
|
}
|
|
delete worker._emnapiTSFNListener;
|
|
};
|
|
worker._emnapiTSFNListener = { handler, dispose };
|
|
if (emscripten_runtime.ENVIRONMENT_IS_NODE) {
|
|
worker.on('message', handler);
|
|
}
|
|
else {
|
|
worker.addEventListener('message', handler, false);
|
|
}
|
|
return true;
|
|
},
|
|
/**
|
|
* When another thread grows the shared WebAssembly.Memory, this agent's
|
|
* cached `wasmMemory.buffer` may still have the old shorter length
|
|
* (V8 refreshes it lazily). If a pointer derived from shared memory lies
|
|
* beyond the cached length, `wasmMemory.grow(0)` forces the agent to
|
|
* observe the current memory size and refreshes the buffer.
|
|
*/
|
|
ensureBufferFor(end) {
|
|
let buffer = emscripten_runtime.wasmMemory.buffer;
|
|
if (end > buffer.byteLength) {
|
|
emscripten_runtime.wasmMemory.grow(0);
|
|
buffer = emscripten_runtime.wasmMemory.buffer;
|
|
}
|
|
return buffer;
|
|
},
|
|
initQueue(func) {
|
|
const size = 2 * 4;
|
|
let queue = emscripten_runtime._malloc(size);
|
|
if (!queue)
|
|
return false;
|
|
queue >>>= 0;
|
|
new Uint8Array(emnapiTSFN.ensureBufferFor(queue + size), queue, size).fill(0);
|
|
emnapiTSFN.storeSizeTypeValue(func + emnapiTSFN.offset.queue, queue, false);
|
|
return true;
|
|
},
|
|
destroyQueue(func) {
|
|
const queue = emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.queue, false);
|
|
if (queue) {
|
|
let node = emnapiTSFN.loadSizeTypeValue(queue, false);
|
|
while (node !== 0) {
|
|
const next = emnapiTSFN.loadSizeTypeValue(node + 4, false);
|
|
emscripten_runtime._free(node);
|
|
node = next;
|
|
}
|
|
emscripten_runtime._free(queue);
|
|
}
|
|
},
|
|
pushQueue(func, data) {
|
|
const queue = emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.queue, false);
|
|
const head = emnapiTSFN.loadSizeTypeValue(queue, false);
|
|
const tail = emnapiTSFN.loadSizeTypeValue(queue + 4, false);
|
|
const size = 2 * 4;
|
|
let node = emscripten_runtime._malloc(size);
|
|
if (!node)
|
|
throw new Error('OOM');
|
|
node >>>= 0;
|
|
emnapiTSFN.storeSizeTypeValue(node, data, false);
|
|
emnapiTSFN.storeSizeTypeValue(node + 4, 0, false);
|
|
if (head === 0 && tail === 0) {
|
|
emnapiTSFN.storeSizeTypeValue(queue, node, false);
|
|
emnapiTSFN.storeSizeTypeValue(queue + 4, node, false);
|
|
}
|
|
else {
|
|
emnapiTSFN.storeSizeTypeValue(tail + 4, node, false);
|
|
emnapiTSFN.storeSizeTypeValue(queue + 4, node, false);
|
|
}
|
|
emnapiTSFN.addQueueSize(func);
|
|
},
|
|
shiftQueue(func) {
|
|
const queue = emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.queue, false);
|
|
const head = emnapiTSFN.loadSizeTypeValue(queue, false);
|
|
if (head === 0)
|
|
return 0;
|
|
const node = head;
|
|
const next = emnapiTSFN.loadSizeTypeValue(head + 4, false);
|
|
emnapiTSFN.storeSizeTypeValue(queue, next, false);
|
|
if (next === 0) {
|
|
emnapiTSFN.storeSizeTypeValue(queue + 4, 0, false);
|
|
}
|
|
emnapiTSFN.storeSizeTypeValue(node + 4, 0, false);
|
|
const value = emnapiTSFN.loadSizeTypeValue(node, false);
|
|
emscripten_runtime._free(node);
|
|
emnapiTSFN.subQueueSize(func);
|
|
return value;
|
|
},
|
|
push(func, data, mode) {
|
|
const mutex = emnapiTSFN.getMutex(func);
|
|
const cond = emnapiTSFN.getCond(func);
|
|
const waitCondition = () => {
|
|
const queueSize = emnapiTSFN.getQueueSize(func);
|
|
const maxSize = emnapiTSFN.getMaxQueueSize(func);
|
|
return queueSize >= maxSize && maxSize > 0 && emnapiTSFN.getState(func) === 0 /* State.kOpen */;
|
|
};
|
|
const isBrowserMain = typeof window !== 'undefined' && typeof document !== 'undefined' && !emscripten_runtime.ENVIRONMENT_IS_NODE;
|
|
let shouldDelete = false;
|
|
const ret = mutex.execute(() => {
|
|
while (waitCondition()) {
|
|
if (mode === 0 /* napi_threadsafe_function_call_mode.napi_tsfn_nonblocking */) {
|
|
return 15 /* napi_status.napi_queue_full */;
|
|
}
|
|
/**
|
|
* Browser JS main thread can not use `Atomics.wait`
|
|
*
|
|
* Related:
|
|
* https://github.com/nodejs/node/pull/32689
|
|
* https://github.com/nodejs/node/pull/33453
|
|
*/
|
|
if (isBrowserMain) {
|
|
return 21 /* napi_status.napi_would_deadlock */;
|
|
}
|
|
cond.wait();
|
|
}
|
|
if (emnapiTSFN.getState(func) === 0 /* State.kOpen */) {
|
|
emnapiTSFN.pushQueue(func, data);
|
|
emnapiTSFN.send(func);
|
|
return 0 /* napi_status.napi_ok */;
|
|
}
|
|
if (emnapiTSFN.getThreadCount(func) === 0) {
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
}
|
|
emnapiTSFN.subThreadCount(func);
|
|
if (!(emnapiTSFN.getState(func) === 2 /* State.kClosed */ && emnapiTSFN.getThreadCount(func) === 0)) {
|
|
return 16 /* napi_status.napi_closing */;
|
|
}
|
|
shouldDelete = true;
|
|
return 16 /* napi_status.napi_closing */;
|
|
});
|
|
if (shouldDelete) {
|
|
emnapiTSFN.destroy(func);
|
|
}
|
|
return ret;
|
|
},
|
|
getMutex(func) {
|
|
const index = func + emnapiTSFN.offset.mutex;
|
|
const mutex = {
|
|
lock() {
|
|
const isBrowserMain = typeof window !== 'undefined' && typeof document !== 'undefined' && !emscripten_runtime.ENVIRONMENT_IS_NODE;
|
|
const i32a = new Int32Array(emnapiTSFN.ensureBufferFor(index + 4), index, 1);
|
|
if (isBrowserMain) {
|
|
while (true) {
|
|
const oldValue = Atomics.compareExchange(i32a, 0, 0, 10);
|
|
if (oldValue === 0) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
while (true) {
|
|
const oldValue = Atomics.compareExchange(i32a, 0, 0, 10);
|
|
if (oldValue === 0) {
|
|
return;
|
|
}
|
|
Atomics.wait(i32a, 0, 10);
|
|
}
|
|
}
|
|
},
|
|
/* lockAsync () {
|
|
return new Promise<void>(resolve => {
|
|
const again = (): void => { fn() }
|
|
const fn = (): void => {
|
|
const i32a = new Int32Array(emnapiTSFN.ensureBufferFor(index + 4), index, 1)
|
|
const oldValue = Atomics.compareExchange(i32a, 0, 0, 10)
|
|
if (oldValue === 0) {
|
|
resolve()
|
|
return
|
|
}
|
|
(Atomics as any).waitAsync(i32a, 0, 10).value.then(again)
|
|
}
|
|
fn()
|
|
})
|
|
}, */
|
|
unlock() {
|
|
const i32a = new Int32Array(emnapiTSFN.ensureBufferFor(index + 4), index, 1);
|
|
const oldValue = Atomics.compareExchange(i32a, 0, 10, 0);
|
|
if (oldValue !== 10) {
|
|
throw new Error('Tried to unlock while not holding the mutex');
|
|
}
|
|
Atomics.notify(i32a, 0, 1);
|
|
},
|
|
execute(fn) {
|
|
mutex.lock();
|
|
try {
|
|
return fn();
|
|
}
|
|
finally {
|
|
mutex.unlock();
|
|
}
|
|
} /* ,
|
|
executeAsync<T> (fn: () => Promise<T>): Promise<T> {
|
|
return mutex.lockAsync().then(() => {
|
|
const r = fn()
|
|
mutex.unlock()
|
|
return r
|
|
}, (err) => {
|
|
mutex.unlock()
|
|
throw err
|
|
})
|
|
} */
|
|
};
|
|
return mutex;
|
|
},
|
|
getCond(func) {
|
|
const index = func + emnapiTSFN.offset.cond;
|
|
const mutex = emnapiTSFN.getMutex(func);
|
|
const cond = {
|
|
wait() {
|
|
const i32a = new Int32Array(emnapiTSFN.ensureBufferFor(index + 4), index, 1);
|
|
const value = Atomics.load(i32a, 0);
|
|
mutex.unlock();
|
|
Atomics.wait(i32a, 0, value);
|
|
mutex.lock();
|
|
},
|
|
/* waitAsync () {
|
|
const i32a = new Int32Array(emnapiTSFN.ensureBufferFor(index + 4), index, 1)
|
|
const value = Atomics.load(i32a, 0)
|
|
mutex.unlock()
|
|
const lock = (): Promise<void> => mutex.lockAsync()
|
|
try {
|
|
return (Atomics as any).waitAsync(i32a, 0, value).value.then(lock, lock)
|
|
} catch (err) {
|
|
return lock()
|
|
}
|
|
}, */
|
|
signal() {
|
|
const i32a = new Int32Array(emnapiTSFN.ensureBufferFor(index + 4), index, 1);
|
|
Atomics.add(i32a, 0, 1);
|
|
Atomics.notify(i32a, 0, 1);
|
|
}
|
|
};
|
|
return cond;
|
|
},
|
|
getQueueSize(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.queue_size, true);
|
|
},
|
|
addQueueSize(func) {
|
|
const offset = emnapiTSFN.offset.queue_size;
|
|
let arr, index;
|
|
arr = new Uint32Array(emnapiTSFN.ensureBufferFor(func + offset + 4));
|
|
index = (func + offset) >>> 2;
|
|
Atomics.add(arr, index, 1);
|
|
},
|
|
subQueueSize(func) {
|
|
const offset = emnapiTSFN.offset.queue_size;
|
|
let arr, index;
|
|
arr = new Uint32Array(emnapiTSFN.ensureBufferFor(func + offset + 4));
|
|
index = (func + offset) >>> 2;
|
|
Atomics.sub(arr, index, 1);
|
|
},
|
|
getThreadCount(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.thread_count, true);
|
|
},
|
|
addThreadCount(func) {
|
|
const offset = emnapiTSFN.offset.thread_count;
|
|
let arr, index;
|
|
arr = new Uint32Array(emnapiTSFN.ensureBufferFor(func + offset + 4));
|
|
index = (func + offset) >>> 2;
|
|
Atomics.add(arr, index, 1);
|
|
},
|
|
subThreadCount(func) {
|
|
const offset = emnapiTSFN.offset.thread_count;
|
|
let arr, index;
|
|
arr = new Uint32Array(emnapiTSFN.ensureBufferFor(func + offset + 4));
|
|
index = (func + offset) >>> 2;
|
|
Atomics.sub(arr, index, 1);
|
|
},
|
|
getState(func) {
|
|
return Atomics.load(new Int32Array(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.state + 4)), (func + emnapiTSFN.offset.state) >>> 2);
|
|
},
|
|
setState(func, value) {
|
|
Atomics.store(new Int32Array(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.state + 4)), (func + emnapiTSFN.offset.state) >>> 2, value);
|
|
},
|
|
getHandlesClosing(func) {
|
|
return Atomics.load(new Int8Array(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.handles_closing + 1)), (func + emnapiTSFN.offset.handles_closing));
|
|
},
|
|
setHandlesClosing(func, value) {
|
|
Atomics.store(new Int8Array(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.handles_closing + 1)), (func + emnapiTSFN.offset.handles_closing), value);
|
|
},
|
|
getDispatchState(func) {
|
|
return Atomics.load(new Uint32Array(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.dispatch_state + 4)), (func + emnapiTSFN.offset.dispatch_state) >>> 2);
|
|
},
|
|
getContext(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.context, false);
|
|
},
|
|
getMaxQueueSize(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.max_queue_size, true);
|
|
},
|
|
getEnv(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.env, false);
|
|
},
|
|
getCallJSCb(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.call_js_cb, false);
|
|
},
|
|
getRef(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.ref, false);
|
|
},
|
|
getResource(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.resource, false);
|
|
},
|
|
getFinalizeCb(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.finalize_cb, false);
|
|
},
|
|
getFinalizeData(func) {
|
|
return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.finalize_data, false);
|
|
},
|
|
loadSizeTypeValue(offset, unsigned) {
|
|
let ret;
|
|
let arr;
|
|
if (unsigned) {
|
|
arr = new Uint32Array(emnapiTSFN.ensureBufferFor(offset + 4));
|
|
ret = Atomics.load(arr, offset >>> 2);
|
|
return ret;
|
|
}
|
|
else {
|
|
arr = new Int32Array(emnapiTSFN.ensureBufferFor(offset + 4));
|
|
ret = Atomics.load(arr, offset >>> 2);
|
|
return ret;
|
|
}
|
|
},
|
|
storeSizeTypeValue(offset, value, unsigned) {
|
|
let arr;
|
|
if (unsigned) {
|
|
arr = new Uint32Array(emnapiTSFN.ensureBufferFor(offset + 4));
|
|
Atomics.store(arr, offset >>> 2, value);
|
|
return undefined;
|
|
}
|
|
else {
|
|
arr = new Int32Array(emnapiTSFN.ensureBufferFor(offset + 4));
|
|
Atomics.store(arr, offset >>> 2, value >>> 0);
|
|
return undefined;
|
|
}
|
|
},
|
|
releaseResources(func) {
|
|
if (emnapiTSFN.getState(func) !== 2 /* State.kClosed */) {
|
|
emnapiTSFN.setState(func, 2 /* State.kClosed */);
|
|
emnapiTSFN.getEnv(func);
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
const ref = emnapiTSFN.getRef(func);
|
|
if (ref) {
|
|
emnapiCtx.getRef(ref).dispose();
|
|
}
|
|
const resource = emnapiTSFN.getResource(func);
|
|
emnapiCtx.getRef(resource).dispose();
|
|
emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.is_some + 1);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
GET_HEAP_DATA_VIEW().setInt8(func + emnapiTSFN.offset.is_some, 0, true);
|
|
emnapiCtx.removeCleanupHook(envObject, emnapiTSFN.cleanup, func);
|
|
envObject.unref();
|
|
const asyncRefAddress = func + emnapiTSFN.offset.async_ref;
|
|
const asyncRefOffset = asyncRefAddress >>> 2;
|
|
const arr = new Uint32Array(emnapiTSFN.ensureBufferFor(asyncRefAddress + 4));
|
|
if (Atomics.load(arr, asyncRefOffset) > 0) {
|
|
Atomics.store(arr, asyncRefOffset, 0);
|
|
__emnapi_runtime_keepalive_pop();
|
|
emnapiCtx.decreaseWaitingRequestCounter();
|
|
}
|
|
if (emnapiNodeBinding) {
|
|
const view = new DataView(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.trigger_async_id + 8));
|
|
const asyncId = view.getFloat64(func + emnapiTSFN.offset.async_id, true);
|
|
const triggerAsyncId = view.getFloat64(func + emnapiTSFN.offset.trigger_async_id, true);
|
|
__emnapi_node_emit_async_destroy(asyncId, triggerAsyncId);
|
|
}
|
|
}
|
|
},
|
|
destroy(func) {
|
|
emnapiTSFN._liveSet.delete(func);
|
|
emnapiTSFN.destroyQueue(func);
|
|
emnapiTSFN.releaseResources(func);
|
|
emscripten_runtime._free(func);
|
|
},
|
|
emptyQueue(func) {
|
|
const drainQueue = [];
|
|
emnapiTSFN.getMutex(func).execute(() => {
|
|
while (emnapiTSFN.getQueueSize(func) > 0) {
|
|
drainQueue.push(emnapiTSFN.shiftQueue(func));
|
|
}
|
|
});
|
|
const callJsCb = emnapiTSFN.getCallJSCb(func);
|
|
const context = emnapiTSFN.getContext(func);
|
|
let data;
|
|
for (let i = 0; i < drainQueue.length; i++) {
|
|
data = drainQueue[i];
|
|
if (callJsCb) {
|
|
(emnapiPluginCtx.wasmTable.get(callJsCb))(0, 0, context, data);
|
|
}
|
|
}
|
|
},
|
|
maybeDelete(func) {
|
|
let shouldDelete = false;
|
|
emnapiTSFN.getMutex(func).execute(() => {
|
|
if (emnapiTSFN.getThreadCount(func) > 0) {
|
|
emnapiTSFN.releaseResources(func);
|
|
}
|
|
else {
|
|
shouldDelete = true;
|
|
}
|
|
});
|
|
if (shouldDelete) {
|
|
emnapiTSFN.destroy(func);
|
|
}
|
|
},
|
|
finalize(func) {
|
|
emnapiTSFN.getEnv(func);
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
emnapiCtx.openScope(envObject);
|
|
const finalize = emnapiTSFN.getFinalizeCb(func);
|
|
const data = emnapiTSFN.getFinalizeData(func);
|
|
const context = emnapiTSFN.getContext(func);
|
|
const f = () => {
|
|
envObject.callFinalizerInternal(0, finalize, data, context);
|
|
};
|
|
try {
|
|
emnapiTSFN.emptyQueue(func);
|
|
if (finalize) {
|
|
if (emnapiNodeBinding) {
|
|
const resource = emnapiTSFN.getResource(func);
|
|
const resource_value = emnapiCtx.getRef(resource).get();
|
|
const resourceObject = emnapiCtx.jsValueFromNapiValue(resource_value);
|
|
const view = new DataView(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.trigger_async_id + 8));
|
|
const asyncId = view.getFloat64(func + emnapiTSFN.offset.async_id, true);
|
|
const triggerAsyncId = view.getFloat64(func + emnapiTSFN.offset.trigger_async_id, true);
|
|
emnapiNodeBinding.node.makeCallback(resourceObject, f, [], {
|
|
asyncId,
|
|
triggerAsyncId
|
|
});
|
|
}
|
|
else {
|
|
f();
|
|
}
|
|
}
|
|
emnapiTSFN.maybeDelete(func);
|
|
}
|
|
finally {
|
|
emnapiCtx.closeScope(envObject);
|
|
}
|
|
},
|
|
cleanup(func) {
|
|
emnapiTSFN.closeHandlesAndMaybeDelete(func, 1);
|
|
},
|
|
closeHandlesAndMaybeDelete(func, set_closing) {
|
|
emnapiTSFN.getEnv(func);
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
emnapiCtx.openScope(envObject);
|
|
try {
|
|
if (set_closing) {
|
|
emnapiTSFN.getMutex(func).execute(() => {
|
|
emnapiTSFN.setState(func, 1 /* State.kClosing */);
|
|
if (emnapiTSFN.getMaxQueueSize(func) > 0) {
|
|
emnapiTSFN.getCond(func).signal();
|
|
}
|
|
});
|
|
}
|
|
if (emnapiTSFN.getHandlesClosing(func)) {
|
|
return;
|
|
}
|
|
emnapiTSFN.setHandlesClosing(func, 1);
|
|
Atomics.store(new Int32Array(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.async_pending + 4)), (func + emnapiTSFN.offset.async_pending) >>> 2, 1);
|
|
emnapiCtx.features.setImmediate(() => {
|
|
emnapiTSFN.finalize(func);
|
|
});
|
|
}
|
|
finally {
|
|
emnapiCtx.closeScope(envObject);
|
|
}
|
|
},
|
|
dispatchOne(func) {
|
|
let data = 0;
|
|
let popped_value = false;
|
|
let has_more = false;
|
|
const mutex = emnapiTSFN.getMutex(func);
|
|
const cond = emnapiTSFN.getCond(func);
|
|
mutex.execute(() => {
|
|
if (emnapiTSFN.getState(func) === 0 /* State.kOpen */) {
|
|
let size = emnapiTSFN.getQueueSize(func);
|
|
if (size > 0) {
|
|
data = emnapiTSFN.shiftQueue(func);
|
|
popped_value = true;
|
|
const maxQueueSize = emnapiTSFN.getMaxQueueSize(func);
|
|
if (size === maxQueueSize && maxQueueSize > 0) {
|
|
cond.signal();
|
|
}
|
|
size--;
|
|
}
|
|
if (size === 0) {
|
|
if (emnapiTSFN.getThreadCount(func) === 0) {
|
|
emnapiTSFN.setState(func, 1 /* State.kClosing */);
|
|
if (emnapiTSFN.getMaxQueueSize(func) > 0) {
|
|
cond.signal();
|
|
}
|
|
emnapiTSFN.closeHandlesAndMaybeDelete(func, 0);
|
|
}
|
|
}
|
|
else {
|
|
has_more = true;
|
|
}
|
|
}
|
|
else {
|
|
emnapiTSFN.closeHandlesAndMaybeDelete(func, 0);
|
|
}
|
|
});
|
|
if (popped_value) {
|
|
const env = emnapiTSFN.getEnv(func);
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
emnapiCtx.openScope(envObject);
|
|
const f = () => {
|
|
envObject.callbackIntoModule(false, () => {
|
|
const callJsCb = emnapiTSFN.getCallJSCb(func);
|
|
const ref = emnapiTSFN.getRef(func);
|
|
const js_callback = ref ? emnapiCtx.getRef(ref).get() : 0;
|
|
if (callJsCb) {
|
|
const context = emnapiTSFN.getContext(func);
|
|
(emnapiPluginCtx.wasmTable.get(callJsCb))(env, js_callback, context, data);
|
|
}
|
|
else {
|
|
const jsCallback = js_callback ? emnapiCtx.jsValueFromNapiValue(js_callback) : null;
|
|
if (typeof jsCallback === 'function') {
|
|
jsCallback();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
try {
|
|
if (emnapiNodeBinding) {
|
|
const resource = emnapiTSFN.getResource(func);
|
|
const resource_value = emnapiCtx.getRef(resource).get();
|
|
const resourceObject = emnapiCtx.jsValueFromNapiValue(resource_value);
|
|
const view = new DataView(emnapiTSFN.ensureBufferFor(func + emnapiTSFN.offset.trigger_async_id + 8));
|
|
emnapiNodeBinding.node.makeCallback(resourceObject, f, [], {
|
|
asyncId: view.getFloat64(func + emnapiTSFN.offset.async_id, true),
|
|
triggerAsyncId: view.getFloat64(func + emnapiTSFN.offset.trigger_async_id, true)
|
|
});
|
|
}
|
|
else {
|
|
f();
|
|
}
|
|
}
|
|
finally {
|
|
emnapiCtx.closeScope(envObject);
|
|
}
|
|
}
|
|
return has_more;
|
|
},
|
|
dispatch(func) {
|
|
let has_more = true;
|
|
let iterations_left = 1000;
|
|
const dispatchStateAddress = func + emnapiTSFN.offset.dispatch_state;
|
|
const index = dispatchStateAddress >>> 2;
|
|
while (has_more && --iterations_left !== 0) {
|
|
Atomics.store(new Uint32Array(emnapiTSFN.ensureBufferFor(dispatchStateAddress + 4)), index, 1);
|
|
try {
|
|
has_more = emnapiTSFN.dispatchOne(func);
|
|
}
|
|
catch (err) {
|
|
// A throwing callback must not leave dispatch_state marked as
|
|
// dispatching, or every later send() would be swallowed. Re-arm
|
|
// the state and schedule another drain before rethrowing.
|
|
if (emnapiTSFN._liveSet.has(func)) {
|
|
Atomics.exchange(new Uint32Array(emnapiTSFN.ensureBufferFor(dispatchStateAddress + 4)), index, 0);
|
|
emnapiTSFN.send(func);
|
|
}
|
|
throw err;
|
|
}
|
|
if (Atomics.exchange(new Uint32Array(emnapiTSFN.ensureBufferFor(dispatchStateAddress + 4)), index, 0) !== 1) {
|
|
has_more = true;
|
|
}
|
|
}
|
|
if (has_more) {
|
|
emnapiTSFN.send(func);
|
|
}
|
|
},
|
|
enqueue(func) {
|
|
// `pending` means a worker thread has requested a wakeup that has not
|
|
// been drained on the main thread yet.
|
|
const pending = func + emnapiTSFN.offset.async_pending;
|
|
// `scheduled` prevents queueing the same main-thread drain chain more than
|
|
// once while a previous wakeup is still in flight.
|
|
const scheduled = func + emnapiTSFN.offset.async_u_fd;
|
|
const end = Math.max(pending, scheduled) + 4;
|
|
const state = () => new Int32Array(emnapiTSFN.ensureBufferFor(end));
|
|
if (Atomics.exchange(state(), scheduled >>> 2, 1) !== 0) {
|
|
return;
|
|
}
|
|
// Match uv_async_send-style coalescing in JS: the first turn represents
|
|
// the wakeup reaching the main thread, and the second turn performs the
|
|
// actual TSFN drain after nearby Send/Signal calls have had a chance to
|
|
// collapse into the shared AsyncProgressWorker state.
|
|
emnapiCtx.features.setImmediate(() => {
|
|
if (!emnapiTSFN._liveSet.has(func)) {
|
|
return;
|
|
}
|
|
if (Atomics.load(state(), pending >>> 2) === 0) {
|
|
Atomics.store(state(), scheduled >>> 2, 0);
|
|
return;
|
|
}
|
|
emnapiCtx.features.setImmediate(() => {
|
|
// After destroy(), the func address is freed. Skip the atomics
|
|
// on that address entirely to avoid use-after-free (JS-side
|
|
// lifecycle check must run before touching the state words).
|
|
if (!emnapiTSFN._liveSet.has(func)) {
|
|
return;
|
|
}
|
|
try {
|
|
// Consume the coalesced wakeup once, then let dispatch() observe any
|
|
// queue mutations through dispatch_state like the C implementation.
|
|
if (Atomics.exchange(state(), pending >>> 2, 0) === 0) {
|
|
return;
|
|
}
|
|
emnapiTSFN.dispatch(func);
|
|
}
|
|
finally {
|
|
// Allow a later wakeup to schedule a new drain chain. If another
|
|
// worker-thread send raced with this drain, enqueue one more turn.
|
|
if (emnapiTSFN._liveSet.has(func)) {
|
|
Atomics.store(state(), scheduled >>> 2, 0);
|
|
if (Atomics.load(state(), pending >>> 2) !== 0) {
|
|
emnapiTSFN.enqueue(func);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
},
|
|
send(func) {
|
|
const dispatchStateAddress = func + emnapiTSFN.offset.dispatch_state;
|
|
const current_state = Atomics.or(new Uint32Array(emnapiTSFN.ensureBufferFor(dispatchStateAddress + 4)), dispatchStateAddress >>> 2, 1 << 1);
|
|
if ((current_state & 1) === 1) {
|
|
return;
|
|
}
|
|
const pendng = func + emnapiTSFN.offset.async_pending;
|
|
// A wakeup is already pending, so this send only needs to leave the queued
|
|
// work in the TSFN queue and let the existing drain pick it up.
|
|
if (Atomics.load(new Int32Array(emnapiTSFN.ensureBufferFor(pendng + 4)), pendng >>> 2) !== 0) {
|
|
return;
|
|
}
|
|
if (Atomics.exchange(new Int32Array(emnapiTSFN.ensureBufferFor(pendng + 4)), pendng >>> 2, 1) === 0) {
|
|
if ((typeof emscripten_runtime.ENVIRONMENT_IS_PTHREAD !== 'undefined') && emscripten_runtime.ENVIRONMENT_IS_PTHREAD) {
|
|
// Worker threads only post a wakeup token. Main-thread draining is
|
|
// serialized by enqueue() once the message is received.
|
|
postMessage({
|
|
__emnapi__: {
|
|
type: 'tsfn-send',
|
|
payload: {
|
|
tsfn: func
|
|
}
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
// On the main thread we can skip the cross-thread hop and schedule the
|
|
// coalesced drain chain directly.
|
|
emnapiTSFN.enqueue(func);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
emnapiTSFN.init();
|
|
emnapiPluginCtx.emnapiTSFN = emnapiTSFN;
|
|
/**
|
|
* @__deps _emnapi_node_emit_async_init
|
|
* @__deps _emnapi_runtime_keepalive_push
|
|
* @__sig ippppppppppp
|
|
*/
|
|
function napi_create_threadsafe_function(env, func, async_resource, async_resource_name, max_queue_size, initial_thread_count, thread_finalize_data, thread_finalize_cb, context, call_js_cb, result) {
|
|
if (!env)
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
// @ts-expect-error
|
|
const envObject = emnapi_shared.emnapiEnv;
|
|
envObject.checkGCAccess();
|
|
if (!async_resource_name)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
max_queue_size >>>= 0;
|
|
initial_thread_count >>>= 0;
|
|
env >>>= 0;
|
|
thread_finalize_data >>>= 0;
|
|
thread_finalize_cb >>>= 0;
|
|
context >>>= 0;
|
|
call_js_cb >>>= 0;
|
|
max_queue_size = max_queue_size >>> 0;
|
|
initial_thread_count = initial_thread_count >>> 0;
|
|
if (initial_thread_count === 0) {
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
}
|
|
if (!result)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
let ref = 0;
|
|
func >>>= 0;
|
|
if (!func) {
|
|
if (!call_js_cb)
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
}
|
|
else {
|
|
const funcValue = emnapiCtx.jsValueFromNapiValue(func);
|
|
if (typeof funcValue !== 'function') {
|
|
return envObject.setLastError(1 /* napi_status.napi_invalid_arg */);
|
|
}
|
|
ref = emnapiCtx.createReference(envObject, func, 1, 1 /* ReferenceOwnership.kUserland */).id;
|
|
}
|
|
let asyncResourceObject;
|
|
if (async_resource) {
|
|
asyncResourceObject = emnapiCtx.jsValueFromNapiValue(async_resource);
|
|
if (asyncResourceObject == null) {
|
|
return envObject.setLastError(2 /* napi_status.napi_object_expected */);
|
|
}
|
|
asyncResourceObject = Object(asyncResourceObject);
|
|
}
|
|
else {
|
|
asyncResourceObject = {};
|
|
}
|
|
const resource = emnapiCtx.napiValueFromJsValue(asyncResourceObject);
|
|
let asyncResourceName = emnapiCtx.jsValueFromNapiValue(async_resource_name);
|
|
if (typeof asyncResourceName === 'symbol') {
|
|
return envObject.setLastError(3 /* napi_status.napi_string_expected */);
|
|
}
|
|
asyncResourceName = String(asyncResourceName);
|
|
const resource_name = emnapiCtx.napiValueFromJsValue(asyncResourceName);
|
|
// tsfn create
|
|
const sizeofTSFN = emnapiTSFN.offset.__size__;
|
|
let tsfn = emscripten_runtime._malloc(sizeofTSFN);
|
|
if (!tsfn)
|
|
return envObject.setLastError(9 /* napi_status.napi_generic_failure */);
|
|
tsfn >>>= 0;
|
|
new Uint8Array(emnapiTSFN.ensureBufferFor(tsfn + sizeofTSFN)).subarray(tsfn, tsfn + sizeofTSFN).fill(0);
|
|
const resourceRef = emnapiCtx.createReference(envObject, resource, 1, 1 /* ReferenceOwnership.kUserland */);
|
|
const resource_ = resourceRef.id;
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.resource, resource_, true);
|
|
if (!emnapiTSFN.initQueue(tsfn)) {
|
|
emscripten_runtime._free(tsfn);
|
|
resourceRef.dispose();
|
|
return envObject.setLastError(9 /* napi_status.napi_generic_failure */);
|
|
}
|
|
__emnapi_node_emit_async_init(resource, resource_name, -1, tsfn + emnapiTSFN.offset.async_id);
|
|
GET_HEAP_DATA_VIEW().setInt8(tsfn + emnapiTSFN.offset.is_some, 1, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.thread_count, initial_thread_count, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.context, context, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.max_queue_size, max_queue_size, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.ref, ref, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.env, env, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.finalize_data, thread_finalize_data, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.finalize_cb, thread_finalize_cb, true);
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.call_js_cb, call_js_cb, true);
|
|
emnapiCtx.addCleanupHook(envObject, emnapiTSFN.cleanup, tsfn);
|
|
emnapiTSFN._liveSet.add(tsfn);
|
|
envObject.ref();
|
|
__emnapi_runtime_keepalive_push();
|
|
emnapiCtx.increaseWaitingRequestCounter();
|
|
GET_HEAP_DATA_VIEW().setUint32(tsfn + emnapiTSFN.offset.async_ref, 1, true);
|
|
result >>>= 0;
|
|
GET_HEAP_DATA_VIEW().setUint32(result, tsfn, true);
|
|
return envObject.clearLastError();
|
|
}
|
|
/** @__sig ipp */
|
|
function napi_get_threadsafe_function_context(func, result) {
|
|
if (!func || !result) {
|
|
emscripten_runtime.abort();
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
}
|
|
func >>>= 0;
|
|
result >>>= 0;
|
|
const context = emnapiTSFN.getContext(func);
|
|
var HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer), GET_HEAP_DATA_VIEW = () => HEAP_DATA_VIEW.buffer === emnapiPluginCtx.wasmMemory.buffer ? HEAP_DATA_VIEW : (HEAP_DATA_VIEW = new DataView(emnapiPluginCtx.wasmMemory.buffer));
|
|
GET_HEAP_DATA_VIEW().setUint32(result, context, true);
|
|
return 0 /* napi_status.napi_ok */;
|
|
}
|
|
/** @__sig ippi */
|
|
function napi_call_threadsafe_function(func, data, mode) {
|
|
if (!func) {
|
|
emscripten_runtime.abort();
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
}
|
|
func >>>= 0;
|
|
data >>>= 0;
|
|
return emnapiTSFN.push(func, data, mode);
|
|
}
|
|
/** @__sig ip */
|
|
function napi_acquire_threadsafe_function(func) {
|
|
if (!func) {
|
|
emscripten_runtime.abort();
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
}
|
|
func >>>= 0;
|
|
const mutex = emnapiTSFN.getMutex(func);
|
|
return mutex.execute(() => {
|
|
if (emnapiTSFN.getState(func) === 0 /* State.kOpen */) {
|
|
emnapiTSFN.addThreadCount(func);
|
|
return 0 /* napi_status.napi_ok */;
|
|
}
|
|
return 16 /* napi_status.napi_closing */;
|
|
});
|
|
}
|
|
/** @__sig ipi */
|
|
function napi_release_threadsafe_function(func, mode) {
|
|
if (!func) {
|
|
emscripten_runtime.abort();
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
}
|
|
func >>>= 0;
|
|
const mutex = emnapiTSFN.getMutex(func);
|
|
const cond = emnapiTSFN.getCond(func);
|
|
let shouldDelete = false;
|
|
const ret = mutex.execute(() => {
|
|
if (emnapiTSFN.getThreadCount(func) === 0) {
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
}
|
|
emnapiTSFN.subThreadCount(func);
|
|
if (emnapiTSFN.getThreadCount(func) === 0 || mode === 1 /* napi_threadsafe_function_release_mode.napi_tsfn_abort */) {
|
|
if (emnapiTSFN.getState(func) === 0 /* State.kOpen */) {
|
|
if (mode === 1 /* napi_threadsafe_function_release_mode.napi_tsfn_abort */) {
|
|
emnapiTSFN.setState(func, 1 /* State.kClosing */);
|
|
}
|
|
if (emnapiTSFN.getState(func) === 1 /* State.kClosing */ && emnapiTSFN.getMaxQueueSize(func) > 0) {
|
|
cond.signal();
|
|
}
|
|
emnapiTSFN.send(func);
|
|
}
|
|
}
|
|
if (!(emnapiTSFN.getState(func) === 2 /* State.kClosed */ && emnapiTSFN.getThreadCount(func) === 0)) {
|
|
return 0 /* napi_status.napi_ok */;
|
|
}
|
|
shouldDelete = true;
|
|
return 0 /* napi_status.napi_ok */;
|
|
});
|
|
if (shouldDelete) {
|
|
emnapiTSFN.destroy(func);
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* @__deps _emnapi_runtime_keepalive_pop
|
|
* @__sig ipp
|
|
*/
|
|
function napi_unref_threadsafe_function(env, func) {
|
|
if (!func) {
|
|
emscripten_runtime.abort();
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
}
|
|
func >>>= 0;
|
|
const asyncRefAddress = func + emnapiTSFN.offset.async_ref;
|
|
const asyncRefOffset = asyncRefAddress >>> 2;
|
|
const arr = new Uint32Array(emnapiTSFN.ensureBufferFor(asyncRefAddress + 4));
|
|
const currentValue = Atomics.load(arr, asyncRefOffset);
|
|
if (currentValue > 0) {
|
|
Atomics.store(arr, asyncRefOffset, currentValue - 1);
|
|
if (currentValue === 1) {
|
|
__emnapi_runtime_keepalive_pop();
|
|
emnapiCtx.decreaseWaitingRequestCounter();
|
|
}
|
|
}
|
|
return 0 /* napi_status.napi_ok */;
|
|
}
|
|
/**
|
|
* @__deps _emnapi_runtime_keepalive_push
|
|
* @__sig ipp
|
|
*/
|
|
function napi_ref_threadsafe_function(env, func) {
|
|
if (!func) {
|
|
emscripten_runtime.abort();
|
|
return 1 /* napi_status.napi_invalid_arg */;
|
|
}
|
|
func >>>= 0;
|
|
const asyncRefAddress = func + emnapiTSFN.offset.async_ref;
|
|
const asyncRefOffset = asyncRefAddress >>> 2;
|
|
const arr = new Uint32Array(emnapiTSFN.ensureBufferFor(asyncRefAddress + 4));
|
|
const currentValue = Atomics.load(arr, asyncRefOffset);
|
|
if (!currentValue) {
|
|
__emnapi_runtime_keepalive_push();
|
|
emnapiCtx.increaseWaitingRequestCounter();
|
|
}
|
|
Atomics.store(arr, asyncRefOffset, currentValue + 1);
|
|
return 0 /* napi_status.napi_ok */;
|
|
}
|
|
|
|
//#endregion src/threadsafe-function.ts
|
|
|
|
exports.napi_acquire_threadsafe_function = napi_acquire_threadsafe_function;
|
|
exports.napi_call_threadsafe_function = napi_call_threadsafe_function;
|
|
exports.napi_create_threadsafe_function = napi_create_threadsafe_function;
|
|
exports.napi_get_threadsafe_function_context = napi_get_threadsafe_function_context;
|
|
exports.napi_ref_threadsafe_function = napi_ref_threadsafe_function;
|
|
exports.napi_release_threadsafe_function = napi_release_threadsafe_function;
|
|
exports.napi_unref_threadsafe_function = napi_unref_threadsafe_function;
|
|
|
|
return exports;
|
|
|
|
})({}, emnapiPluginCtx, emnapiPluginCtx);
|
|
return {
|
|
importObject: (original) => {
|
|
Object.assign(original.napi, mod);
|
|
}
|
|
};
|
|
}
|
|
|
|
export { asyncWork as emnapiAsyncWorkPlugin, threadsafeFunction as emnapiTSFNPlugin };
|