Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -3502,6 +3502,12 @@ mergeInto(LibraryManager.library, {
return;
}
try {
#if ASSERTIONS
if (userCodeEntriesOnStack != 0) {
warnOnce('nested call to callUserCallback detected (This should only be used to enter from the event loop)');
}
userCodeEntriesOnStack += 1;
#endif
func();
#if EXIT_RUNTIME || USE_PTHREADS
#if USE_PTHREADS && !EXIT_RUNTIME
Expand All @@ -3512,6 +3518,11 @@ mergeInto(LibraryManager.library, {
} catch (e) {
handleException(e);
}
#if ASSERTIONS
finally {
userCodeEntriesOnStack -= 1;
}
#endif
},

$maybeExit__deps: ['exit', '$handleException',
Expand Down
8 changes: 8 additions & 0 deletions src/postamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ function callMain(args) {
abortWrapperDepth += 2;
#endif

#if ASSERTIONS
// TODO(sbc): Have `callMain` use `callUserCallback` to avoid duplicating
// this tracking.
userCodeEntriesOnStack += 1;
#endif
#if STANDALONE_WASM
entryFunction();
// _start (in crt1.c) will call exit() if main return non-zero. So we know
Expand Down Expand Up @@ -189,6 +194,9 @@ function callMain(args) {
return handleException(e);
#endif // !PROXY_TO_PTHREAD
} finally {
#if ASSERTIONS
userCodeEntriesOnStack -= 1;
#endif
calledMain = true;

#if ABORT_ON_WASM_EXCEPTIONS
Expand Down
5 changes: 5 additions & 0 deletions src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,11 @@ var runDependencyWatcher = null;
var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled
#if ASSERTIONS
var runDependencyTracking = {};
// Keeps track of how many times we have entered user code in the current stack.
// For example, this starts of at 0, and is set to 1 when we enter main. This
// enables callUserCallback to issue a warning when it is called from within
// user code (its designed to be used to enter user code from the event loop).
var userCodeEntriesOnStack = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this name still feels quite wrong me, sorry. How about callUserCallbackDepth? That seems unambiguous as it ties the variable to the function. Then if/when we rename that function we'd rename both.

#endif

function getUniqueRunDependency(id) {
Expand Down
36 changes: 36 additions & 0 deletions tests/other/test_nested_user_callback.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <assert.h>
#include <emscripten.h>
#include <stdlib.h>
#include <stdio.h>

void myatexit() {
puts("myatexit\n");
}

int main() {
atexit(myatexit);

puts("in main");

EM_ASM({
// Without this Push, the runtime will exit after the user callback.
runtimeKeepalivePush();
// Should generate a warning because we are already within user code.
callUserCallback(() => out("in user callback"));
runtimeKeepalivePop();

assert(!runtimeExited);
out('before: ' + runtimeExited);
callUserCallback(() => out("in user callback; without runtimeKeepalivePush"));
// callUserCallback without runtimeKeepalivePush will result in the runtime
// exiting at the end of the user callback.
// So we should never get here:
assert(false);
});

puts("returning from main");
// Should never get here since the EM_ASM block above causes application to
// exit.
assert(0);
return 0;
}
6 changes: 6 additions & 0 deletions tests/other/test_nested_user_callback.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
in main
nested call to callUserCallback detected (This should only be used to enter from the event loop)
in user callback
before: false
in user callback; without runtimeKeepalivePush
myatexit
7 changes: 6 additions & 1 deletion tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -11299,11 +11299,16 @@ def test_shell_Oz(self):
self.do_runf(test_file('hello_world.c'), 'hello, world!')

def test_runtime_keepalive(self):
self.uses_es6 = True
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$runtimeKeepalivePush', '$runtimeKeepalivePop', '$callUserCallback'])
self.set_setting('EXIT_RUNTIME')
self.do_other_test('test_runtime_keepalive.cpp')

def test_nested_user_callback(self):
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$runtimeKeepalivePush', '$runtimeKeepalivePop', '$callUserCallback'])
self.set_setting('EXIT_RUNTIME')
self.set_setting('ASSERTIONS')
self.do_other_test('test_nested_user_callback.c')

def test_em_asm_side_module(self):
err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('hello_world_em_asm.c')])
self.assertContained('EM_ASM is not supported in side modules', err)
Expand Down