Skip to content
Merged
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
6 changes: 6 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ See docs/process.md for more on how version tagging works.
default in #22257, and is no longer used by emscripten itself. It is also
problematic as it injects a global process.on handler. It is easy to replace
with a simple `--pre-js` file for those that require it. (#26326)
- The following APIs are now available in Wasm Workers:
- emscripten_futex_wait
- emscripten_futex_wake
- emscripten_is_main_runtime_thread
- emscripten_is_main_browser_thread
(#26325)
- Several low level emscripten APIs that return success/failure now return the
C `bool` type rather than `int`. For example `emscripten_proxy_sync` and
`emscripten_is_main_runtime_thread`. (#26316)
Expand Down
1 change: 1 addition & 0 deletions site/source/docs/api_reference/wasm_workers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ the middle.
Pthreads and Wasm Workers share several similarities:

* Both can use emscripten_atomic_* Atomics API,
* Both can use emscripten_futex_wait/wake API,
* Both can use GCC __sync_* Atomics API,
* Both can use C11 and C++11 Atomics APIs,
* Both types of threads have a local stack.
Expand Down
20 changes: 18 additions & 2 deletions src/lib/libwasm_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#error "internal error, feature_matrix should not allow this"
#endif

#endif // ~WASM_WORKERS

{{{
const workerSupportsFutexWait = () => AUDIO_WORKLET ? "!ENVIRONMENT_IS_AUDIO_WORKLET" : '1';
const wasmWorkerJs = `
Expand Down Expand Up @@ -59,7 +61,6 @@
}`;
}}}

#endif // ~WASM_WORKERS


addToLibrary({
Expand Down Expand Up @@ -342,5 +343,20 @@ if (ENVIRONMENT_IS_WASM_WORKER
else dispatch(-1/*idx*/, 2/*'timed-out'*/);
};
tryAcquireSemaphore();
}
},

#if !PTHREADS
// When pthreads are used we call `__set_thread_state` immediately on worker
// creation. When wasm workers is used without pthreads, we call
// `__set_thread_state` lazily to save code size for programs that don't use
// the threads state.
__do_set_thread_state__deps: ['__set_thread_state'],
__do_set_thread_state: (tb) => {
___set_thread_state(
/*thread_ptr=*/0,
/*is_main_thread=*/!ENVIRONMENT_IS_WORKER,
/*is_runtime_thread=*/!ENVIRONMENT_IS_WASM_WORKER,
/*supports_wait=*/ENVIRONMENT_IS_WORKER && {{{ workerSupportsFutexWait() }}});
},
#endif
});
2 changes: 2 additions & 0 deletions system/include/emscripten/threading.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ int emscripten_futex_wake(volatile void/*uint32_t*/ * _Nonnull addr, int count);

// Returns true if the current thread is the thread that hosts the Emscripten
// runtime.
// Returns false on pthreads and Wasm Workers.
bool emscripten_is_main_runtime_thread(void);

// Returns true if the current thread is the main browser thread. In the case
// that the Emscripten module is started in a worker there will be no thread
// for which this returns true.
// Returns false on pthreads and Wasm Workers.
bool emscripten_is_main_browser_thread(void);

// A temporary workaround to issue
Expand Down
47 changes: 41 additions & 6 deletions system/lib/pthread/emscripten_thread_state.S
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ is_runtime_thread:
.globaltype supports_wait, i32
supports_wait:

.section .text,"",@
#if WASM_WORKERS_ONLY
.globaltype done_init, i32
done_init:
#endif

.globl __get_tp
__get_tp:
.functype __get_tp () -> (PTR)
global.get thread_ptr
end_function
.section .text,"",@

.globl __set_thread_state
__set_thread_state:
Expand All @@ -39,23 +38,59 @@ __set_thread_state:
global.set supports_wait
end_function

#if WASM_WORKERS_ONLY
// With Wasm Workers we do lazy initializtion of the thread
// state so that only workers that call these APIs actually
// initializes their state.
.functype __do_set_thread_state () -> ()

lazy_init_thread_state:
.functype lazy_init_thread_state () -> ()
block
global.get done_init
br_if 0
call __do_set_thread_state
i32.const 1
global.set done_init
end_block
end_function
#endif

.globl __get_tp
__get_tp:
.functype __get_tp () -> (PTR)
#if WASM_WORKERS_ONLY
call lazy_init_thread_state
#endif
global.get thread_ptr
end_function

# Semantically the same as testing "!ENVIRONMENT_IS_PTHREAD" in JS
.globl emscripten_is_main_runtime_thread
emscripten_is_main_runtime_thread:
.functype emscripten_is_main_runtime_thread () -> (i32)
#if WASM_WORKERS_ONLY
call lazy_init_thread_state
#endif
global.get is_runtime_thread
end_function

# Semantically the same as testing "!ENVIRONMENT_IS_WORKER" in JS
.globl emscripten_is_main_browser_thread
emscripten_is_main_browser_thread:
.functype emscripten_is_main_browser_thread () -> (i32)
#if WASM_WORKERS_ONLY
call lazy_init_thread_state
#endif
global.get is_main_thread
end_function

# Semantically the same as testing "!ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_AUDIO_WORKLET" in JS
.globl _emscripten_thread_supports_atomics_wait
_emscripten_thread_supports_atomics_wait:
.functype _emscripten_thread_supports_atomics_wait () -> (i32)
#if WASM_WORKERS_ONLY
call lazy_init_thread_state
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if there might be a solution that would use the __postset construct, with a

init_thread_state__postset: '__do_set_thread_state()',
emscripten_futex_wait__deps: ['init_thread_state'],
emscripten_futex_wake__deps: ['init_thread_state'],
emscripten_is_main_runtime_thread__deps: ['init_thread_state'],
emscripten_is_main_browser_thread__deps: ['init_thread_state'],

This way we would avoid the complexity of the lazy init, but still, only emit the thread state initialization if any of the functions needing it would be called?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That sounds like a good idea if it could be made to work.

Its tricky because those function are not JS function so they can't currently have __deps entries like that.

But yes, it would be good find a solution that was dependency graph based rather than based on a runtime flag.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also this "thread state" is just a handful of wasm globals so I'm hoping maybe the cost if very marginal for real programs.

global.get supports_wait
end_function
26 changes: 15 additions & 11 deletions system/lib/pthread/library_pthread_stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ bool emscripten_has_threading_support() { return false; }

int emscripten_num_logical_cores() { return 1; }

#ifndef __EMSCRIPTEN_WASM_WORKERS__
// These low level primites are defined in both pthreads and wasm workers
// builds.

int emscripten_futex_wait(volatile void /*uint32_t*/* addr,
uint32_t val,
double maxWaitMilliseconds) {
Expand All @@ -39,10 +43,20 @@ int emscripten_futex_wake(volatile void /*uint32_t*/* addr, int count) {
}

bool emscripten_is_main_runtime_thread() {
// TODO: We probably shouldn't be returning true here in WASM_WORKERS builds.
return true;
}

void __wait(volatile int *addr, volatile int *waiters, int val, int priv) {}

void __lock(void* ptr) {}

void __unlock(void* ptr) {}

void __acquire_ptc() {}

void __release_ptc() {}
#endif

void emscripten_main_thread_process_queued_calls() {
// nop
}
Expand Down Expand Up @@ -385,16 +399,6 @@ int sem_destroy(sem_t *sem) {
return 0;
}

void __wait(volatile int *addr, volatile int *waiters, int val, int priv) {}

void __lock(void* ptr) {}

void __unlock(void* ptr) {}

void __acquire_ptc() {}

void __release_ptc() {}

// When pthreads is not enabled, we can't use the Atomics futex api to do
// proper sleeps, so simulate a busy spin wait loop instead.
void emscripten_thread_sleep(double msecs) {
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_cxx_ctors1.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"a.out.js": 19555,
"a.out.js.gz": 8102,
"a.out.nodebug.wasm": 132828,
"a.out.nodebug.wasm.gz": 49874,
"a.out.nodebug.wasm.gz": 49876,
"total": 152383,
"total_gz": 57976,
"total_gz": 57978,
"sent": [
"__cxa_throw",
"_abort_js",
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_cxx_ctors2.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"a.out.js": 19532,
"a.out.js.gz": 8087,
"a.out.nodebug.wasm": 132248,
"a.out.nodebug.wasm.gz": 49531,
"a.out.nodebug.wasm.gz": 49533,
"total": 151780,
"total_gz": 57618,
"total_gz": 57620,
"sent": [
"__cxa_throw",
"_abort_js",
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_cxx_except.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"a.out.js": 23216,
"a.out.js.gz": 9081,
"a.out.nodebug.wasm": 172758,
"a.out.nodebug.wasm.gz": 57395,
"a.out.nodebug.wasm.gz": 57391,
"total": 195974,
"total_gz": 66476,
"total_gz": 66472,
"sent": [
"__cxa_begin_catch",
"__cxa_end_catch",
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_cxx_except_wasm.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"a.out.js": 19366,
"a.out.js.gz": 8022,
"a.out.nodebug.wasm": 148153,
"a.out.nodebug.wasm.gz": 55275,
"a.out.nodebug.wasm.gz": 55273,
"total": 167519,
"total_gz": 63297,
"total_gz": 63295,
"sent": [
"_abort_js",
"_tzset_js",
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_cxx_except_wasm_legacy.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"a.out.js": 19440,
"a.out.js.gz": 8042,
"a.out.nodebug.wasm": 145959,
"a.out.nodebug.wasm.gz": 54906,
"a.out.nodebug.wasm.gz": 54905,
"total": 165399,
"total_gz": 62948,
"total_gz": 62947,
"sent": [
"_abort_js",
"_tzset_js",
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_cxx_mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"a.out.js": 23266,
"a.out.js.gz": 9101,
"a.out.nodebug.wasm": 239192,
"a.out.nodebug.wasm.gz": 79781,
"a.out.nodebug.wasm.gz": 79786,
"total": 262458,
"total_gz": 88882,
"total_gz": 88887,
"sent": [
"__cxa_begin_catch",
"__cxa_end_catch",
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_cxx_noexcept.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"a.out.js": 19555,
"a.out.js.gz": 8102,
"a.out.nodebug.wasm": 134835,
"a.out.nodebug.wasm.gz": 50701,
"a.out.nodebug.wasm.gz": 50703,
"total": 154390,
"total_gz": 58803,
"total_gz": 58805,
"sent": [
"__cxa_throw",
"_abort_js",
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_cxx_wasmfs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"a.out.js": 7053,
"a.out.js.gz": 3325,
"a.out.nodebug.wasm": 172904,
"a.out.nodebug.wasm.gz": 63288,
"a.out.nodebug.wasm.gz": 63290,
"total": 179957,
"total_gz": 66613,
"total_gz": 66615,
"sent": [
"__cxa_throw",
"_abort_js",
Expand Down
3 changes: 1 addition & 2 deletions test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5042,8 +5042,7 @@ def test_wasm_worker_hello_embedded(self):
# Tests that it is possible to call emscripten_futex_wait() in Wasm Workers when pthreads
# are also enabled.
@parameterized({
# Without pthreads we expect the stub version of the futex API
'': (['-DEXPECT_STUB'],),
'': ([],),
'pthread': (['-pthread'],),
})
def test_wasm_worker_futex_wait(self, args):
Expand Down
4 changes: 4 additions & 0 deletions test/wasm_worker/hello_wasm_worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <emscripten/console.h>
#include <emscripten/em_asm.h>
#include <emscripten/wasm_worker.h>
#include <emscripten/threading.h>
#include <assert.h>

// This is the code example in site/source/docs/api_reference/wasm_workers.rst
Expand All @@ -13,11 +14,14 @@ void do_exit() {

void run_in_worker() {
emscripten_out("Hello from wasm worker!");
assert(!emscripten_is_main_runtime_thread());
assert(!emscripten_is_main_browser_thread());
EM_ASM(typeof checkStackCookie == 'function' && checkStackCookie());
emscripten_wasm_worker_post_function_v(EMSCRIPTEN_WASM_WORKER_ID_PARENT, do_exit);
}

int main() {
assert(emscripten_is_main_runtime_thread());
emscripten_wasm_worker_t worker = emscripten_malloc_wasm_worker(/*stack size: */1024);
assert(worker);
emscripten_wasm_worker_post_function_v(worker, run_in_worker);
Expand Down
5 changes: 0 additions & 5 deletions test/wasm_worker/wasm_worker_futex_wait.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ void worker_main() {
int rc = emscripten_futex_wait(&futex_value, 0, 100);
double end = emscripten_performance_now();
printf("emscripten_futex_wait returned: %d after %.2fms\n", rc, end - start);
#if EXPECT_STUB
// The stub implemenation returns -ENOTSUP
assert(rc == -ENOTSUP);
#else
assert(rc == -ETIMEDOUT);
assert((end - start) >= 100);

Expand All @@ -41,7 +37,6 @@ void worker_main() {
printf("emscripten_futex_wait returned: %d\n", rc);
assert(rc == 0);
assert(futex_value == 1);
#endif

#ifdef REPORT_RESULT
REPORT_RESULT(0);
Expand Down
4 changes: 3 additions & 1 deletion tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -1540,9 +1540,11 @@ def limit_incoming_module_api():
if settings.MEMORY64 and settings.RELOCATABLE:
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append('__table_base32')

if settings.WASM_WORKERS or (settings.SHARED_MEMORY and not settings.PTHREADS):
add_system_js_lib('libwasm_worker.js')

if settings.WASM_WORKERS:
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$_wasmWorkerInitializeRuntime']
add_system_js_lib('libwasm_worker.js')

# Set min browser versions based on certain settings such as WASM_BIGINT,
# PTHREADS, AUDIO_WORKLET
Expand Down
Loading