Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libasync.js
6165 views
1
/**
2
* @license
3
* Copyright 2014 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
//
8
// Async support via ASYNCIFY
9
//
10
11
addToLibrary({
12
// error handling
13
14
$runAndAbortIfError: (func) => {
15
try {
16
return func();
17
} catch (e) {
18
abort(e);
19
}
20
},
21
22
#if ASYNCIFY
23
$Asyncify__deps: ['$runAndAbortIfError', '$callUserCallback',
24
#if ASSERTIONS
25
'$createNamedFunction',
26
#endif
27
#if !MINIMAL_RUNTIME
28
'$runtimeKeepalivePush', '$runtimeKeepalivePop',
29
#endif
30
#if ASYNCIFY == 1
31
// Needed by allocateData and handleSleep respectively
32
'malloc', 'free',
33
#endif
34
],
35
36
$Asyncify: {
37
//
38
// Asyncify code that is shared between mode 1 (original) and mode 2 (JSPI).
39
//
40
#if ASYNCIFY == 1 && MEMORY64
41
rewindArguments: new Map(),
42
#endif
43
instrumentWasmImports(imports) {
44
#if EMBIND_GEN_MODE
45
// Instrumenting is not needed when generating code.
46
return imports;
47
#endif
48
#if ASYNCIFY_DEBUG
49
dbg('asyncify instrumenting imports');
50
#endif
51
#if ASSERTIONS && ASYNCIFY == 2
52
assert('Suspending' in WebAssembly, 'JSPI not supported by current environment. Perhaps it needs to be enabled via flags?');
53
#endif
54
var importPattern = {{{ new RegExp(`^(${ASYNCIFY_IMPORTS_EXCEPT_JS_LIBS.map(x => x.split('.')[1]).join('|').replace(/\*/g, '.*')})$`) }}};
55
56
for (let [x, original] of Object.entries(imports)) {
57
if (typeof original == 'function') {
58
let isAsyncifyImport = original.isAsync || importPattern.test(x);
59
#if ASYNCIFY == 2
60
// Wrap async imports with a suspending WebAssembly function.
61
if (isAsyncifyImport) {
62
#if ASYNCIFY_DEBUG
63
dbg('asyncify: suspendOnReturnedPromise for', x, original);
64
#endif
65
imports[x] = original = new WebAssembly.Suspending(original);
66
}
67
#endif
68
#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
69
imports[x] = (...args) => {
70
var originalAsyncifyState = Asyncify.state;
71
try {
72
return original(...args);
73
} finally {
74
// Only asyncify-declared imports are allowed to change the
75
// state.
76
// Changing the state from normal to disabled is allowed (in any
77
// function) as that is what shutdown does (and we don't have an
78
// explicit list of shutdown imports).
79
var changedToDisabled =
80
originalAsyncifyState === Asyncify.State.Normal &&
81
Asyncify.state === Asyncify.State.Disabled;
82
// invoke_* functions are allowed to change the state if we do
83
// not ignore indirect calls.
84
var ignoredInvoke = x.startsWith('invoke_') &&
85
{{{ !ASYNCIFY_IGNORE_INDIRECT }}};
86
if (Asyncify.state !== originalAsyncifyState &&
87
!isAsyncifyImport &&
88
!changedToDisabled &&
89
!ignoredInvoke) {
90
abort(`import ${x} was not in ASYNCIFY_IMPORTS, but changed the state`);
91
}
92
}
93
};
94
#if MAIN_MODULE
95
// The dynamic library loader needs to be able to read .sig
96
// properties, so that it knows function signatures when it adds
97
// them to the table.
98
imports[x].sig = original.sig;
99
#endif // MAIN_MODULE
100
#endif // ASSERTIONS
101
}
102
}
103
},
104
#if ASYNCIFY == 1 && MEMORY64
105
saveRewindArguments(func, passedArguments) {
106
return Asyncify.rewindArguments.set(func, Array.from(passedArguments));
107
},
108
restoreRewindArguments(func) {
109
#if ASSERTIONS
110
assert(Asyncify.rewindArguments.has(func));
111
#endif
112
return Asyncify.rewindArguments.get(func);
113
},
114
#endif
115
116
#if ASYNCIFY == 1
117
instrumentFunction(original) {
118
var wrapper = (...args) => {
119
#if ASYNCIFY_DEBUG >= 2
120
dbg(`ASYNCIFY: ${' '.repeat(Asyncify.exportCallStack.length)} try ${original}`);
121
#endif
122
Asyncify.exportCallStack.push(original);
123
try {
124
#if MEMORY64
125
Asyncify.saveRewindArguments(original, args);
126
#endif
127
return original(...args);
128
} finally {
129
if (!ABORT) {
130
var top = Asyncify.exportCallStack.pop();
131
#if ASSERTIONS
132
assert(top === original);
133
#endif
134
#if ASYNCIFY_DEBUG >= 2
135
dbg(`ASYNCIFY: ${' '.repeat(Asyncify.exportCallStack.length)} finally ${original}`);
136
#endif
137
Asyncify.maybeStopUnwind();
138
}
139
}
140
};
141
Asyncify.funcWrappers.set(original, wrapper);
142
#if MAIN_MODULE
143
wrapper.orig = original;
144
#endif
145
#if ASSERTIONS
146
wrapper = createNamedFunction(`__asyncify_wrapper_${original.name}`, wrapper);
147
#endif
148
return wrapper;
149
},
150
#endif // ASYNCIFY == 1
151
152
instrumentWasmExports(exports) {
153
#if EMBIND_GEN_MODE
154
// Instrumenting is not needed when generating code.
155
return exports;
156
#endif
157
#if ASYNCIFY_DEBUG
158
dbg('asyncify instrumenting exports');
159
#endif
160
#if ASYNCIFY == 2
161
var exportPattern = {{{ new RegExp(`^(${ASYNCIFY_EXPORTS.join('|').replace(/\*/g, '.*')})$`) }}};
162
Asyncify.asyncExports = new Set();
163
#endif
164
var ret = {};
165
for (let [x, original] of Object.entries(exports)) {
166
if (typeof original == 'function') {
167
#if ASYNCIFY == 2
168
// Wrap all exports with a promising WebAssembly function.
169
let isAsyncifyExport = exportPattern.test(x);
170
if (isAsyncifyExport) {
171
Asyncify.asyncExports.add(original);
172
original = Asyncify.makeAsyncFunction(original);
173
}
174
ret[x] = original;
175
#else
176
var wrapper = Asyncify.instrumentFunction(original);
177
ret[x] = wrapper;
178
#endif
179
} else {
180
ret[x] = original;
181
}
182
}
183
return ret;
184
},
185
186
#if ASYNCIFY == 1
187
//
188
// Original implementation of Asyncify.
189
//
190
State: {
191
Normal: 0,
192
Unwinding: 1,
193
Rewinding: 2,
194
Disabled: 3,
195
},
196
state: 0,
197
StackSize: {{{ ASYNCIFY_STACK_SIZE }}},
198
currData: null,
199
// The return value passed to wakeUp() in
200
// Asyncify.handleSleep((wakeUp) => {...}) is stored here,
201
// so we can return it later from the C function that called
202
// Asyncify.handleSleep() after rewinding finishes.
203
handleSleepReturnValue: 0,
204
// We must track which wasm exports are called into and
205
// exited, so that we know where the call stack began,
206
// which is where we must call to rewind it.
207
// This list contains the original Wasm exports.
208
exportCallStack: [],
209
callstackFuncToId: new Map(),
210
callStackIdToFunc: new Map(),
211
// Maps wasm functions to their corresponding wrapper function.
212
funcWrappers: new Map(),
213
callStackId: 0,
214
asyncPromiseHandlers: null, // { resolve, reject } pair for when *all* asynchronicity is done
215
sleepCallbacks: [], // functions to call every time we sleep
216
217
getCallStackId(func) {
218
#if ASSERTIONS
219
assert(func);
220
#endif
221
if (!Asyncify.callstackFuncToId.has(func)) {
222
var id = Asyncify.callStackId++;
223
Asyncify.callstackFuncToId.set(func, id);
224
Asyncify.callStackIdToFunc.set(id, func);
225
}
226
return Asyncify.callstackFuncToId.get(func);
227
},
228
229
maybeStopUnwind() {
230
#if ASYNCIFY_DEBUG
231
dbg('ASYNCIFY: maybe stop unwind', Asyncify.exportCallStack);
232
#endif
233
if (Asyncify.currData &&
234
Asyncify.state === Asyncify.State.Unwinding &&
235
Asyncify.exportCallStack.length === 0) {
236
// We just finished unwinding.
237
// Be sure to set the state before calling any other functions to avoid
238
// possible infinite recursion here (For example in debug pthread builds
239
// the dbg() function itself can call back into WebAssembly to get the
240
// current pthread_self() pointer).
241
Asyncify.state = Asyncify.State.Normal;
242
#if ASYNCIFY_DEBUG
243
dbg('ASYNCIFY: stop unwind');
244
#endif
245
{{{ runtimeKeepalivePush(); }}}
246
// Keep the runtime alive so that a re-wind can be done later.
247
runAndAbortIfError(_asyncify_stop_unwind);
248
if (typeof Fibers != 'undefined') {
249
Fibers.trampoline();
250
}
251
}
252
},
253
254
whenDone() {
255
#if ASSERTIONS
256
assert(Asyncify.currData, 'Tried to wait for an async operation when none is in progress.');
257
assert(!Asyncify.asyncPromiseHandlers, 'Cannot have multiple async operations in flight at once');
258
#endif
259
return new Promise((resolve, reject) => {
260
Asyncify.asyncPromiseHandlers = { resolve, reject };
261
});
262
},
263
264
allocateData() {
265
// An asyncify data structure has three fields:
266
// 0 current stack pos
267
// 4 max stack pos
268
// 8 id of function at bottom of the call stack (callStackIdToFunc[id] == wasm func)
269
//
270
// The Asyncify ABI only interprets the first two fields, the rest is for the runtime.
271
// We also embed a stack in the same memory region here, right next to the structure.
272
// This struct is also defined as asyncify_data_t in emscripten/fiber.h
273
var ptr = _malloc({{{ C_STRUCTS.asyncify_data_s.__size__ }}} + Asyncify.StackSize);
274
Asyncify.setDataHeader(ptr, ptr + {{{ C_STRUCTS.asyncify_data_s.__size__ }}}, Asyncify.StackSize);
275
Asyncify.setDataRewindFunc(ptr);
276
return ptr;
277
},
278
279
setDataHeader(ptr, stack, stackSize) {
280
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_ptr, 'stack', '*') }}};
281
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_limit, 'stack + stackSize', '*') }}};
282
},
283
284
setDataRewindFunc(ptr) {
285
var bottomOfCallStack = Asyncify.exportCallStack[0];
286
#if ASYNCIFY_DEBUG >= 2
287
dbg(`ASYNCIFY: setDataRewindFunc(${ptr}), bottomOfCallStack is`, bottomOfCallStack, new Error().stack);
288
#endif
289
#if ASSERTIONS
290
assert(bottomOfCallStack, 'exportCallStack is empty');
291
#endif
292
var rewindId = Asyncify.getCallStackId(bottomOfCallStack);
293
{{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'rewindId', 'i32') }}};
294
},
295
296
getDataRewindFunc(ptr) {
297
var id = {{{ makeGetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'i32') }}};
298
var func = Asyncify.callStackIdToFunc.get(id);
299
#if ASSERTIONS
300
assert(func, `id ${id} not found in callStackIdToFunc`);
301
#endif
302
return func;
303
},
304
305
doRewind(ptr) {
306
var original = Asyncify.getDataRewindFunc(ptr);
307
#if ASYNCIFY_DEBUG
308
dbg('ASYNCIFY: doRewind:', original);
309
#endif
310
var func = Asyncify.funcWrappers.get(original);
311
#if ASSERTIONS
312
assert(original);
313
assert(func);
314
#endif
315
// Once we have rewound and the stack we no longer need to artificially
316
// keep the runtime alive.
317
{{{ runtimeKeepalivePop(); }}}
318
#if MEMORY64
319
// When re-winding, the arguments to a function are ignored. For i32 arguments we
320
// can just call the function with no args at all since the engine will produce zeros
321
// for all arguments. However, for i64 arguments we get `undefined cannot be converted to
322
// BigInt`.
323
func = func.bind(0, ...Asyncify.restoreRewindArguments(original));
324
#endif
325
return callUserCallback(func);
326
},
327
328
// This receives a function to call to start the async operation, and
329
// handles everything else for the user of this API. See emscripten_sleep()
330
// and other async methods for simple examples of usage.
331
handleSleep(startAsync) {
332
#if ASSERTIONS
333
assert(Asyncify.state !== Asyncify.State.Disabled, 'Asyncify cannot be done during or after the runtime exits');
334
#endif
335
if (ABORT) return;
336
#if ASYNCIFY_DEBUG
337
dbg(`ASYNCIFY: handleSleep ${Asyncify.state}`);
338
#endif
339
if (Asyncify.state === Asyncify.State.Normal) {
340
// Prepare to sleep. Call startAsync, and see what happens:
341
// if the code decided to call our callback synchronously,
342
// then no async operation was in fact begun, and we don't
343
// need to do anything.
344
var reachedCallback = false;
345
var reachedAfterCallback = false;
346
startAsync((handleSleepReturnValue = 0) => {
347
#if ASSERTIONS
348
// old emterpretify API supported other stuff
349
assert(['undefined', 'number', 'boolean', 'bigint'].includes(typeof handleSleepReturnValue), `invalid type for handleSleepReturnValue: '${typeof handleSleepReturnValue}'`);
350
#endif
351
if (ABORT) return;
352
Asyncify.handleSleepReturnValue = handleSleepReturnValue;
353
reachedCallback = true;
354
if (!reachedAfterCallback) {
355
// We are happening synchronously, so no need for async.
356
return;
357
}
358
#if ASSERTIONS
359
// This async operation did not happen synchronously, so we did
360
// unwind. In that case there can be no compiled code on the stack,
361
// as it might break later operations (we can rewind ok now, but if
362
// we unwind again, we would unwind through the extra compiled code
363
// too).
364
assert(!Asyncify.exportCallStack.length, 'Waking up (starting to rewind) must be done from JS, without compiled code on the stack.');
365
#endif
366
#if ASYNCIFY_DEBUG
367
dbg(`ASYNCIFY: start rewind ${Asyncify.currData}`);
368
#endif
369
Asyncify.state = Asyncify.State.Rewinding;
370
runAndAbortIfError(() => _asyncify_start_rewind(Asyncify.currData));
371
if (typeof MainLoop != 'undefined' && MainLoop.func) {
372
MainLoop.resume();
373
}
374
var asyncWasmReturnValue, isError = false;
375
try {
376
asyncWasmReturnValue = Asyncify.doRewind(Asyncify.currData);
377
} catch (err) {
378
asyncWasmReturnValue = err;
379
isError = true;
380
}
381
// Track whether the return value was handled by any promise handlers.
382
var handled = false;
383
if (!Asyncify.currData) {
384
// All asynchronous execution has finished.
385
// `asyncWasmReturnValue` now contains the final
386
// return value of the exported async WASM function.
387
//
388
// Note: `asyncWasmReturnValue` is distinct from
389
// `Asyncify.handleSleepReturnValue`.
390
// `Asyncify.handleSleepReturnValue` contains the return
391
// value of the last C function to have executed
392
// `Asyncify.handleSleep()`, whereas `asyncWasmReturnValue`
393
// contains the return value of the exported WASM function
394
// that may have called C functions that
395
// call `Asyncify.handleSleep()`.
396
var asyncPromiseHandlers = Asyncify.asyncPromiseHandlers;
397
if (asyncPromiseHandlers) {
398
Asyncify.asyncPromiseHandlers = null;
399
(isError ? asyncPromiseHandlers.reject : asyncPromiseHandlers.resolve)(asyncWasmReturnValue);
400
handled = true;
401
}
402
}
403
if (isError && !handled) {
404
// If there was an error and it was not handled by now, we have no choice but to
405
// rethrow that error into the global scope where it can be caught only by
406
// `onerror` or `onunhandledpromiserejection`.
407
throw asyncWasmReturnValue;
408
}
409
});
410
reachedAfterCallback = true;
411
if (!reachedCallback) {
412
// A true async operation was begun; start a sleep.
413
Asyncify.state = Asyncify.State.Unwinding;
414
// TODO: reuse, don't alloc/free every sleep
415
Asyncify.currData = Asyncify.allocateData();
416
#if ASYNCIFY_DEBUG
417
dbg(`ASYNCIFY: start unwind ${Asyncify.currData}`);
418
#endif
419
if (typeof MainLoop != 'undefined' && MainLoop.func) {
420
MainLoop.pause();
421
}
422
runAndAbortIfError(() => _asyncify_start_unwind(Asyncify.currData));
423
}
424
} else if (Asyncify.state === Asyncify.State.Rewinding) {
425
// Stop a resume.
426
#if ASYNCIFY_DEBUG
427
dbg('ASYNCIFY: stop rewind');
428
#endif
429
Asyncify.state = Asyncify.State.Normal;
430
runAndAbortIfError(_asyncify_stop_rewind);
431
_free(Asyncify.currData);
432
Asyncify.currData = null;
433
// Call all sleep callbacks now that the sleep-resume is all done.
434
Asyncify.sleepCallbacks.forEach(callUserCallback);
435
} else {
436
abort(`invalid state: ${Asyncify.state}`);
437
}
438
return Asyncify.handleSleepReturnValue;
439
},
440
441
// Unlike `handleSleep`, accepts a function returning a `Promise`
442
// and uses the fulfilled value instead of passing in a separate callback.
443
//
444
// This is particularly useful for native JS `async` functions where the
445
// returned value will "just work" and be passed back to C++.
446
handleAsync: (startAsync) => Asyncify.handleSleep(async (wakeUp) => {
447
// TODO: add error handling as a second param when handleSleep implements it.
448
wakeUp(await startAsync());
449
}),
450
451
#elif ASYNCIFY == 2
452
//
453
// JSPI implementation of Asyncify.
454
//
455
456
// Stores all the exported raw Wasm functions that are wrapped with async
457
// WebAssembly.Functions.
458
asyncExports: null,
459
isAsyncExport(func) {
460
return Asyncify.asyncExports?.has(func);
461
},
462
handleAsync: async (startAsync) => {
463
{{{ runtimeKeepalivePush(); }}}
464
try {
465
return await startAsync();
466
} finally {
467
{{{ runtimeKeepalivePop(); }}}
468
}
469
},
470
handleSleep: (startAsync) => Asyncify.handleAsync(() => new Promise(startAsync)),
471
makeAsyncFunction(original) {
472
#if ASYNCIFY_DEBUG
473
dbg('asyncify: makeAsyncFunction for', original);
474
#endif
475
return WebAssembly.promising(original);
476
},
477
#endif
478
},
479
480
emscripten_sleep__async: 'auto',
481
emscripten_sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
482
483
emscripten_wget_data__deps: ['$asyncLoad', 'malloc'],
484
emscripten_wget_data__async: 'auto',
485
emscripten_wget_data: async (url, pbuffer, pnum, perror) => {
486
/* no need for run dependency, this is async but will not do any prepare etc. step */
487
try {
488
const byteArray = await asyncLoad(UTF8ToString(url));
489
// can only allocate the buffer after the wakeUp, not during an asyncing
490
var buffer = _malloc(byteArray.length); // must be freed by caller!
491
HEAPU8.set(byteArray, buffer);
492
{{{ makeSetValue('pbuffer', 0, 'buffer', '*') }}};
493
{{{ makeSetValue('pnum', 0, 'byteArray.length', 'i32') }}};
494
{{{ makeSetValue('perror', 0, '0', 'i32') }}};
495
} catch (err) {
496
{{{ makeSetValue('perror', 0, '1', 'i32') }}};
497
}
498
},
499
500
emscripten_scan_registers__deps: ['$safeSetTimeout'],
501
emscripten_scan_registers__async: true,
502
emscripten_scan_registers: (func) => {
503
return Asyncify.handleSleep((wakeUp) => {
504
// We must first unwind, so things are spilled to the stack. Then while
505
// we are pausing we do the actual scan. After that we can resume. Note
506
// how using a timeout here avoids unbounded call stack growth, which
507
// could happen if we tried to scan the stack immediately after unwinding.
508
safeSetTimeout(() => {
509
var stackBegin = Asyncify.currData + {{{ C_STRUCTS.asyncify_data_s.__size__ }}};
510
var stackEnd = {{{ makeGetValue('Asyncify.currData', 0, '*') }}};
511
{{{ makeDynCall('vpp', 'func') }}}(stackBegin, stackEnd);
512
wakeUp();
513
}, 0);
514
});
515
},
516
517
_load_secondary_module__sig: 'v',
518
_load_secondary_module__async: 'auto',
519
_load_secondary_module: async function() {
520
// Mark the module as loading for the wasm module (so it doesn't try to load it again).
521
wasmExports['load_secondary_module_status'].value = 1;
522
var imports = {'primary': wasmRawExports};
523
// Replace '.wasm' suffix with '.deferred.wasm'.
524
var deferred = wasmBinaryFile.slice(0, -5) + '.deferred.wasm';
525
await instantiateAsync(null, deferred, imports);
526
},
527
528
$Fibers__deps: ['$Asyncify', 'emscripten_stack_set_limits', '$stackRestore'],
529
$Fibers: {
530
nextFiber: 0,
531
trampolineRunning: false,
532
trampoline() {
533
if (!Fibers.trampolineRunning && Fibers.nextFiber) {
534
Fibers.trampolineRunning = true;
535
do {
536
var fiber = Fibers.nextFiber;
537
Fibers.nextFiber = 0;
538
#if ASYNCIFY_DEBUG >= 2
539
dbg("ASYNCIFY/FIBER: trampoline jump into fiber", fiber, new Error().stack);
540
#endif
541
Fibers.finishContextSwitch(fiber);
542
} while (Fibers.nextFiber);
543
Fibers.trampolineRunning = false;
544
}
545
},
546
/*
547
* NOTE: This function is the asynchronous part of emscripten_fiber_swap.
548
*/
549
finishContextSwitch(newFiber) {
550
var stack_base = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_base, '*') }}};
551
var stack_max = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_limit, '*') }}};
552
_emscripten_stack_set_limits(stack_base, stack_max);
553
554
#if STACK_OVERFLOW_CHECK >= 2
555
___set_stack_limits(stack_base, stack_max);
556
#endif
557
558
stackRestore({{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, '*') }}});
559
560
var entryPoint = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, '*') }}};
561
562
if (entryPoint !== 0) {
563
#if STACK_OVERFLOW_CHECK
564
writeStackCookie();
565
#endif
566
#if ASYNCIFY_DEBUG
567
dbg('ASYNCIFY/FIBER: entering fiber', newFiber, 'for the first time');
568
#endif
569
Asyncify.currData = null;
570
{{{ makeSetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, 0, '*') }}};
571
572
var userData = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.user_data, '*') }}};
573
{{{ makeDynCall('vp', 'entryPoint') }}}(userData);
574
} else {
575
var asyncifyData = newFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}};
576
Asyncify.currData = asyncifyData;
577
578
#if ASYNCIFY_DEBUG
579
dbg('ASYNCIFY/FIBER: start rewind', asyncifyData, '(resuming fiber', newFiber, ')');
580
#endif
581
Asyncify.state = Asyncify.State.Rewinding;
582
_asyncify_start_rewind(asyncifyData);
583
Asyncify.doRewind(asyncifyData);
584
}
585
},
586
},
587
588
emscripten_fiber_swap__deps: ["$Asyncify", "$Fibers", '$stackSave'],
589
emscripten_fiber_swap__async: true,
590
emscripten_fiber_swap: (oldFiber, newFiber) => {
591
if (ABORT) return;
592
#if ASYNCIFY_DEBUG
593
dbg('ASYNCIFY/FIBER: swap', oldFiber, '->', newFiber, 'state:', Asyncify.state);
594
#endif
595
if (Asyncify.state === Asyncify.State.Normal) {
596
Asyncify.state = Asyncify.State.Unwinding;
597
598
var asyncifyData = oldFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}};
599
Asyncify.setDataRewindFunc(asyncifyData);
600
Asyncify.currData = asyncifyData;
601
602
#if ASYNCIFY_DEBUG
603
dbg('ASYNCIFY/FIBER: start unwind', asyncifyData);
604
#endif
605
_asyncify_start_unwind(asyncifyData);
606
607
var stackTop = stackSave();
608
{{{ makeSetValue('oldFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, 'stackTop', '*') }}};
609
610
Fibers.nextFiber = newFiber;
611
} else {
612
#if ASSERTIONS
613
assert(Asyncify.state === Asyncify.State.Rewinding);
614
#endif
615
#if ASYNCIFY_DEBUG
616
dbg('ASYNCIFY/FIBER: stop rewind');
617
#endif
618
Asyncify.state = Asyncify.State.Normal;
619
_asyncify_stop_rewind();
620
Asyncify.currData = null;
621
}
622
},
623
#else // ASYNCIFY
624
emscripten_sleep: () => {
625
abort('Please compile your program with async support in order to use asynchronous operations like emscripten_sleep');
626
},
627
emscripten_wget: (url, file) => {
628
abort('Please compile your program with async support in order to use asynchronous operations like emscripten_wget');
629
},
630
emscripten_wget_data: (url, pbuffer, pnum, perror) => {
631
abort('Please compile your program with async support in order to use asynchronous operations like emscripten_wget_data');
632
},
633
emscripten_scan_registers: (func) => {
634
abort('Please compile your program with async support in order to use asynchronous operations like emscripten_scan_registers');
635
},
636
emscripten_fiber_swap: (oldFiber, newFiber) => {
637
abort('Please compile your program with async support in order to use asynchronous operations like emscripten_fiber_swap');
638
},
639
#endif // ASYNCIFY
640
});
641
642
if (ASYNCIFY) {
643
extraLibraryFuncs.push('$Asyncify');
644
}
645
646