diff --git a/ChangeLog.md b/ChangeLog.md index 42a72c453b7d2..ff6e418be0f92 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -21,6 +21,8 @@ Current Trunk - LLVM backend pthread builds no longer use external memory initialization files, replacing them with passive data segments. + - LLVM backend now supports thread local storage via the C extension `__thread` + and C11/C++11 keyword `thread_local`. (#8976) v1.38.39: 07/16/2019 -------------------- diff --git a/src/worker.js b/src/worker.js index 643a225fa6ab2..3a79f5ab89a1f 100644 --- a/src/worker.js +++ b/src/worker.js @@ -207,6 +207,9 @@ this.onmessage = function(e) { // Also call inside JS module to set up the stack frame for this pthread in JS module scope Module['establishStackSpaceInJsModule'](e.data.stackBase, e.data.stackBase + e.data.stackSize); #endif +#if WASM_BACKEND + Module['_emscripten_tls_init'](); +#endif #if STACK_OVERFLOW_CHECK {{{ makeAsmGlobalAccessInPthread('writeStackCookie') }}}(); #endif diff --git a/system/lib/libc/musl/src/misc/emscripten_pthread.c b/system/lib/libc/musl/src/misc/emscripten_pthread.c index ed8f8c0b9b3be..5bff59452e700 100644 --- a/system/lib/libc/musl/src/misc/emscripten_pthread.c +++ b/system/lib/libc/musl/src/misc/emscripten_pthread.c @@ -25,7 +25,7 @@ EM_JS(void, initPthreadsJS, (void), { }) EMSCRIPTEN_KEEPALIVE -__attribute__((constructor(100))) // This must run before any userland ctors +__attribute__((constructor(99))) // This must run before any userland ctors void __emscripten_pthread_data_constructor(void) { initPthreadsJS(); pthread_self()->locale = &libc.global_locale; diff --git a/system/lib/pthread/library_pthread_wasm.c b/system/lib/pthread/library_pthread_wasm.c index 79ada9ec8de1b..ecf89d59ba68e 100644 --- a/system/lib/pthread/library_pthread_wasm.c +++ b/system/lib/pthread/library_pthread_wasm.c @@ -168,3 +168,17 @@ uint32_t emscripten_atomic_xor_u32(void /*uint32_t*/* addr, uint32_t val) { uint64_t emscripten_atomic_xor_u64(void /*uint64_t*/* addr, uint64_t val) { return __c11_atomic_fetch_xor((_Atomic uint64_t*)addr, val, __ATOMIC_SEQ_CST); } + +extern void __wasm_init_tls(void *memory); +void *emscripten_builtin_malloc(size_t size); +void emscripten_builtin_free(void *memory); + +__attribute__((constructor(100))) +void EMSCRIPTEN_KEEPALIVE emscripten_tls_init(void) { + size_t tls_size = __builtin_wasm_tls_size(); + if (tls_size) { + void *tls_block = emscripten_builtin_malloc(tls_size); + __wasm_init_tls(tls_block); + pthread_cleanup_push(emscripten_builtin_free, tls_block); + } +} diff --git a/tests/pthread/test_pthread_tls.cpp b/tests/pthread/test_pthread_tls.cpp new file mode 100644 index 0000000000000..1acf666227638 --- /dev/null +++ b/tests/pthread/test_pthread_tls.cpp @@ -0,0 +1,32 @@ +#include +#include + +thread_local int tls; +thread_local struct { + int a; + double b; +} data = {1, 2}; +thread_local int array[10]; + +void thread(void) { + ++tls; + data.a = 3; + data.b = 4; + assert(tls == 1); + assert(data.a == 3); + assert(data.b == 4); + assert(array[9] == 0); +} + +int main(void) { + array[9] = 1337; + std::thread t(thread); + t.join(); + assert(tls == 0); + assert(data.a == 1); + assert(data.b == 2); + assert(array[9] == 1337); +#ifdef REPORT_RESULT + REPORT_RESULT(1337); +#endif +} diff --git a/tests/pthread/test_pthread_tls_main.cpp b/tests/pthread/test_pthread_tls_main.cpp new file mode 100644 index 0000000000000..a78e42f7ecf39 --- /dev/null +++ b/tests/pthread/test_pthread_tls_main.cpp @@ -0,0 +1,16 @@ +#include +#include + +thread_local int tls = 1330; +thread_local int tls2; + +__attribute__((constructor)) +void init_tls2(void) { + tls2 = 7; +} + +int main(void) { +#ifdef REPORT_RESULT + REPORT_RESULT(tls + tls2); +#endif +} diff --git a/tests/test_browser.py b/tests/test_browser.py index 7e42429e3107b..e9b4f38276be4 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -3892,6 +3892,18 @@ def test_pthread_utf8_funcs(self): def test_pthread_wake_all(self): self.btest(path_from_root('tests', 'pthread', 'test_futex_wake_all.cpp'), expected='0', args=['-O3', '-s', 'USE_PTHREADS=1', '-s', 'TOTAL_MEMORY=64MB', '-s', 'NO_EXIT_RUNTIME=1'], also_asmjs=True) + # Test that real `thread_local` works. + @no_fastcomp('thread_local is only supported on WASM backend') + @requires_threads + def test_pthread_tls(self): + self.btest(path_from_root('tests', 'pthread', 'test_pthread_tls.cpp'), expected='1337', args=['-s', 'PROXY_TO_PTHREAD', '-s', 'USE_PTHREADS', '-std=c++11']) + + # Test that real `thread_local` works in main thread without PROXY_TO_PTHREAD. + @no_fastcomp('thread_local is only supported on WASM backend') + @requires_threads + def test_pthread_tls_main(self): + self.btest(path_from_root('tests', 'pthread', 'test_pthread_tls_main.cpp'), expected='1337', args=['-s', 'USE_PTHREADS', '-std=c++11']) + # Tests MAIN_THREAD_EM_ASM_INT() function call signatures. @no_wasm_backend('MAIN_THREAD_EM_ASM() not yet implemented in Wasm backend') def test_main_thread_em_asm_signatures(self):