diff --git a/system/lib/libc/emscripten_internal.h b/system/lib/libc/emscripten_internal.h index 0c85dbc4fcb72..15465864c4d7a 100644 --- a/system/lib/libc/emscripten_internal.h +++ b/system/lib/libc/emscripten_internal.h @@ -10,6 +10,8 @@ * for `tools/gen_sig_info.py` to work. This file contains declarations for * functions that are not declared in any other public or private header. */ +#ifndef __EMSCRIPTEN_INTERNAL_H__ +#define __EMSCRIPTEN_INTERNAL_H__ #include #include @@ -148,3 +150,5 @@ int _poll_js(void* fds, int nfds, int timeout, void* ctx, void* arg); #ifdef __cplusplus } #endif + +#endif /* __EMSCRIPTEN_INTERNAL_H__ */ diff --git a/system/lib/libc/musl/src/internal/pthread_impl.h b/system/lib/libc/musl/src/internal/pthread_impl.h index 7cff31e1108d6..b2588344eb1ab 100644 --- a/system/lib/libc/musl/src/internal/pthread_impl.h +++ b/system/lib/libc/musl/src/internal/pthread_impl.h @@ -275,4 +275,13 @@ extern hidden unsigned __default_guardsize; #define __ATTRP_C11_THREAD ((void*)(uintptr_t)-1) +#ifdef __EMSCRIPTEN_SHARED_MEMORY__ +pid_t gettid(void); +// Unlike `__pthread_self()->tid, `gettid` works under both wasm workers and +// pthreads. +#define CURRENT_THREAD_ID gettid() +#else +#define CURRENT_THREAD_ID __pthread_self()->tid +#endif + #endif diff --git a/system/lib/libc/musl/src/internal/stdio_impl.h b/system/lib/libc/musl/src/internal/stdio_impl.h index d1c3978ffa808..3dded20e50149 100644 --- a/system/lib/libc/musl/src/internal/stdio_impl.h +++ b/system/lib/libc/musl/src/internal/stdio_impl.h @@ -6,7 +6,7 @@ #define UNGET 8 -#if defined(__EMSCRIPTEN__) && !defined(_REENTRANT) +#if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_SHARED_MEMORY__) #define FFINALLOCK(f) #define FLOCK(f) #define FUNLOCK(f) diff --git a/system/lib/libc/musl/src/linux/gettid.c b/system/lib/libc/musl/src/linux/gettid.c index 147128f8ba95e..cd401dd52a3cd 100644 --- a/system/lib/libc/musl/src/linux/gettid.c +++ b/system/lib/libc/musl/src/linux/gettid.c @@ -2,9 +2,7 @@ #include #include "pthread_impl.h" -#ifdef __EMSCRIPTEN_WASM_WORKERS__ -#include -#elif defined(__EMSCRIPTEN_PTHREADS__) +#ifdef __EMSCRIPTEN__ weak int emscripten_wasm_worker_self_id(); #endif @@ -16,7 +14,14 @@ pid_t gettid(void) { #ifdef __EMSCRIPTEN_WASM_WORKERS__ // Offset the worker ID by 1 so we never return 0 from this function. - return emscripten_wasm_worker_self_id() + 1; + // Strangly we cannot assume the existence of emscripten_wasm_worker_self_id + // here because libc-ww is also used for `-sSHARED_MEMORY` builds (without + // libwasm_workers linked in. + if (emscripten_wasm_worker_self_id) { + return emscripten_wasm_worker_self_id() + 1; + } else { + return 42; + } #else #if defined(__EMSCRIPTEN_PTHREADS__) // The pthread-variant of libc can also be used alongside wasm workers. diff --git a/system/lib/libc/musl/src/stdio/__lockfile.c b/system/lib/libc/musl/src/stdio/__lockfile.c index 2b648c05660a2..3300a3c25495b 100644 --- a/system/lib/libc/musl/src/stdio/__lockfile.c +++ b/system/lib/libc/musl/src/stdio/__lockfile.c @@ -3,8 +3,8 @@ int __lockfile(FILE *f) { -#if defined(__EMSCRIPTEN_PTHREADS__) - int owner = f->lock, tid = __pthread_self()->tid; +#ifdef __EMSCRIPTEN_SHARED_MEMORY__ + int owner = f->lock, tid = CURRENT_THREAD_ID; if ((owner & ~MAYBE_WAITERS) == tid) return 0; owner = a_cas(&f->lock, 0, tid); @@ -20,7 +20,7 @@ int __lockfile(FILE *f) void __unlockfile(FILE *f) { -#if defined(__EMSCRIPTEN_PTHREADS__) +#ifdef __EMSCRIPTEN_SHARED_MEMORY__ if (a_swap(&f->lock, 0) & MAYBE_WAITERS) __wake(&f->lock, 1, 1); #endif diff --git a/system/lib/libc/musl/src/stdio/getc.h b/system/lib/libc/musl/src/stdio/getc.h index e24f9905c1f8c..f2700b41d2223 100644 --- a/system/lib/libc/musl/src/stdio/getc.h +++ b/system/lib/libc/musl/src/stdio/getc.h @@ -16,7 +16,7 @@ static int locking_getc(FILE *f) static inline int do_getc(FILE *f) { int l = f->lock; - if (l < 0 || l && (l & ~MAYBE_WAITERS) == __pthread_self()->tid) + if (l < 0 || l && (l & ~MAYBE_WAITERS) == CURRENT_THREAD_ID) return getc_unlocked(f); return locking_getc(f); } diff --git a/system/lib/libc/musl/src/stdio/putc.h b/system/lib/libc/musl/src/stdio/putc.h index 2014c4ec4af9c..a284f853f3b02 100644 --- a/system/lib/libc/musl/src/stdio/putc.h +++ b/system/lib/libc/musl/src/stdio/putc.h @@ -16,7 +16,7 @@ static int locking_putc(int c, FILE *f) static inline int do_putc(int c, FILE *f) { int l = f->lock; - if (l < 0 || l && (l & ~MAYBE_WAITERS) == __pthread_self()->tid) + if (l < 0 || l && (l & ~MAYBE_WAITERS) == CURRENT_THREAD_ID) return putc_unlocked(c, f); return locking_putc(c, f); } diff --git a/system/lib/wasm_worker/library_wasm_worker.c b/system/lib/wasm_worker/library_wasm_worker.c index a1327c3700b13..15484acaa29de 100644 --- a/system/lib/wasm_worker/library_wasm_worker.c +++ b/system/lib/wasm_worker/library_wasm_worker.c @@ -1,3 +1,20 @@ +/* + * Copyright 2022 The Emscripten Authors. All rights reserved. + * Emscripten is available under two separate licenses, the MIT license and the + * University of Illinois/NCSA Open Source License. Both these licenses can be + * found in the LICENSE file. + */ + +// libc files are compiled as -std=c99 which doesn't normally declare +// max_align_t. +#if __STDC_VERSION__ < 201112L +#define __NEED_max_align_t +#endif + +#include "libc.h" +#include "stdio_impl.h" +#include "emscripten_internal.h" + #include #include #include @@ -7,8 +24,6 @@ #include #include // For MAX() -#include "emscripten_internal.h" - #ifndef __EMSCRIPTEN_WASM_WORKERS__ #error __EMSCRIPTEN_WASM_WORKERS__ should be defined when building this file! #endif @@ -33,6 +48,15 @@ static void emscripten_wasm_worker_main_thread_initialize() { *sbrk_ptr += ROUND_UP(__builtin_wasm_tls_size(), SBRK_ALIGN); } +static FILE *volatile dummy_file = 0; +weak_alias(dummy_file, __stdin_used); +weak_alias(dummy_file, __stdout_used); +weak_alias(dummy_file, __stderr_used); + +static void init_file_lock(FILE *f) { + if (f && f->lock<0) f->lock = 0; +} + emscripten_wasm_worker_t emscripten_create_wasm_worker(void *stackPlusTLSAddress, size_t stackPlusTLSSize) { assert(stackPlusTLSAddress != 0); assert((uintptr_t)stackPlusTLSAddress % STACK_ALIGN == 0); @@ -61,6 +85,22 @@ emscripten_wasm_worker_t emscripten_create_wasm_worker(void *stackPlusTLSAddress stackPlusTLSAddress = (void*)tlsBase; stackPlusTLSSize -= padding; } + + if (!libc.threaded) { + for (FILE *f=*__ofl_lock(); f; f=f->next) { + init_file_lock(f); + } + __ofl_unlock(); + init_file_lock(__stdin_used); + init_file_lock(__stdout_used); + init_file_lock(__stderr_used); + libc.threaded = 1; + } + + // Unlike with ptheads, wasm workers never really exit and so this counter + // only going one way here. + if (!libc.threads_minus_1++) libc.need_locks = 1; + return _emscripten_create_wasm_worker(stackPlusTLSAddress, stackPlusTLSSize); } diff --git a/test/codesize/hello_wasm_worker_wasm.expected.js b/test/codesize/hello_wasm_worker_wasm.expected.js index 2a61cfc803f62..73c0e9ee8352f 100644 --- a/test/codesize/hello_wasm_worker_wasm.expected.js +++ b/test/codesize/hello_wasm_worker_wasm.expected.js @@ -1,75 +1,80 @@ -var c = Module, d = "em-ww" == globalThis.name, e, f, y, z, l, A, t; +var c = Module, d = "em-ww" == globalThis.name, e = !!globalThis.WorkerGlobalScope, f, g, C, r, D, n, E, w; d && (onmessage = a => { onmessage = null; - e = a = a.data; - f = a.l; - g(); - c ||= {}; - c.wasm = a.j; + f = a = a.data; + g = a.o; h(); - a.j = a.l = 0; + c ||= {}; + c.wasm = a.m; + k(); + a.m = a.o = 0; }); -function g() {} +function h() {} -d || (f = c.mem || new WebAssembly.Memory({ +d || (g = c.mem || new WebAssembly.Memory({ initial: 256, maximum: 256, shared: !0 -}), g()); +}), h()); -var k = [], n = a => { +var l = [], p = a => { a = a.data; let b = a._wsc; - b && l.get(b)(...a.x); -}, p = a => { - k.push(a); -}, q = {}, r = 1, u = (a, b) => { - let m = q[r] = new Worker(c.js, { + b && n.get(b)(...a.x); +}, q = a => { + l.push(a); +}, t = () => { + r(0, !e, !d, e && 1); +}, u = {}, v = 1, x = (a, b) => { + let m = u[v] = new Worker(c.js, { name: "em-ww" }); m.postMessage({ - s: r, - j: t, - l: f, - m: a, - o: b + v: v, + m: w, + o: g, + s: a, + u: b }); - m.onmessage = n; - return r++; -}, v = () => !1, w = (a, b) => { - q[a].postMessage({ + m.onmessage = p; + return v++; +}, y = () => performance.now(), z = () => !1, A = (a, b) => { + u[a].postMessage({ _wsc: b, x: [] }); }; -d && (q[0] = globalThis, addEventListener("message", p)); +d && (u[0] = globalThis, addEventListener("message", q)); -function x() { +function B() { console.log("Hello from wasm worker!"); } -function h() { - A = { - b: u, - c: v, - d: w, - e: x, - a: f +function k() { + E = { + e: t, + c: x, + b: y, + d: z, + f: A, + g: B, + a: g }; WebAssembly.instantiate(c.wasm, { - a: A + a: E }).then((a => { var b = (a.instance || a).exports; - t = a.module || c.wasm; - y = b.g; - z = b.i; - l = b.h; - d ? (z(e.s, e.m, e.o), removeEventListener("message", p), k = k.forEach(n), addEventListener("message", n)) : b.f(); - d || y(); + w = a.module || c.wasm; + C = b.i; + r = b.k; + D = b.l; + n = b.j; + d ? (D(f.v, f.s, f.u), removeEventListener("message", q), l = l.forEach(p), addEventListener("message", p)) : b.h(); + d || C(); })); } -d || h(); \ No newline at end of file +d || k(); \ No newline at end of file diff --git a/test/codesize/test_minimal_runtime_code_size_hello_wasm_worker.json b/test/codesize/test_minimal_runtime_code_size_hello_wasm_worker.json index 9f2ca39bb3535..e1aa99671bff8 100644 --- a/test/codesize/test_minimal_runtime_code_size_hello_wasm_worker.json +++ b/test/codesize/test_minimal_runtime_code_size_hello_wasm_worker.json @@ -1,10 +1,10 @@ { "a.html": 515, "a.html.gz": 355, - "a.js": 859, - "a.js.gz": 545, - "a.wasm": 1847, - "a.wasm.gz": 1071, - "total": 3221, - "total_gz": 1971 + "a.js": 956, + "a.js.gz": 605, + "a.wasm": 2744, + "a.wasm.gz": 1530, + "total": 4215, + "total_gz": 2490 } diff --git a/test/core/test_stdio_locking.c b/test/core/test_stdio_locking.c index 538c157e36236..f90086419c854 100644 --- a/test/core/test_stdio_locking.c +++ b/test/core/test_stdio_locking.c @@ -14,7 +14,10 @@ #include #include -pthread_t thread[2]; +#ifdef __EMSCRIPTEN_WASM_WORKERS__ +#include +#include +#endif char *char_repeat(int n, char c) { char *dest = malloc(n + 1); @@ -23,16 +26,41 @@ char *char_repeat(int n, char c) { return dest; } -void *thread_main(void *arg) { +void thread_func() { char *msg = char_repeat(100, 'a'); - for (int i = 0; i < 10; ++i) - printf("%s\n", msg); + for (int i = 0; i < 10; ++i) { + printf("%s\n", msg); + } free(msg); +} + +#if defined(__EMSCRIPTEN_PTHREADS__) +void *thread_main(void *arg) { + thread_func(); return 0; } +#endif + +#if defined(__EMSCRIPTEN_WASM_WORKERS__) +void terminate_worker(void* userData) { + emscripten_terminate_all_wasm_workers(); + printf("main done\n"); +} +#endif int main() { printf("in main\n"); +#ifdef __EMSCRIPTEN_WASM_WORKERS__ + emscripten_wasm_worker_t worker[2]; + worker[0] = emscripten_malloc_wasm_worker(/*stack size: */ 1024); + worker[1] = emscripten_malloc_wasm_worker(/*stack size: */ 1024); + emscripten_wasm_worker_post_function_v(worker[0], thread_func); + emscripten_wasm_worker_post_function_v(worker[1], thread_func); + + // Terminate both workers after a small delay + emscripten_set_timeout(terminate_worker, 1000, 0); +#else + pthread_t thread[2]; void *thread_rtn; int rc; @@ -49,7 +77,7 @@ int main() { rc = pthread_join(thread[1], &thread_rtn); assert(rc == 0); assert(thread_rtn == 0); - printf("main done\n"); +#endif return 0; } diff --git a/test/runner.py b/test/runner.py index e93ca3aeafbff..db91a8529c94e 100755 --- a/test/runner.py +++ b/test/runner.py @@ -107,6 +107,7 @@ # picked from here, but you can force them to be, using something like # randombrowser10 (which runs 10 random tests from 'browser'). misc_test_modes = [ + 'codesize', 'other', 'jslib', 'browser', diff --git a/test/test_core.py b/test/test_core.py index f129b2223a7f5..b84dfd72d634e 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -9258,8 +9258,13 @@ def test_emscripten_futexes(self): @requires_pthreads def test_stdio_locking(self): - self.set_setting('PTHREAD_POOL_SIZE', '2') - self.do_core_test('test_stdio_locking.c') + self.do_core_test('test_stdio_locking.c', cflags=['-sPTHREAD_POOL_SIZE=2']) + + @no_esm_integration('WASM_ESM_INTEGRATION is not compatible with WASM_WORKERS') + def test_stdio_locking_ww(self): + # Note: do not combine with test_stdio_locking above because we want to test standalone + # wasm workers here and `@requires_pthreads` would prevent that. + self.do_core_test('test_stdio_locking.c', cflags=['-sWASM_WORKERS']) @with_dylink_reversed @requires_pthreads diff --git a/tools/system_libs.py b/tools/system_libs.py index f669f63ac471c..5073e0614f3f5 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -916,6 +916,8 @@ class MuslInternalLibrary(Library): '-std=c99', '-D_XOPEN_SOURCE=700', '-Wno-unused-result', # system call results are often ignored in musl, and in wasi that warns + '-Wno-bitwise-op-parentheses', + '-Wno-shift-op-parentheses', ] @@ -1068,11 +1070,9 @@ class libc(MuslInternalLibrary, cflags += ['-Wno-ignored-attributes', # tre.h defines NDEBUG internally itself '-Wno-macro-redefined', - '-Wno-shift-op-parentheses', '-Wno-string-plus-int', '-Wno-missing-braces', '-Wno-logical-op-parentheses', - '-Wno-bitwise-op-parentheses', '-Wno-unused-but-set-variable', '-Wno-unused-variable', '-Wno-unused-label', @@ -1503,7 +1503,7 @@ def can_use(self): return super().can_use() and settings.PRINTF_LONG_DOUBLE -class libwasm_workers(DebugLibrary): +class libwasm_workers(MuslInternalLibrary, DebugLibrary): name = 'libwasm_workers' includes = ['system/lib/libc'] src_dir = 'system/lib/wasm_worker'