'use strict'; //#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(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 (fn: () => Promise): Promise { 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 => 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); } }; } exports.emnapiAsyncWorkPlugin = asyncWork; exports.emnapiTSFNPlugin = threadsafeFunction;