blob: 8ccffcc33a78d31a0cface1ea4bbb43eb798dc71 [file] [log] [blame]
/**
* @license
* Copyright 2014 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
//
// Async support via ASYNCIFY
//
mergeInto(LibraryManager.library, {
// error handling
$runAndAbortIfError: function(func) {
try {
return func();
} catch (e) {
abort(e);
}
},
#if ASYNCIFY
$Asyncify__deps: ['$runAndAbortIfError', '$callUserCallback', '$sigToWasmTypes',
#if !MINIMAL_RUNTIME
'$runtimeKeepalivePush', '$runtimeKeepalivePop'
#endif
],
$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(function(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.
exportCallStack: [],
callStackNameToId: {},
callStackIdToName: {},
callStackId: 0,
asyncPromiseHandlers: null, // { resolve, reject } pair for when *all* asynchronicity is done
sleepCallbacks: [], // functions to call every time we sleep
#if ASYNCIFY == 2
// TODO: Stack switching support could be implemented without all the
// asyncify infrastructure, perhaps as code on the size instead of
// ifdefs. That might be cleaner, and it would avoid overhead like the
// malloc() asyncify does (we could ifdef it out, but it's even more
// ifdefing).
// The global suspender object used with the VM's stack switching Promise
// API.
suspender: null,
// The promise that is being suspended on in the VM atm, or null.
promise: null,
// The function we should call to resolve the promise at the right time.
promiseResolve: null,
#endif
getCallStackId: function(funcName) {
var id = Asyncify.callStackNameToId[funcName];
if (id === undefined) {
id = Asyncify.callStackId++;
Asyncify.callStackNameToId[funcName] = id;
Asyncify.callStackIdToName[id] = funcName;
}
return id;
},
instrumentWasmImports: function(imports) {
#if ASYNCIFY_DEBUG
err('asyncify instrumenting imports');
#endif
#if ASYNCIFY == 2
// TODO we could perhaps add an init function and put this there, but
// this should work for now.
Asyncify.suspender = new WebAssembly.Suspender();
#endif
var ASYNCIFY_IMPORTS = {{{ JSON.stringify(ASYNCIFY_IMPORTS) }}}.map((x) => x.split('.')[1]);
for (var x in imports) {
(function(x) {
var original = imports[x];
var sig = original.sig;
if (typeof original == 'function') {
var isAsyncifyImport = ASYNCIFY_IMPORTS.indexOf(x) >= 0 ||
x.startsWith('__asyncjs__');
#if ASYNCIFY == 2
if (isAsyncifyImport) {
#if ASSERTIONS
if (!sig) {
throw new Error('Missing __sig for ' + x);
}
#endif
var type = sigToWasmTypes(sig, original);
// Regardless of the original result type of the function, as it
// is now expected to potentially return a Promise, change it to
// an externref.
type.results = ['externref'];
#if ASYNCIFY_DEBUG
err('asyncify: suspendOnReturnedPromise for', x, original);
#endif
imports[x] = original = Asyncify.suspender.suspendOnReturnedPromise(
new WebAssembly.Function(type, 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] = function() {
var originalAsyncifyState = Asyncify.state;
try {
return original.apply(null, arguments);
} 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) {
throw new Error('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 = sig;
#endif // MAIN_MODULE
#endif // ASSERTIONS
}
})(x);
}
},
instrumentWasmExports: function(exports) {
#if ASYNCIFY_DEBUG
err('asyncify instrumenting exports');
#endif
var ret = {};
for (var x in exports) {
(function(x) {
var original = exports[x];
#if ASYNCIFY == 2
// TODO: need a list of all suspending exports.
if (x === 'main') {
#if ASYNCIFY_DEBUG
err('asyncify: returnPromiseOnSuspend for', x, original);
#endif
ret[x] = original = Asyncify.suspender.returnPromiseOnSuspend(original);
}
#endif
if (typeof original == 'function') {
ret[x] = function() {
#if ASYNCIFY_DEBUG >= 2
err('ASYNCIFY: ' + ' '.repeat(Asyncify.exportCallStack.length) + ' try ' + x);
#endif
Asyncify.exportCallStack.push(x);
try {
return original.apply(null, arguments);
} finally {
if (!ABORT) {
var y = Asyncify.exportCallStack.pop();
assert(y === x);
#if ASYNCIFY_DEBUG >= 2
err('ASYNCIFY: ' + ' '.repeat(Asyncify.exportCallStack.length) + ' finally ' + x);
#endif
Asyncify.maybeStopUnwind();
}
}
};
#if MAIN_MODULE
ret[x].orig = original;
#endif
} else {
ret[x] = original;
}
})(x);
}
return ret;
},
maybeStopUnwind: function() {
#if ASYNCIFY_DEBUG
err('ASYNCIFY: maybe stop unwind', Asyncify.exportCallStack);
#endif
if (Asyncify.currData &&
Asyncify.state === Asyncify.State.Unwinding &&
Asyncify.exportCallStack.length === 0) {
// We just finished unwinding.
#if ASYNCIFY_DEBUG
err('ASYNCIFY: stop unwind');
#endif
{{{ runtimeKeepalivePush(); }}}
Asyncify.state = Asyncify.State.Normal;
// Keep the runtime alive so that a re-wind can be done later.
#if ASYNCIFY == 1
runAndAbortIfError(_asyncify_stop_unwind);
#endif
if (typeof Fibers != 'undefined') {
Fibers.trampoline();
}
}
},
whenDone: function() {
#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: resolve,
reject: reject
};
});
},
allocateData: function() {
// 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 (callStackIdToName[id] == name of js function)
//
// 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: function(ptr, stack, stackSize) {
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_ptr, 'stack', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_limit, 'stack + stackSize', 'i32') }}};
},
setDataRewindFunc: function(ptr) {
var bottomOfCallStack = Asyncify.exportCallStack[0];
#if ASYNCIFY_DEBUG >= 2
err('ASYNCIFY: setDataRewindFunc('+ptr+'), bottomOfCallStack is', bottomOfCallStack, new Error().stack);
#endif
var rewindId = Asyncify.getCallStackId(bottomOfCallStack);
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'rewindId', 'i32') }}};
},
#if RELOCATABLE
getDataRewindFunc__deps: [ '$resolveGlobalSymbol' ],
#endif
getDataRewindFunc: function(ptr) {
var id = {{{ makeGetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'i32') }}};
var name = Asyncify.callStackIdToName[id];
var func = Module['asm'][name];
#if RELOCATABLE
// Exported functions in side modules are not listed in `Module["asm"]`,
// So we should use `resolveGlobalSymbol` helper function, which is defined in `library_dylink.js`.
if (!func) {
func = resolveGlobalSymbol(name, false);
}
#endif
return func;
},
doRewind: function(ptr) {
#if ASYNCIFY == 2
// Resolve the promise. The VM will resume the wasm on the next event loop
// turn.
Asyncify.promiseResolve(Asyncify.handleSleepReturnValue);
setTimeout(() => {
// This timeout happens after the wasm has been resumed; we can stop
// artificially keeping the runtime alive at that time.
{{{ runtimeKeepalivePop(); }}}
});
#else
var start = Asyncify.getDataRewindFunc(ptr);
#if ASYNCIFY_DEBUG
err('ASYNCIFY: start:', start);
#endif
// Once we have rewound and the stack we no longer need to artificially
// keep the runtime alive.
{{{ runtimeKeepalivePop(); }}}
return start();
#endif
},
// 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: function(startAsync) {
#if ASSERTIONS
assert(Asyncify.state !== Asyncify.State.Disabled, 'Asyncify cannot be done during or after the runtime exits');
#endif
if (ABORT) return;
#if ASYNCIFY_DEBUG
err('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) => {
#if ASSERTIONS
assert(!handleSleepReturnValue || typeof handleSleepReturnValue == 'number' || typeof handleSleepReturnValue == 'boolean'); // old emterpretify API supported other stuff
#endif
if (ABORT) return;
Asyncify.handleSleepReturnValue = handleSleepReturnValue || 0;
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
err('ASYNCIFY: start rewind ' + Asyncify.currData);
#endif
Asyncify.state = Asyncify.State.Rewinding;
#if ASYNCIFY == 1
runAndAbortIfError(() => _asyncify_start_rewind(Asyncify.currData));
#endif
if (typeof Browser != 'undefined' && Browser.mainLoop.func) {
Browser.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()`, where as `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
err('ASYNCIFY: start unwind ' + Asyncify.currData);
#endif
if (typeof Browser != 'undefined' && Browser.mainLoop.func) {
Browser.mainLoop.pause();
}
#if ASYNCIFY == 2
// Return a Promise to get the browser's stack switching logic to run.
return Asyncify.promise = new Promise((resolve, reject) => {
// Stash the resolve hook so we can call it at the proper time.
Asyncify.promiseResolve = resolve;
// TODO: handle rejection
});
#else
runAndAbortIfError(() => _asyncify_start_unwind(Asyncify.currData));
#endif
}
} else if (Asyncify.state === Asyncify.State.Rewinding) {
// Stop a resume.
#if ASYNCIFY_DEBUG
err('ASYNCIFY: stop rewind');
#endif
Asyncify.state = Asyncify.State.Normal;
#if ASYNCIFY == 1
runAndAbortIfError(_asyncify_stop_rewind);
#endif
_free(Asyncify.currData);
Asyncify.currData = null;
// Call all sleep callbacks now that the sleep-resume is all done.
Asyncify.sleepCallbacks.forEach((func) => callUserCallback(func));
} 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: function(startAsync) {
return Asyncify.handleSleep((wakeUp) => {
// TODO: add error handling as a second param when handleSleep implements it.
startAsync().then(wakeUp);
});
},
},
emscripten_sleep__sig: 'vi',
emscripten_sleep__deps: ['$safeSetTimeout'],
emscripten_sleep: function(ms) {
// emscripten_sleep() does not return a value, but we still need a |return|
// here for stack switching support (ASYNCIFY=2). In that mode this function
// returns a Promise instead of nothing, and that Promise is what tells the
// wasm VM to pause the stack.
return Asyncify.handleSleep((wakeUp) => safeSetTimeout(wakeUp, ms));
},
emscripten_wget__sig: 'vpp',
emscripten_wget__deps: ['$Browser', '$PATH_FS', '$FS'],
emscripten_wget: function(url, file) {
return Asyncify.handleSleep((wakeUp) => {
var _url = UTF8ToString(url);
var _file = UTF8ToString(file);
_file = PATH_FS.resolve(FS.cwd(), _file);
var destinationDirectory = PATH.dirname(_file);
FS.createPreloadedFile(
destinationDirectory,
PATH.basename(_file),
_url, true, true,
wakeUp,
wakeUp,
false, // dontCreateFile
false, // canOwn
// preFinish: if the destination directory does not yet exist, create it
() => FS.mkdirTree(destinationDirectory)
);
});
},
emscripten_wget_data__sig: 'vpppp',
emscripten_wget_data__deps: ['$asyncLoad', 'malloc'],
emscripten_wget_data: function(url, pbuffer, pnum, perror) {
return Asyncify.handleSleep((wakeUp) => {
asyncLoad(UTF8ToString(url), (byteArray) => {
// 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', 'i32') }}};
{{{ makeSetValue('pnum', 0, 'byteArray.length', 'i32') }}};
{{{ makeSetValue('perror', 0, '0', 'i32') }}};
wakeUp();
}, () => {
{{{ makeSetValue('perror', 0, '1', 'i32') }}};
wakeUp();
}, true /* no need for run dependency, this is async but will not do any prepare etc. step */ );
});
},
emscripten_scan_registers__sig: 'vp',
emscripten_scan_registers__deps: ['$safeSetTimeout'],
emscripten_scan_registers: function(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 = HEAP32[Asyncify.currData >> 2];
{{{ makeDynCall('vii', 'func') }}}(stackBegin, stackEnd);
wakeUp();
}, 0);
});
},
emscripten_lazy_load_code__sig: 'v',
emscripten_lazy_load_code: function() {
return Asyncify.handleSleep((wakeUp) => {
// Update the expected wasm binary file to be the lazy one.
wasmBinaryFile += '.lazy.wasm';
// Add a callback for when all run dependencies are fulfilled, which happens when async wasm loading is done.
dependenciesFulfilled = wakeUp;
// Load the new wasm.
asm = createWasm();
});
},
$Fibers__deps: ['$Asyncify'],
$Fibers: {
nextFiber: 0,
trampolineRunning: false,
trampoline: function() {
if (!Fibers.trampolineRunning && Fibers.nextFiber) {
Fibers.trampolineRunning = true;
do {
var fiber = Fibers.nextFiber;
Fibers.nextFiber = 0;
#if ASYNCIFY_DEBUG >= 2
err("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: function(newFiber) {
var stack_base = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_base, 'i32') }}};
var stack_max = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_limit, 'i32') }}};
_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, 'i32') }}});
var entryPoint = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, 'i32') }}};
if (entryPoint !== 0) {
#if STACK_OVERFLOW_CHECK
writeStackCookie();
#endif
#if ASYNCIFY_DEBUG
err('ASYNCIFY/FIBER: entering fiber', newFiber, 'for the first time');
#endif
Asyncify.currData = null;
{{{ makeSetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, 0, 'i32') }}};
var userData = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.user_data, 'i32') }}};
{{{ makeDynCall('vi', 'entryPoint') }}}(userData);
} else {
var asyncifyData = newFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}};
Asyncify.currData = asyncifyData;
#if ASYNCIFY_DEBUG
err('ASYNCIFY/FIBER: start rewind', asyncifyData, '(resuming fiber', newFiber, ')');
#endif
Asyncify.state = Asyncify.State.Rewinding;
_asyncify_start_rewind(asyncifyData);
Asyncify.doRewind(asyncifyData);
}
},
},
emscripten_fiber_init__sig: 'viiiiiii',
emscripten_fiber_init__deps: ['$Asyncify'],
emscripten_fiber_init: function(fiber, entryPoint, userData, cStack, cStackSize, asyncStack, asyncStackSize) {
var cStackBase = cStack + cStackSize;
{{{ makeSetValue('fiber', C_STRUCTS.emscripten_fiber_s.stack_base, 'cStackBase', 'i32') }}};
{{{ makeSetValue('fiber', C_STRUCTS.emscripten_fiber_s.stack_limit, 'cStack', 'i32') }}};
{{{ makeSetValue('fiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, 'cStackBase', 'i32') }}};
{{{ makeSetValue('fiber', C_STRUCTS.emscripten_fiber_s.entry, 'entryPoint', 'i32') }}};
{{{ makeSetValue('fiber', C_STRUCTS.emscripten_fiber_s.user_data, 'userData', 'i32') }}};
var asyncifyData = fiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}};
Asyncify.setDataHeader(asyncifyData, asyncStack, asyncStackSize);
},
emscripten_fiber_init_from_current_context__sig: 'vii',
emscripten_fiber_init_from_current_context__deps: ['$Asyncify'],
emscripten_fiber_init_from_current_context: function(fiber, asyncStack, asyncStackSize) {
{{{ makeSetValue('fiber', C_STRUCTS.emscripten_fiber_s.stack_base, '_emscripten_stack_get_base()', 'i32') }}};
{{{ makeSetValue('fiber', C_STRUCTS.emscripten_fiber_s.stack_limit, '_emscripten_stack_get_end()', 'i32') }}};
{{{ makeSetValue('fiber', C_STRUCTS.emscripten_fiber_s.entry, 0, 'i32') }}};
var asyncifyData = fiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}};
Asyncify.setDataHeader(asyncifyData, asyncStack, asyncStackSize);
},
emscripten_fiber_swap__sig: 'vii',
emscripten_fiber_swap__deps: ["$Asyncify", "$Fibers"],
emscripten_fiber_swap: function(oldFiber, newFiber) {
if (ABORT) return;
#if ASYNCIFY_DEBUG
err('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
err('ASYNCIFY/FIBER: start unwind', asyncifyData);
#endif
_asyncify_start_unwind(asyncifyData);
var stackTop = stackSave();
{{{ makeSetValue('oldFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, 'stackTop', 'i32') }}};
Fibers.nextFiber = newFiber;
} else {
#if ASSERTIONS
assert(Asyncify.state === Asyncify.State.Rewinding);
#endif
#if ASYNCIFY_DEBUG
err('ASYNCIFY/FIBER: stop rewind');
#endif
Asyncify.state = Asyncify.State.Normal;
_asyncify_stop_rewind();
Asyncify.currData = null;
}
},
#else // ASYNCIFY
emscripten_sleep: function() {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_sleep';
},
emscripten_wget: function() {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_wget';
},
emscripten_wget_data: function() {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_wget_data';
},
emscripten_scan_registers: function() {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_scan_registers';
},
emscripten_fiber_init: function() {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_fiber_init';
},
emscripten_fiber_init_from_current_context: function() {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_fiber_init_from_current_context';
},
emscripten_fiber_swap: function() {
throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_fiber_swap';
},
#endif // ASYNCIFY
});
if (ASYNCIFY) {
DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push('$Asyncify');
}