Buckets:
| /** | |
| * @license | |
| * Copyright 2014 The Emscripten Authors | |
| * SPDX-License-Identifier: MIT | |
| */ | |
| // | |
| // Async support via ASYNCIFY | |
| // | |
| addToLibrary({ | |
| // error handling | |
| $runAndAbortIfError: (func) => { | |
| try { | |
| return func(); | |
| } catch (e) { | |
| abort(e); | |
| } | |
| }, | |
| #if ASYNCIFY | |
| $Asyncify__deps: ['$runAndAbortIfError', '$callUserCallback', | |
| #if ASSERTIONS | |
| '$createNamedFunction', | |
| #endif | |
| #if !MINIMAL_RUNTIME | |
| '$runtimeKeepalivePush', '$runtimeKeepalivePop', | |
| #endif | |
| #if ASYNCIFY == 1 | |
| // Needed by allocateData and handleSleep respectively | |
| 'malloc', 'free', | |
| #endif | |
| ], | |
| $Asyncify: { | |
| // | |
| // Asyncify code that is shared between mode 1 (original) and mode 2 (JSPI). | |
| // | |
| #if ASYNCIFY == 1 && MEMORY64 | |
| rewindArguments: new Map(), | |
| #endif | |
| instrumentWasmImports(imports) { | |
| #if EMBIND_GEN_MODE | |
| // Instrumenting is not needed when generating code. | |
| return imports; | |
| #endif | |
| #if ASYNCIFY_DEBUG | |
| dbg('asyncify instrumenting imports'); | |
| #endif | |
| #if ASSERTIONS && ASYNCIFY == 2 | |
| assert('Suspending' in WebAssembly, 'JSPI not supported by current environment. Perhaps it needs to be enabled via flags?'); | |
| #endif | |
| var importPattern = {{{ new RegExp(`^(${ASYNCIFY_IMPORTS_EXCEPT_JS_LIBS.map(x => x.split('.')[1]).join('|').replace(/\*/g, '.*')})$`) }}}; | |
| for (let [x, original] of Object.entries(imports)) { | |
| if (typeof original == 'function') { | |
| let isAsyncifyImport = original.isAsync || importPattern.test(x); | |
| #if ASYNCIFY == 2 | |
| // Wrap async imports with a suspending WebAssembly function. | |
| if (isAsyncifyImport) { | |
| #if ASYNCIFY_DEBUG | |
| dbg('asyncify: suspendOnReturnedPromise for', x, original); | |
| #endif | |
| imports[x] = original = new WebAssembly.Suspending(original); | |
| } | |
| #endif | |
| #if ASSERTIONS && ASYNCIFY != 2 // We cannot apply assertions with stack switching, as the imports must not be modified from suspender.suspendOnReturnedPromise TODO find a way | |
| imports[x] = (...args) => { | |
| var originalAsyncifyState = Asyncify.state; | |
| try { | |
| return original(...args); | |
| } finally { | |
| // Only asyncify-declared imports are allowed to change the | |
| // state. | |
| // Changing the state from normal to disabled is allowed (in any | |
| // function) as that is what shutdown does (and we don't have an | |
| // explicit list of shutdown imports). | |
| var changedToDisabled = | |
| originalAsyncifyState === Asyncify.State.Normal && | |
| Asyncify.state === Asyncify.State.Disabled; | |
| // invoke_* functions are allowed to change the state if we do | |
| // not ignore indirect calls. | |
| var ignoredInvoke = x.startsWith('invoke_') && | |
| {{{ !ASYNCIFY_IGNORE_INDIRECT }}}; | |
| if (Asyncify.state !== originalAsyncifyState && | |
| !isAsyncifyImport && | |
| !changedToDisabled && | |
| !ignoredInvoke) { | |
| abort(`import ${x} was not in ASYNCIFY_IMPORTS, but changed the state`); | |
| } | |
| } | |
| }; | |
| #if MAIN_MODULE | |
| // The dynamic library loader needs to be able to read .sig | |
| // properties, so that it knows function signatures when it adds | |
| // them to the table. | |
| imports[x].sig = original.sig; | |
| #endif // MAIN_MODULE | |
| #endif // ASSERTIONS | |
| } | |
| } | |
| }, | |
| #if ASYNCIFY == 1 && MEMORY64 | |
| saveRewindArguments(func, passedArguments) { | |
| return Asyncify.rewindArguments.set(func, Array.from(passedArguments)); | |
| }, | |
| restoreRewindArguments(func) { | |
| #if ASSERTIONS | |
| assert(Asyncify.rewindArguments.has(func)); | |
| #endif | |
| return Asyncify.rewindArguments.get(func); | |
| }, | |
| #endif | |
| #if ASYNCIFY == 1 | |
| instrumentFunction(original) { | |
| var wrapper = (...args) => { | |
| #if ASYNCIFY_DEBUG >= 2 | |
| dbg(`ASYNCIFY: ${' '.repeat(Asyncify.exportCallStack.length)} try ${original}`); | |
| #endif | |
| Asyncify.exportCallStack.push(original); | |
| try { | |
| #if MEMORY64 | |
| Asyncify.saveRewindArguments(original, args); | |
| #endif | |
| return original(...args); | |
| } finally { | |
| if (!ABORT) { | |
| var top = Asyncify.exportCallStack.pop(); | |
| #if ASSERTIONS | |
| assert(top === original); | |
| #endif | |
| #if ASYNCIFY_DEBUG >= 2 | |
| dbg(`ASYNCIFY: ${' '.repeat(Asyncify.exportCallStack.length)} finally ${original}`); | |
| #endif | |
| Asyncify.maybeStopUnwind(); | |
| } | |
| } | |
| }; | |
| Asyncify.funcWrappers.set(original, wrapper); | |
| #if MAIN_MODULE | |
| wrapper.orig = original; | |
| #endif | |
| #if ASSERTIONS | |
| wrapper = createNamedFunction(`__asyncify_wrapper_${original.name}`, wrapper); | |
| #endif | |
| return wrapper; | |
| }, | |
| #endif // ASYNCIFY == 1 | |
| instrumentWasmExports(exports) { | |
| #if EMBIND_GEN_MODE | |
| // Instrumenting is not needed when generating code. | |
| return exports; | |
| #endif | |
| #if ASYNCIFY_DEBUG | |
| dbg('asyncify instrumenting exports'); | |
| #endif | |
| #if ASYNCIFY == 2 | |
| var exportPattern = {{{ new RegExp(`^(${ASYNCIFY_EXPORTS.join('|').replace(/\*/g, '.*')})$`) }}}; | |
| Asyncify.asyncExports = new Set(); | |
| #endif | |
| var ret = {}; | |
| for (let [x, original] of Object.entries(exports)) { | |
| if (typeof original == 'function') { | |
| #if ASYNCIFY == 2 | |
| // Wrap all exports with a promising WebAssembly function. | |
| let isAsyncifyExport = exportPattern.test(x); | |
| if (isAsyncifyExport) { | |
| Asyncify.asyncExports.add(original); | |
| original = Asyncify.makeAsyncFunction(original); | |
| } | |
| ret[x] = original; | |
| #else | |
| var wrapper = Asyncify.instrumentFunction(original); | |
| ret[x] = wrapper; | |
| #endif | |
| } else { | |
| ret[x] = original; | |
| } | |
| } | |
| return ret; | |
| }, | |
| #if ASYNCIFY == 1 | |
| // | |
| // Original implementation of Asyncify. | |
| // | |
| State: { | |
| Normal: 0, | |
| Unwinding: 1, | |
| Rewinding: 2, | |
| Disabled: 3, | |
| }, | |
| state: 0, | |
| StackSize: {{{ ASYNCIFY_STACK_SIZE }}}, | |
| currData: null, | |
| // The return value passed to wakeUp() in | |
| // Asyncify.handleSleep((wakeUp) => {...}) is stored here, | |
| // so we can return it later from the C function that called | |
| // Asyncify.handleSleep() after rewinding finishes. | |
| handleSleepReturnValue: 0, | |
| // We must track which wasm exports are called into and | |
| // exited, so that we know where the call stack began, | |
| // which is where we must call to rewind it. | |
| // This list contains the original Wasm exports. | |
| exportCallStack: [], | |
| callstackFuncToId: new Map(), | |
| callStackIdToFunc: new Map(), | |
| // Maps wasm functions to their corresponding wrapper function. | |
| funcWrappers: new Map(), | |
| callStackId: 0, | |
| asyncPromiseHandlers: null, // { resolve, reject } pair for when *all* asynchronicity is done | |
| sleepCallbacks: [], // functions to call every time we sleep | |
| getCallStackId(func) { | |
| #if ASSERTIONS | |
| assert(func); | |
| #endif | |
| if (!Asyncify.callstackFuncToId.has(func)) { | |
| var id = Asyncify.callStackId++; | |
| Asyncify.callstackFuncToId.set(func, id); | |
| Asyncify.callStackIdToFunc.set(id, func); | |
| } | |
| return Asyncify.callstackFuncToId.get(func); | |
| }, | |
| maybeStopUnwind() { | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY: maybe stop unwind', Asyncify.exportCallStack); | |
| #endif | |
| if (Asyncify.currData && | |
| Asyncify.state === Asyncify.State.Unwinding && | |
| Asyncify.exportCallStack.length === 0) { | |
| // We just finished unwinding. | |
| // Be sure to set the state before calling any other functions to avoid | |
| // possible infinite recursion here (For example in debug pthread builds | |
| // the dbg() function itself can call back into WebAssembly to get the | |
| // current pthread_self() pointer). | |
| Asyncify.state = Asyncify.State.Normal; | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY: stop unwind'); | |
| #endif | |
| {{{ runtimeKeepalivePush(); }}} | |
| // Keep the runtime alive so that a re-wind can be done later. | |
| runAndAbortIfError(_asyncify_stop_unwind); | |
| if (typeof Fibers != 'undefined') { | |
| Fibers.trampoline(); | |
| } | |
| } | |
| }, | |
| whenDone() { | |
| #if ASSERTIONS | |
| assert(Asyncify.currData, 'tried to wait for an async operation when none is in progress'); | |
| assert(!Asyncify.asyncPromiseHandlers, 'cannot have multiple async operations in flight at once'); | |
| #endif | |
| return new Promise((resolve, reject) => { | |
| Asyncify.asyncPromiseHandlers = { resolve, reject }; | |
| }); | |
| }, | |
| allocateData() { | |
| // An asyncify data structure has three fields: | |
| // 0 current stack pos | |
| // 4 max stack pos | |
| // 8 id of function at bottom of the call stack (callStackIdToFunc[id] == wasm func) | |
| // | |
| // The Asyncify ABI only interprets the first two fields, the rest is for the runtime. | |
| // We also embed a stack in the same memory region here, right next to the structure. | |
| // This struct is also defined as asyncify_data_t in emscripten/fiber.h | |
| var ptr = _malloc({{{ C_STRUCTS.asyncify_data_s.__size__ }}} + Asyncify.StackSize); | |
| Asyncify.setDataHeader(ptr, ptr + {{{ C_STRUCTS.asyncify_data_s.__size__ }}}, Asyncify.StackSize); | |
| Asyncify.setDataRewindFunc(ptr); | |
| return ptr; | |
| }, | |
| setDataHeader(ptr, stack, stackSize) { | |
| {{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_ptr, 'stack', '*') }}}; | |
| {{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_limit, 'stack + stackSize', '*') }}}; | |
| }, | |
| setDataRewindFunc(ptr) { | |
| var bottomOfCallStack = Asyncify.exportCallStack[0]; | |
| #if ASYNCIFY_DEBUG >= 2 | |
| dbg(`ASYNCIFY: setDataRewindFunc(${ptr}), bottomOfCallStack is`, bottomOfCallStack, new Error().stack); | |
| #endif | |
| #if ASSERTIONS | |
| assert(bottomOfCallStack, 'exportCallStack is empty'); | |
| #endif | |
| var rewindId = Asyncify.getCallStackId(bottomOfCallStack); | |
| {{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'rewindId', 'i32') }}}; | |
| }, | |
| getDataRewindFunc(ptr) { | |
| var id = {{{ makeGetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'i32') }}}; | |
| var func = Asyncify.callStackIdToFunc.get(id); | |
| #if ASSERTIONS | |
| assert(func, `id ${id} not found in callStackIdToFunc`); | |
| #endif | |
| return func; | |
| }, | |
| doRewind(ptr) { | |
| var original = Asyncify.getDataRewindFunc(ptr); | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY: doRewind:', original); | |
| #endif | |
| var func = Asyncify.funcWrappers.get(original); | |
| #if ASSERTIONS | |
| assert(original); | |
| assert(func); | |
| #endif | |
| // Once we have rewound and the stack we no longer need to artificially | |
| // keep the runtime alive. | |
| {{{ runtimeKeepalivePop(); }}} | |
| #if MEMORY64 | |
| // When re-winding, the arguments to a function are ignored. For i32 arguments we | |
| // can just call the function with no args at all since the engine will produce zeros | |
| // for all arguments. However, for i64 arguments we get `undefined cannot be converted to | |
| // BigInt`. | |
| func = func.bind(0, ...Asyncify.restoreRewindArguments(original)); | |
| #endif | |
| return callUserCallback(func); | |
| }, | |
| // This receives a function to call to start the async operation, and | |
| // handles everything else for the user of this API. See emscripten_sleep() | |
| // and other async methods for simple examples of usage. | |
| handleSleep(startAsync) { | |
| #if ASSERTIONS | |
| assert(Asyncify.state !== Asyncify.State.Disabled, 'handleSleep called after Asyncify was shut down'); | |
| #endif | |
| if (ABORT) return; | |
| #if ASYNCIFY_DEBUG | |
| dbg(`ASYNCIFY: handleSleep ${Asyncify.state}`); | |
| #endif | |
| if (Asyncify.state === Asyncify.State.Normal) { | |
| // Prepare to sleep. Call startAsync, and see what happens: | |
| // if the code decided to call our callback synchronously, | |
| // then no async operation was in fact begun, and we don't | |
| // need to do anything. | |
| var reachedCallback = false; | |
| var reachedAfterCallback = false; | |
| startAsync((handleSleepReturnValue = 0) => { | |
| #if ASSERTIONS | |
| // old emterpretify API supported other stuff | |
| assert(['undefined', 'number', 'boolean', 'bigint'].includes(typeof handleSleepReturnValue), `invalid type for handleSleepReturnValue: '${typeof handleSleepReturnValue}'`); | |
| #endif | |
| if (ABORT) return; | |
| Asyncify.handleSleepReturnValue = handleSleepReturnValue; | |
| reachedCallback = true; | |
| if (!reachedAfterCallback) { | |
| // We are happening synchronously, so no need for async. | |
| return; | |
| } | |
| #if ASSERTIONS | |
| // This async operation did not happen synchronously, so we did | |
| // unwind. In that case there can be no compiled code on the stack, | |
| // as it might break later operations (we can rewind ok now, but if | |
| // we unwind again, we would unwind through the extra compiled code | |
| // too). | |
| assert(!Asyncify.exportCallStack.length, 'waking up (starting to rewind) must be done from JS, without compiled code on the stack'); | |
| #endif | |
| #if ASYNCIFY_DEBUG | |
| dbg(`ASYNCIFY: start rewind ${Asyncify.currData}`); | |
| #endif | |
| Asyncify.state = Asyncify.State.Rewinding; | |
| runAndAbortIfError(() => _asyncify_start_rewind(Asyncify.currData)); | |
| if (typeof MainLoop != 'undefined' && MainLoop.func) { | |
| MainLoop.resume(); | |
| } | |
| var asyncWasmReturnValue, isError = false; | |
| try { | |
| asyncWasmReturnValue = Asyncify.doRewind(Asyncify.currData); | |
| } catch (err) { | |
| asyncWasmReturnValue = err; | |
| isError = true; | |
| } | |
| // Track whether the return value was handled by any promise handlers. | |
| var handled = false; | |
| if (!Asyncify.currData) { | |
| // All asynchronous execution has finished. | |
| // `asyncWasmReturnValue` now contains the final | |
| // return value of the exported async WASM function. | |
| // | |
| // Note: `asyncWasmReturnValue` is distinct from | |
| // `Asyncify.handleSleepReturnValue`. | |
| // `Asyncify.handleSleepReturnValue` contains the return | |
| // value of the last C function to have executed | |
| // `Asyncify.handleSleep()`, whereas `asyncWasmReturnValue` | |
| // contains the return value of the exported WASM function | |
| // that may have called C functions that | |
| // call `Asyncify.handleSleep()`. | |
| var asyncPromiseHandlers = Asyncify.asyncPromiseHandlers; | |
| if (asyncPromiseHandlers) { | |
| Asyncify.asyncPromiseHandlers = null; | |
| (isError ? asyncPromiseHandlers.reject : asyncPromiseHandlers.resolve)(asyncWasmReturnValue); | |
| handled = true; | |
| } | |
| } | |
| if (isError && !handled) { | |
| // If there was an error and it was not handled by now, we have no choice but to | |
| // rethrow that error into the global scope where it can be caught only by | |
| // `onerror` or `onunhandledpromiserejection`. | |
| throw asyncWasmReturnValue; | |
| } | |
| }); | |
| reachedAfterCallback = true; | |
| if (!reachedCallback) { | |
| // A true async operation was begun; start a sleep. | |
| Asyncify.state = Asyncify.State.Unwinding; | |
| // TODO: reuse, don't alloc/free every sleep | |
| Asyncify.currData = Asyncify.allocateData(); | |
| #if ASYNCIFY_DEBUG | |
| dbg(`ASYNCIFY: start unwind ${Asyncify.currData}`); | |
| #endif | |
| if (typeof MainLoop != 'undefined' && MainLoop.func) { | |
| MainLoop.pause(); | |
| } | |
| runAndAbortIfError(() => _asyncify_start_unwind(Asyncify.currData)); | |
| } | |
| } else if (Asyncify.state === Asyncify.State.Rewinding) { | |
| // Stop a resume. | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY: stop rewind'); | |
| #endif | |
| Asyncify.state = Asyncify.State.Normal; | |
| runAndAbortIfError(_asyncify_stop_rewind); | |
| _free(Asyncify.currData); | |
| Asyncify.currData = null; | |
| // Call all sleep callbacks now that the sleep-resume is all done. | |
| Asyncify.sleepCallbacks.forEach(callUserCallback); | |
| } else { | |
| abort(`invalid state: ${Asyncify.state}`); | |
| } | |
| return Asyncify.handleSleepReturnValue; | |
| }, | |
| // Unlike `handleSleep`, accepts a function returning a `Promise` | |
| // and uses the fulfilled value instead of passing in a separate callback. | |
| // | |
| // This is particularly useful for native JS `async` functions where the | |
| // returned value will "just work" and be passed back to C++. | |
| handleAsync: (startAsync) => Asyncify.handleSleep(async (wakeUp) => { | |
| // TODO: add error handling as a second param when handleSleep implements it. | |
| wakeUp(await startAsync()); | |
| }), | |
| #elif ASYNCIFY == 2 | |
| // | |
| // JSPI implementation of Asyncify. | |
| // | |
| // Stores all the exported raw Wasm functions that are wrapped with async | |
| // WebAssembly.Functions. | |
| asyncExports: null, | |
| isAsyncExport(func) { | |
| return Asyncify.asyncExports?.has(func); | |
| }, | |
| handleAsync: async (startAsync) => { | |
| {{{ runtimeKeepalivePush(); }}} | |
| try { | |
| return await startAsync(); | |
| } finally { | |
| {{{ runtimeKeepalivePop(); }}} | |
| } | |
| }, | |
| handleSleep: (startAsync) => Asyncify.handleAsync(() => new Promise(startAsync)), | |
| makeAsyncFunction(original) { | |
| #if ASYNCIFY_DEBUG | |
| dbg('asyncify: makeAsyncFunction for', original); | |
| #endif | |
| return WebAssembly.promising(original); | |
| }, | |
| #endif | |
| }, | |
| emscripten_sleep__async: 'auto', | |
| emscripten_sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)), | |
| emscripten_wget_data__deps: ['$asyncLoad', 'malloc'], | |
| emscripten_wget_data__async: 'auto', | |
| emscripten_wget_data: async (url, pbuffer, pnum, perror) => { | |
| /* no need for run dependency, this is async but will not do any prepare etc. step */ | |
| try { | |
| const byteArray = await asyncLoad(UTF8ToString(url)); | |
| // can only allocate the buffer after the wakeUp, not during an asyncing | |
| var buffer = _malloc(byteArray.length); // must be freed by caller! | |
| HEAPU8.set(byteArray, buffer); | |
| {{{ makeSetValue('pbuffer', 0, 'buffer', '*') }}}; | |
| {{{ makeSetValue('pnum', 0, 'byteArray.length', 'i32') }}}; | |
| {{{ makeSetValue('perror', 0, '0', 'i32') }}}; | |
| } catch (err) { | |
| {{{ makeSetValue('perror', 0, '1', 'i32') }}}; | |
| } | |
| }, | |
| emscripten_scan_registers__deps: ['$safeSetTimeout'], | |
| emscripten_scan_registers__async: true, | |
| emscripten_scan_registers: (func) => { | |
| return Asyncify.handleSleep((wakeUp) => { | |
| // We must first unwind, so things are spilled to the stack. Then while | |
| // we are pausing we do the actual scan. After that we can resume. Note | |
| // how using a timeout here avoids unbounded call stack growth, which | |
| // could happen if we tried to scan the stack immediately after unwinding. | |
| safeSetTimeout(() => { | |
| var stackBegin = Asyncify.currData + {{{ C_STRUCTS.asyncify_data_s.__size__ }}}; | |
| var stackEnd = {{{ makeGetValue('Asyncify.currData', 0, '*') }}}; | |
| {{{ makeDynCall('vpp', 'func') }}}(stackBegin, stackEnd); | |
| wakeUp(); | |
| }, 0); | |
| }); | |
| }, | |
| $Fibers__deps: ['$Asyncify', 'emscripten_stack_set_limits', '$stackRestore'], | |
| $Fibers: { | |
| nextFiber: 0, | |
| trampolineRunning: false, | |
| trampoline() { | |
| if (!Fibers.trampolineRunning && Fibers.nextFiber) { | |
| Fibers.trampolineRunning = true; | |
| do { | |
| var fiber = Fibers.nextFiber; | |
| Fibers.nextFiber = 0; | |
| #if ASYNCIFY_DEBUG >= 2 | |
| dbg("ASYNCIFY/FIBER: trampoline jump into fiber", fiber, new Error().stack); | |
| #endif | |
| Fibers.finishContextSwitch(fiber); | |
| } while (Fibers.nextFiber); | |
| Fibers.trampolineRunning = false; | |
| } | |
| }, | |
| /* | |
| * NOTE: This function is the asynchronous part of emscripten_fiber_swap. | |
| */ | |
| finishContextSwitch(newFiber) { | |
| var stack_base = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_base, '*') }}}; | |
| var stack_max = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_limit, '*') }}}; | |
| _emscripten_stack_set_limits(stack_base, stack_max); | |
| #if STACK_OVERFLOW_CHECK >= 2 | |
| ___set_stack_limits(stack_base, stack_max); | |
| #endif | |
| stackRestore({{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, '*') }}}); | |
| var entryPoint = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, '*') }}}; | |
| if (entryPoint !== 0) { | |
| #if STACK_OVERFLOW_CHECK | |
| writeStackCookie(); | |
| #endif | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY/FIBER: entering fiber', newFiber, 'for the first time'); | |
| #endif | |
| Asyncify.currData = null; | |
| {{{ makeSetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, 0, '*') }}}; | |
| var userData = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.user_data, '*') }}}; | |
| {{{ makeDynCall('vp', 'entryPoint') }}}(userData); | |
| } else { | |
| var asyncifyData = newFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}}; | |
| Asyncify.currData = asyncifyData; | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY/FIBER: start rewind', asyncifyData, '(resuming fiber', newFiber, ')'); | |
| #endif | |
| Asyncify.state = Asyncify.State.Rewinding; | |
| _asyncify_start_rewind(asyncifyData); | |
| Asyncify.doRewind(asyncifyData); | |
| } | |
| }, | |
| }, | |
| emscripten_fiber_swap__deps: ["$Asyncify", "$Fibers", '$stackSave'], | |
| emscripten_fiber_swap__async: true, | |
| emscripten_fiber_swap: (oldFiber, newFiber) => { | |
| if (ABORT) return; | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY/FIBER: swap', oldFiber, '->', newFiber, 'state:', Asyncify.state); | |
| #endif | |
| if (Asyncify.state === Asyncify.State.Normal) { | |
| Asyncify.state = Asyncify.State.Unwinding; | |
| var asyncifyData = oldFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}}; | |
| Asyncify.setDataRewindFunc(asyncifyData); | |
| Asyncify.currData = asyncifyData; | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY/FIBER: start unwind', asyncifyData); | |
| #endif | |
| _asyncify_start_unwind(asyncifyData); | |
| var stackTop = stackSave(); | |
| {{{ makeSetValue('oldFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, 'stackTop', '*') }}}; | |
| Fibers.nextFiber = newFiber; | |
| } else { | |
| #if ASSERTIONS | |
| assert(Asyncify.state === Asyncify.State.Rewinding); | |
| #endif | |
| #if ASYNCIFY_DEBUG | |
| dbg('ASYNCIFY/FIBER: stop rewind'); | |
| #endif | |
| Asyncify.state = Asyncify.State.Normal; | |
| _asyncify_stop_rewind(); | |
| Asyncify.currData = null; | |
| } | |
| }, | |
| #else // ASYNCIFY | |
| emscripten_sleep: () => { | |
| abort('Please compile your program with async support in order to use asynchronous operations like emscripten_sleep'); | |
| }, | |
| emscripten_wget: (url, file) => { | |
| abort('Please compile your program with async support in order to use asynchronous operations like emscripten_wget'); | |
| }, | |
| emscripten_wget_data: (url, pbuffer, pnum, perror) => { | |
| abort('Please compile your program with async support in order to use asynchronous operations like emscripten_wget_data'); | |
| }, | |
| emscripten_scan_registers: (func) => { | |
| abort('Please compile your program with async support in order to use asynchronous operations like emscripten_scan_registers'); | |
| }, | |
| emscripten_fiber_swap: (oldFiber, newFiber) => { | |
| abort('Please compile your program with async support in order to use asynchronous operations like emscripten_fiber_swap'); | |
| }, | |
| #endif // ASYNCIFY | |
| }); | |
| if (ASYNCIFY) { | |
| extraLibraryFuncs.push('$Asyncify'); | |
| } | |
Xet Storage Details
- Size:
- 23.6 kB
- Xet hash:
- 6d5ef76bd17175b698e5edd40588f4420072f176f2611e4b176b890a1d6cf36e
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.