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