Skip to content

[pthreads] Proxy _emscripten_fs_load_embedded_files to main thread - #28270

Open
sbc100 wants to merge 1 commit into
emscripten-core:mainfrom
sbc100:pthread_dlopen_embed
Open

sbc100 wants to merge 1 commit into
emscripten-core:mainfrom
sbc100:pthread_dlopen_embed

Conversation

@sbc100

@sbc100 sbc100 commented Nov 15, 2026

Copy link
Copy Markdown
Collaborator

When a side module built with --embed-file is dlopened from a pthread, its static constructors run on that worker thread (firstLoad is true there). Without __proxy: 'sync',
_emscripten_fs_load_embedded_files populated the worker's local FS (or un-flushed WasmFS preloaded arrays) instead of the main thread's filesystem.

Also, add some assertions to make this kind of thing easier to detect in the future.

Fixes: #28256

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces thread-safety assertions for the File System (FS) on pthreads, proxies embedded file loading to the main thread, and adds a new test for loading embedded files via dlopen on a pthread. The review feedback highlights two main issues: first, in the new C test, returning from the thread main function during an asynchronous dlopen can prematurely terminate the thread runtime, so emscripten_exit_with_live_runtime() should be used instead; second, the Python test violates the newly added documentation guidelines by mutating self.cflags directly rather than passing compiler flags inline.

Comment on lines +43 to +55
#ifdef USE_ASYNC_DLOPEN
emscripten_dlopen("libside.so", RTLD_NOW | RTLD_GLOBAL, NULL, on_dl_success, on_dl_fail);
#else
void* handle = dlopen("libside.so", RTLD_NOW | RTLD_GLOBAL);
assert(handle);
func_t read_embedded_file = (func_t)dlsym(handle, "read_embedded_file");
assert(read_embedded_file);
read_embedded_file();
check_embedded_file("pthread");
dlclose(handle);
thread_done = true;
#endif
return NULL;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

When using asynchronous emscripten_dlopen on a pthread, returning NULL from thread_main immediately exits the thread and cleans up its resources. This can prevent the asynchronous on_dl_success callback from executing, or cause it to run on a defunct thread context, leading to undefined behavior or test flakiness. To keep the thread's runtime alive so it can process the async callback, use emscripten_exit_with_live_runtime() instead of returning.

#ifdef USE_ASYNC_DLOPEN
  emscripten_dlopen("libside.so", RTLD_NOW | RTLD_GLOBAL, NULL, on_dl_success, on_dl_fail);
  emscripten_exit_with_live_runtime();
#else
  void* handle = dlopen("libside.so", RTLD_NOW | RTLD_GLOBAL);
  assert(handle);
  func_t read_embedded_file = (func_t)dlsym(handle, "read_embedded_file");
  assert(read_embedded_file);
  read_embedded_file();
  check_embedded_file("pthread");
  dlclose(handle);
  thread_done = true;
  return NULL;
#endif

Comment thread test/test_core.py
Comment on lines +9489 to +9495
self.cflags += ['-Wno-experimental', '-pthread']
self.build_dlfcn_lib(test_file('core/pthread/test_pthread_dlopen_embed_side.c'),
cflags=['--embed-file', 'embedded.txt'])

self.prep_dlfcn_main()
self.do_runf('core/pthread/test_pthread_dlopen_embed.c', 'done\n',
cflags=args + ['-sEXIT_RUNTIME'])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This test mutates self.cflags directly, which violates the newly added guideline in docs/process.md stating that compiler/linker flags should be passed via inline cflags= arguments to test methods rather than mutating self.cflags. We can pass the flags directly to build_dlfcn_lib and do_runf instead.

    self.build_dlfcn_lib(test_file('core/pthread/test_pthread_dlopen_embed_side.c'),
                         cflags=['--embed-file', 'embedded.txt', '-Wno-experimental', '-pthread'])

    self.prep_dlfcn_main()
    self.do_runf('core/pthread/test_pthread_dlopen_embed.c', 'done\n',
                 cflags=args + ['-sEXIT_RUNTIME', '-Wno-experimental', '-pthread'])

When a side module built with `--embed-file` is `dlopen`ed from a
pthread, its static constructors run on that worker thread (`firstLoad`
is true there). Without `__proxy: 'sync'`,
`_emscripten_fs_load_embedded_files` populated the worker's local `FS`
(or un-flushed WasmFS preloaded arrays) instead of the main thread's
filesystem.

Also, add some assertions to make this kind of thing easier to detect
in the future.

Fixes: emscripten-core#28256

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

File does not appear in MemFS when dlopen a side module with --embed-file in a std::thread

1 participant