From 84ab9abef251009d556204078c87d7de7ace2e62 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Tue, 1 Feb 2022 16:33:41 -0800 Subject: [PATCH] pthread+dylink: Ensure code is in sync on thread start We still don't have a great way to keep threads in sync but this at least means they will be correct at the moment the start. Part of #3494. Fixes: #16021 --- emcc.py | 4 +++ src/library_dylink.js | 26 ++++++++++++---- src/library_pthread.js | 6 ++++ .../pthread/test_pthread_dylink_entry_point.c | 30 +++++++++++++++++++ .../test_pthread_dylink_entry_point.out | 1 + .../test_pthread_dylink_entry_point_side.c | 8 +++++ tests/test_core.py | 14 +++++++++ 7 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 tests/core/pthread/test_pthread_dylink_entry_point.c create mode 100644 tests/core/pthread/test_pthread_dylink_entry_point.out create mode 100644 tests/core/pthread/test_pthread_dylink_entry_point_side.c diff --git a/emcc.py b/emcc.py index e5c1f4e71350e..a6ec695175cf7 100755 --- a/emcc.py +++ b/emcc.py @@ -2044,6 +2044,10 @@ def default_setting(name, new_default): 'emscripten_sync_run_in_main_thread_2', 'emscripten_sync_run_in_main_thread_4', ] + + if settings.MAIN_MODULE: + settings.REQUIRED_EXPORTS += ['_emscripten_thread_sync_code', '__dl_seterr'] + settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ '$exitOnMainThread', ] diff --git a/src/library_dylink.js b/src/library_dylink.js index 261a236baad94..24b72a39182da 100644 --- a/src/library_dylink.js +++ b/src/library_dylink.js @@ -115,12 +115,12 @@ var LibraryDylink = { err("unhandled export type for `" + symName + "`: " + (typeof value)); } #if DYLINK_DEBUG - err("updateGOT: after: " + symName + ' : ' + GOT[symName].value); + err("updateGOT: after: " + symName + ' : ' + GOT[symName].value + ' (' + value + ')'); #endif } #if DYLINK_DEBUG else if (GOT[symName].value != value) { - err("updateGOT: EXISTING SYMBOL: " + symName + ' : ' + GOT[symName].value + " " + value); + err("updateGOT: EXISTING SYMBOL: " + symName + ' : ' + GOT[symName].value + ' (' + value + ')'); } #endif } @@ -427,6 +427,13 @@ var LibraryDylink = { } }, +#if DYLINK_DEBUG + $dumpTable: function() { + for (var i = 0; i < wasmTable.length; i++) + err('table: ' + i + ' : ' + wasmTable.get(i)); + }, +#endif + // Loads a side module from binary data or compiled Module. Returns the module's exports or a // promise that resolves to its exports if the loadAsync flag is set. $loadWebAssemblyModule__deps: [ @@ -649,6 +656,7 @@ var LibraryDylink = { $loadDynamicLibrary: function(lib, flags, handle) { #if DYLINK_DEBUG err('loadDynamicLibrary: ' + lib + ' handle:' + handle); + err('existing: ' + Object.keys(LDSO.loadedLibsByName)); #endif if (lib == '__main__' && !LDSO.loadedLibsByName[lib]) { LDSO.loadedLibsByName[lib] = { @@ -798,7 +806,7 @@ var LibraryDylink = { }, // void* dlopen(const char* filename, int flags); - $dlopenInternal__deps: ['$FS', '$ENV', '$dlSetError'], + $dlopenInternal__deps: ['$FS', '$ENV', '$dlSetError', '$PATH'], $dlopenInternal: function(handle, jsflags) { // void *dlopen(const char *file, int mode); // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html @@ -807,6 +815,7 @@ var LibraryDylink = { #if DYLINK_DEBUG err('dlopenInternal: ' + filename); #endif + filename = PATH.normalize(filename); var searchpaths = []; var isValidFile = (filename) => { @@ -933,14 +942,19 @@ var LibraryDylink = { } if (typeof result === 'function') { +#if DYLINK_DEBUG + err('dlsym: ' + symbol + ' getting table slot for: ' + result); +#endif // Insert the function into the wasm table. If its a direct wasm function // the second argument will not be needed. If its a JS function we rely // on the `sig` attribute being set based on the `__sig` specified // in library JS file. - return addFunction(result, result.sig); - } else { - return result; + result = addFunction(result, result.sig); } +#if DYLINK_DEBUG + err('dlsym: ' + symbol + ' -> ' + result); +#endif + return result; }, #endif // MAIN_MODULE != 0 }; diff --git a/src/library_pthread.js b/src/library_pthread.js index 5a850513a488d..0f5cc42605b52 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -996,6 +996,12 @@ var LibraryPThread = { $invokeEntryPoint: function(ptr, arg) { #if PTHREADS_DEBUG err('invokeEntryPoint: ' + ptrToString(ptr)); +#endif +#if MAIN_MODULE + // Before we call the thread entry point, make sure any shared libraries + // have been loaded on this there. Otherwise our table migth be not be + // in sync and might not contain the function pointer `ptr` at all. + __emscripten_thread_sync_code(); #endif return {{{ makeDynCall('ii', 'ptr') }}}(arg); }, diff --git a/tests/core/pthread/test_pthread_dylink_entry_point.c b/tests/core/pthread/test_pthread_dylink_entry_point.c new file mode 100644 index 0000000000000..a91a4639b8f3f --- /dev/null +++ b/tests/core/pthread/test_pthread_dylink_entry_point.c @@ -0,0 +1,30 @@ +#include +#include +#include + +typedef void* (*thread_main_t)(void*); + +int main() { + puts("hello from main"); + + void *lib_handle = dlopen("./liblib.so", RTLD_NOW); + if (!lib_handle) { + puts("cannot load side module"); + puts(dlerror()); + return 1; + } + + thread_main_t tmain = (thread_main_t)dlsym(lib_handle, "side_module_thread_main"); + if (!tmain) { + puts("cannot find side function"); + return 1; + } + + printf("starting thread with %p\n", tmain); + pthread_t t; + pthread_create(&t, NULL, tmain, (void*)"hello"); + pthread_join(t, NULL); + + printf("done\n"); + return 0; +} diff --git a/tests/core/pthread/test_pthread_dylink_entry_point.out b/tests/core/pthread/test_pthread_dylink_entry_point.out new file mode 100644 index 0000000000000..f6c3e2c4445b2 --- /dev/null +++ b/tests/core/pthread/test_pthread_dylink_entry_point.out @@ -0,0 +1 @@ +call thread fn in side: hello diff --git a/tests/core/pthread/test_pthread_dylink_entry_point_side.c b/tests/core/pthread/test_pthread_dylink_entry_point_side.c new file mode 100644 index 0000000000000..189cff1e0e973 --- /dev/null +++ b/tests/core/pthread/test_pthread_dylink_entry_point_side.c @@ -0,0 +1,8 @@ +#include +#include +#include + +EMSCRIPTEN_KEEPALIVE void* side_module_thread_main(void* arg) { + printf("call thread fn in side: %s\n", (char*)arg); + return NULL; +} diff --git a/tests/test_core.py b/tests/test_core.py index 2ba8e62d11fc1..74438611b4a7e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8681,6 +8681,20 @@ def test_pthread_dylink(self): self.dylink_testf(main, so_name=very_long_name, need_reverse=False) + @parameterized({ + '': (['-sNO_AUTOLOAD_DYLIBS'],), + 'autoload': ([],) + }) + @needs_dylink + @node_pthreads + def test_pthread_dylink_entry_point(self, args): + self.emcc_args.append('-Wno-experimental') + self.set_setting('EXIT_RUNTIME') + self.set_setting('USE_PTHREADS') + self.set_setting('PTHREAD_POOL_SIZE', 1) + main = test_file('core/pthread/test_pthread_dylink_entry_point.c') + self.dylink_testf(main, need_reverse=False, emcc_args=args) + @needs_dylink @node_pthreads def test_pthread_dlopen(self):