Skip to content

Commit

Permalink
Avoid proxying atexit calls back to main thread.
Browse files Browse the repository at this point in the history
To achieve this we manage the `atexit` functions in native code using
existing musl code.

This is small step towards a large change to just use musl for all
`atexit` handling: #14479.

The codesize implications of this change are a mixed bag.  In some
places we see saving but in other cases the extra export causes a small
regression (only when EXIT_RUNTIME=1).  In the long, once we land #14479
there should be more code size saving to be had by doing everything on
the native side.

Fixes #15868
  • Loading branch information
sbc100 committed Jan 7, 2022
1 parent b77adc9 commit e89185d
Show file tree
Hide file tree
Showing 32 changed files with 82 additions and 36 deletions.
3 changes: 3 additions & 0 deletions emcc.py
Expand Up @@ -2328,6 +2328,9 @@ def check_memory_setting(setting):
# enables the --post-emscripten pass
settings.GLOBAL_BASE = 1024

if settings.EXIT_RUNTIME:
settings.REQUIRED_EXPORTS += ['__funcs_on_exit']

if settings.MINIMAL_RUNTIME:
if settings.EXIT_RUNTIME:
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['proc_exit', '$callRuntimeCallbacks']
Expand Down
15 changes: 0 additions & 15 deletions src/library.js
Expand Up @@ -361,21 +361,6 @@ LibraryManager.library = {
// stdlib.h
// ==========================================================================

#if MINIMAL_RUNTIME && !EXIT_RUNTIME
atexit__sig: 'v', // atexit unsupported in MINIMAL_RUNTIME
atexit: function(){},
__cxa_atexit: function(){},
#else
atexit__proxy: 'sync',
atexit__sig: 'iii',
atexit: function(func, arg) {
#if EXIT_RUNTIME
__ATEXIT__.unshift({ func: func, arg: arg });
#endif
},
__cxa_atexit: 'atexit',
#endif

// TODO: There are currently two abort() functions that get imported to asm
// module scope: the built-in runtime function abort(), and this library
// function _abort(). Remove one of these, importing two functions for the
Expand Down
3 changes: 3 additions & 0 deletions src/preamble.js
Expand Up @@ -430,6 +430,9 @@ function exitRuntime() {
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
#endif
#if EXIT_RUNTIME
#if !STANDALONE_WASM
___funcs_on_exit(); // Native atexit() functions
#endif
callRuntimeCallbacks(__ATEXIT__);
<<< ATEXITS >>>
#endif
Expand Down
6 changes: 6 additions & 0 deletions system/lib/libc/musl/src/exit/atexit.c
Expand Up @@ -49,6 +49,11 @@ int __cxa_atexit(void (*func)(void *), void *arg, void *dso)

/* If the current function list is full, add a new one */
if (slot==COUNT) {
#ifdef __EMSCRIPTEN__
// Avoid the malloc dependency and just abort if we exceed the
// the static limit.
abort();
#else
struct fl *new_fl = calloc(sizeof(struct fl), 1);
if (!new_fl) {
UNLOCK(lock);
Expand All @@ -57,6 +62,7 @@ int __cxa_atexit(void (*func)(void *), void *arg, void *dso)
new_fl->next = head;
head = new_fl;
slot = 0;
#endif
}

/* Append function to the list. */
Expand Down
2 changes: 1 addition & 1 deletion tests/other/metadce/hello_libcxx_O2.jssize
@@ -1 +1 @@
98361
98236
1 change: 0 additions & 1 deletion tests/other/metadce/hello_libcxx_O2.sent
@@ -1,4 +1,3 @@
__cxa_atexit
abort
emscripten_memcpy_big
emscripten_resize_heap
Expand Down
2 changes: 1 addition & 1 deletion tests/other/metadce/hello_libcxx_O2.size
@@ -1 +1 @@
124705
124869
2 changes: 1 addition & 1 deletion tests/other/metadce/hello_libcxx_O2_fexceptions.jssize
@@ -1 +1 @@
111833
111708
1 change: 0 additions & 1 deletion tests/other/metadce/hello_libcxx_O2_fexceptions.sent
@@ -1,5 +1,4 @@
__cxa_allocate_exception
__cxa_atexit
__cxa_begin_catch
__cxa_end_catch
__cxa_find_matching_catch_2
Expand Down
2 changes: 1 addition & 1 deletion tests/other/metadce/hello_libcxx_O2_fexceptions.size
@@ -1 +1 @@
166697
166882
@@ -1 +1 @@
112820
112695
@@ -1,5 +1,4 @@
__cxa_allocate_exception
__cxa_atexit
__cxa_begin_catch
__cxa_end_catch
__cxa_find_matching_catch_2
Expand Down
@@ -1 +1 @@
226724
226909
@@ -1,3 +1,4 @@
__funcs_on_exit
__indirect_function_table
_start
memory
@@ -1 +1 @@
15003
15208
@@ -1 +1 @@
181
199
@@ -1,3 +1,4 @@
__funcs_on_exit
__indirect_function_table
_start
memory
@@ -1 +1 @@
15898
16103
@@ -1 +1 @@
6303
6321
1 change: 1 addition & 0 deletions tests/other/metadce/mem_O3_STANDALONE_WASM.exports
@@ -1,3 +1,4 @@
__funcs_on_exit
__indirect_function_table
_start
memory
2 changes: 1 addition & 1 deletion tests/other/metadce/mem_O3_STANDALONE_WASM.jssize
@@ -1 +1 @@
15720
15925
2 changes: 1 addition & 1 deletion tests/other/metadce/mem_O3_STANDALONE_WASM.size
@@ -1 +1 @@
6226
6244
1 change: 1 addition & 0 deletions tests/other/metadce/mem_no_argv_O3_STANDALONE_WASM.exports
@@ -1,3 +1,4 @@
__funcs_on_exit
__indirect_function_table
_start
memory
2 changes: 1 addition & 1 deletion tests/other/metadce/mem_no_argv_O3_STANDALONE_WASM.jssize
@@ -1 +1 @@
15003
15208
2 changes: 1 addition & 1 deletion tests/other/metadce/mem_no_argv_O3_STANDALONE_WASM.size
@@ -1 +1 @@
6026
6044
@@ -1,3 +1,4 @@
__funcs_on_exit
__indirect_function_table
_start
memory
@@ -1 +1 @@
15003
15208
@@ -1 +1 @@
4707
4725
37 changes: 37 additions & 0 deletions tests/pthread/test_pthread_busy_wait_atexit.cpp
@@ -0,0 +1,37 @@
#include <stdatomic.h>
#include <stdbool.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <emscripten/console.h>

_Atomic bool done = false;

void exit_handler() {
printf("exit_handler\n");
}

void* thread_main(void*) {
// Avoid using printf here since stdio is proxies back to the
// main thread which is busy looping
_emscripten_out("in thread");
atexit(exit_handler);
done = true;
return NULL;
}

// Similar to test_pthread_busy_wait.cpp but with lower level pthreads
// API and explcit use of atexit before setting done to true.
// We also don't make any calls during the busy loop which means that
// proxied calls are *not* processed.
int main() {
printf("in main\n");
pthread_t t;
pthread_create(&t, NULL, thread_main, NULL);

while (!done) { }

pthread_join(t, NULL);
printf("done main\n");
return 0;
}
4 changes: 4 additions & 0 deletions tests/pthread/test_pthread_busy_wait_atexit.out
@@ -0,0 +1,4 @@
in main
in thread
done main
exit_handler
6 changes: 6 additions & 0 deletions tests/test_core.py
Expand Up @@ -8486,6 +8486,12 @@ def test_pthread_busy_wait(self):
self.set_setting('EXIT_RUNTIME')
self.do_run_in_out_file_test('pthread/test_pthread_busy_wait.cpp')

@node_pthreads
def test_pthread_busy_wait_atexit(self):
self.set_setting('PTHREAD_POOL_SIZE', 1)
self.set_setting('EXIT_RUNTIME')
self.do_run_in_out_file_test('pthread/test_pthread_busy_wait_atexit.cpp')

@node_pthreads
def test_pthread_create_pool(self):
# with a pool, we can synchronously depend on workers being available
Expand Down
4 changes: 2 additions & 2 deletions tools/system_libs.py
Expand Up @@ -918,7 +918,7 @@ def get_files(self):

libc_files += files_in_path(
path='system/lib/libc/musl/src/exit',
filenames=['_Exit.c'])
filenames=['_Exit.c', 'atexit.c'])

libc_files += files_in_path(
path='system/lib/libc/musl/src/ldso',
Expand Down Expand Up @@ -1562,7 +1562,7 @@ def get_files(self):
# including fprintf etc.
files += files_in_path(
path='system/lib/libc/musl/src/exit',
filenames=['assert.c', 'atexit.c', 'exit.c'])
filenames=['assert.c', 'exit.c'])
return files

def can_use(self):
Expand Down

0 comments on commit e89185d

Please sign in to comment.