Skip to content

Commit a21a37b

Browse files
committed
Remove WASM_WORKERS_NO_TLS option, and simplify Wasm Worker TLS creation by hiding it as an implementation detail - TLS block will be grafted onto the memory area set for the thread stack. Also fix MINIMAL_RUNTIME+WASM_WORKERS+WASM=0 build combination and add a test for that.
1 parent 024641e commit a21a37b

33 files changed

+91
-133
lines changed

site/source/docs/api_reference/wasm_workers.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ which shares the same WebAssembly.Module and WebAssembly.Memory object. Then a
3333
``postMessage()`` is passed to the Worker to ask it to execute the function
3434
``run_in_worker()`` to print a string.
3535

36-
For more complex threading scenarios, see the functions ``emscripten_create_wasm_worker_with_tls()``
37-
and ``emscripten_create_wasm_worker_no_tls()`` on how to manage the memory resources that
38-
the thread will use.
36+
To explicitly control the memory allocation placement when creating a worker, use the
37+
function ``emscripten_create_wasm_worker()``.
3938

4039
Introduction
4140
============

src/library_wasm_worker.js

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,7 @@ mergeInto(LibraryManager.library, {
5656
#endif
5757

5858
// Run the C side Worker initialization for stack and TLS.
59-
_emscripten_wasm_worker_initialize(m['sb'], m['sz']
60-
#if !WASM_WORKERS_NO_TLS
61-
, m['tb']
62-
#endif
63-
);
59+
_emscripten_wasm_worker_initialize(m['sb'], m['sz']);
6460
// The above function initializes the stack for this Worker, but C code cannot
6561
// call to extern __set_stack_limits() function, or Binaryen breaks with
6662
// "Fatal: Module::addFunction: __set_stack_limits already exists".
@@ -87,20 +83,16 @@ mergeInto(LibraryManager.library, {
8783
_wasmWorkerBlobUrl: "URL.createObjectURL(new Blob(['onmessage=function(d){onmessage=null;d=d.data;{{{ captureModuleArg() }}}{{{ instantiateWasm() }}}importScripts(d.js);{{{ instantiateModule() }}}d.wasm=d.mem=d.js=0;}'],{type:'application/javascript'}))",
8884
#endif
8985

90-
_emscripten_create_wasm_worker_with_tls__deps: ['wasm_workers', 'wasm_workers_id', '_wasm_worker_appendToQueue', '_wasm_worker_runPostMessage'
86+
_emscripten_create_wasm_worker__deps: ['wasm_workers', 'wasm_workers_id', '_wasm_worker_appendToQueue', '_wasm_worker_runPostMessage'
9187
#if WASM_WORKERS == 2
9288
, '_wasmWorkerBlobUrl'
9389
#endif
9490
],
95-
_emscripten_create_wasm_worker_with_tls__postset: 'if (ENVIRONMENT_IS_WASM_WORKER) {\n'
91+
_emscripten_create_wasm_worker__postset: 'if (ENVIRONMENT_IS_WASM_WORKER) {\n'
9692
+ '_wasm_workers[0] = this;\n'
9793
+ 'addEventListener("message", __wasm_worker_appendToQueue);\n'
9894
+ '}\n',
99-
_emscripten_create_wasm_worker_with_tls: function(stackLowestAddress, stackSize, tlsAddress) {
100-
#if ASSERTIONS
101-
assert(stackLowestAddress % 16 == 0);
102-
assert(stackSize % 16 == 0);
103-
#endif
95+
_emscripten_create_wasm_worker: function(stackLowestAddress, stackSize) {
10496
let worker = _wasm_workers[_wasm_workers_id] = new Worker(
10597
#if WASM_WORKERS == 2 // WASM_WORKERS=2 mode embeds .ww.js file contents into the main .js file as a Blob URL. (convenient, but not CSP security safe, since this is eval-like)
10698
__wasmWorkerBlobUrl
@@ -122,19 +114,12 @@ mergeInto(LibraryManager.library, {
122114
'js': Module['mainScriptUrlOrBlob'] || _scriptDir,
123115
'wasmMemory': wasmMemory,
124116
#endif
125-
'sb': stackLowestAddress, // sb = stack base
117+
'sb': stackLowestAddress, // sb = stack bottom (lowest stack address, SP points at this when stack is full)
126118
'sz': stackSize, // sz = stack size
127-
#if !WASM_WORKERS_NO_TLS
128-
'tb': tlsAddress, // tb = TLS base
129-
#endif
130119
});
131120
worker.addEventListener('message', __wasm_worker_runPostMessage);
132121
return _wasm_workers_id++;
133122
},
134-
_emscripten_create_wasm_worker_no_tls__deps: ['_emscripten_create_wasm_worker_with_tls'],
135-
_emscripten_create_wasm_worker_no_tls: function(stackLowestAddress, stackSize) {
136-
return __emscripten_create_wasm_worker_with_tls(stackLowestAddress, stackSize, 0, 0);
137-
},
138123

139124
emscripten_terminate_wasm_worker: function(id) {
140125
#if ASSERTIONS

src/settings.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,12 +1480,6 @@ var USE_PTHREADS = 0;
14801480
// [compile+link] - affects user code at compile and system libraries at link.
14811481
var WASM_WORKERS = 0;
14821482

1483-
// Wasm Worker option: set to 1 to disable TLS for small code size gain when
1484-
// not using TLS. This setting can be used to verify that there is no leftover
1485-
// state stored in a Worker when manually implementing thread pooling in Workers.
1486-
// [link]
1487-
var WASM_WORKERS_NO_TLS = 0;
1488-
14891483
// In web browsers, Workers cannot be created while the main browser thread
14901484
// is executing JS/Wasm code, but the main thread must regularly yield back
14911485
// to the browser event loop for Worker initialization to occur.

system/include/emscripten/wasm_worker.h

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,16 @@ extern "C" {
1717
behaves, the dynamically allocated memory can never be freed, so use this function
1818
only in scenarios where the page does not need to deinitialize/tear itself down.
1919
20-
emscripten_create_wasm_worker_no_tls: Creates a Wasm Worker on the given placed stack address area, but no TLS.
21-
Use this function on codebase that explicitly do not have any TLS state,
22-
e.g. when the Worker is being reset to reinit execution at runtime, or to micro-
23-
optimize code size when TLS is not needed. This function will assert() fail if
24-
the compiled code does have any TLS uses in it. This function does not use any
25-
dynamic memory allocation.
26-
27-
emscripten_create_wasm_worker_with_tls: Creates a Wasm Worker on given placed stack address and TLS area.
28-
Use the Wasm built-in functions __builtin_wasm_tls_align() and
29-
__builtin_wasm_tls_size() to obtain the needed memory size for the TLS area
30-
(though note that __builtin_wasm_tls_align() can return zero when the TLS size
31-
is zero, so be careful to avoid a divide by zero if/when rounding to this alignment)
32-
Use this function to manually manage the memory that a Worker should use.
33-
This function does not use any dynamic memory allocation.
20+
emscripten_create_wasm_worker: Creates a Wasm Worker on given placed stack address.
21+
Use this function to manually manage the memory that a Worker should use.
22+
This function does not use any dynamic memory allocation.
3423
3524
Returns an ID that represents the given Worker. If not building with Wasm workers enabled (-s WASM_WORKERS=0),
3625
these functions will return 0 to denote failure.
3726
Note that the Worker will be loaded up asynchronously, and initially will not be executing any code. Use
3827
emscripten_wasm_worker_post_function_*() set of functions to start executing code on the Worker. */
3928
emscripten_wasm_worker_t emscripten_malloc_wasm_worker(uint32_t stackSize);
40-
emscripten_wasm_worker_t emscripten_create_wasm_worker_no_tls(void *stackLowestAddress, uint32_t stackSize);
41-
emscripten_wasm_worker_t emscripten_create_wasm_worker_with_tls(void *stackLowestAddress, uint32_t stackSize, void *tlsAddress, uint32_t tlsSize);
29+
emscripten_wasm_worker_t emscripten_create_wasm_worker(void *stackLowestAddress, uint32_t stackSize);
4230

4331
// Terminates the given Wasm Worker some time after it has finished executing its current, or possibly some subsequent
4432
// posted functions. Note that this function is not C++ RAII safe, but you must manually coordinate to release any

system/lib/compiler-rt/stack_limits.S

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ emscripten_stack_get_free:
8282
# __stack_base and __stack_end globals from a separate file as externs in order for that to work.
8383
.globl emscripten_wasm_worker_initialize
8484
emscripten_wasm_worker_initialize:
85-
.functype emscripten_wasm_worker_initialize (PTR, i32, PTR) -> ()
85+
.functype emscripten_wasm_worker_initialize (PTR /*stackLowestAddress*/, i32 /*stackSize*/) -> ()
8686

87+
// __stack_end = stackLowestAddress;
8788
local.get 0
8889
global.set __stack_end
8990

91+
// __stack_base = stackLowestAddress + stackSize;
9092
local.get 0
9193
local.get 1
9294
#ifdef __wasm64__
@@ -96,13 +98,23 @@ emscripten_wasm_worker_initialize:
9698
local.tee 0
9799
global.set __stack_base
98100

101+
// __stack_pointer = __stack_base;
99102
local.get 0
100103
global.set __stack_pointer
101104

102-
local.get 2
103-
.functype __wasm_init_tls (PTR) -> ()
104-
call __wasm_init_tls
105+
// __wasm_init_tls(__stack_end);
106+
// __stack_end += __builtin_wasm_tls_size();
107+
global.get __stack_end
108+
global.get __stack_end
109+
.functype wasm_worker_init_tls (PTR) -> (i32)
110+
call wasm_worker_init_tls
111+
#ifdef __wasm64__
112+
i64.extend_i32_u
113+
#endif
114+
PTR.add
115+
global.set __stack_end
105116

117+
i32.const 0
106118
end_function
107119
#endif
108120

system/lib/wasm_worker/library_wasm_worker.c

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,68 +2,63 @@
22
#include <emscripten/wasm_worker.h>
33
#include <emscripten/threading.h>
44
#include <emscripten/heap.h>
5+
#include <emscripten/stack.h>
56
#include <malloc.h>
67

78
#ifndef __EMSCRIPTEN_WASM_WORKERS__
89
#error __EMSCRIPTEN_WASM_WORKERS__ should be defined when building this file!
910
#endif
1011

1112
// Options:
12-
// #define WASM_WORKER_NO_TLS 0/1 : set to 1 to disable TLS compilation support for a small code size gain
1313
// #define STACK_OVERFLOW_CHECK 0/1/2 : set to the current stack overflow check mode
1414

1515
// Internal implementation function in JavaScript side that emscripten_create_wasm_worker() calls to
1616
// to perform the wasm worker creation.
17-
emscripten_wasm_worker_t _emscripten_create_wasm_worker_with_tls(void *stackLowestAddress, uint32_t stackSize, void *tlsAddress);
18-
emscripten_wasm_worker_t _emscripten_create_wasm_worker_no_tls(void *stackLowestAddress, uint32_t stackSize);
17+
emscripten_wasm_worker_t _emscripten_create_wasm_worker(void *stackLowestAddress, uint32_t stackSize);
1918

2019
void __wasm_init_tls(void *memory);
2120

22-
#if !WASM_WORKER_NO_TLS
2321
__attribute__((constructor(48)))
2422
static void emscripten_wasm_worker_main_thread_initialize() {
2523
uintptr_t* sbrk_ptr = emscripten_get_sbrk_ptr();
2624
__wasm_init_tls((void*)*sbrk_ptr);
2725
*sbrk_ptr += __builtin_wasm_tls_size();
2826
}
29-
#endif
3027

31-
emscripten_wasm_worker_t emscripten_create_wasm_worker_with_tls(void *stackLowestAddress, uint32_t stackSize, void *tlsAddress, uint32_t tlsSize)
32-
{
33-
#if WASM_WORKER_NO_TLS
34-
return emscripten_create_wasm_worker_no_tls(stackLowestAddress, stackSize);
35-
#else
36-
assert((uintptr_t)stackLowestAddress % 16 == 0);
37-
assert(stackSize % 16 == 0);
38-
assert(__builtin_wasm_tls_align() != 0 || __builtin_wasm_tls_size() == 0); // Internal consistency check: Clang can report __builtin_wasm_tls_align to be zero if there is no TLS used - double check it never reports zero if it is used.
39-
assert(__builtin_wasm_tls_align() == 0 || (uintptr_t)tlsAddress % __builtin_wasm_tls_align() == 0 && "TLS memory address not aligned in a call to emscripten_create_wasm_worker_with_tls()! Please allocate memory with alignment from __builtin_wasm_tls_align() when creating a Wasm Worker!");
40-
assert(tlsSize != 0 || __builtin_wasm_tls_size() == 0 && "Program code contains TLS: please use function emscripten_create_wasm_worker_with_tls() to create a Wasm Worker!");
41-
assert(tlsSize == __builtin_wasm_tls_size() && "TLS size mismatch! Please reserve exactly __builtin_wasm_tls_size() TLS memory in a call to emscripten_create_wasm_worker_with_tls()");
42-
assert(tlsAddress != 0 || tlsSize == 0);
43-
return _emscripten_create_wasm_worker_with_tls((void*)stackLowestAddress, stackSize, tlsAddress);
44-
#endif
28+
// TODO: If it is possible to directly access __builtin_wasm_tls_size() and __builtin_wasm_tls_align()
29+
// from system/lib/compiler-rt/stack_limits.S, then this function can be merged into there.
30+
uint32_t wasm_worker_init_tls(void *stackLowestAddress) {
31+
size_t alignedTlsSize = (__builtin_wasm_tls_size() + 15) & -16;
32+
assert(__builtin_wasm_tls_align() == 0 || (uintptr_t)stackLowestAddress % __builtin_wasm_tls_align() == 0);
33+
__wasm_init_tls(stackLowestAddress);
34+
return alignedTlsSize;
4535
}
4636

47-
emscripten_wasm_worker_t emscripten_create_wasm_worker_no_tls(void *stackLowestAddress, uint32_t stackSize)
37+
emscripten_wasm_worker_t emscripten_create_wasm_worker(void *stackLowestAddress, uint32_t stackSize)
4838
{
49-
#if !WASM_WORKER_NO_TLS
50-
return emscripten_create_wasm_worker_with_tls(stackLowestAddress, stackSize, 0, 0);
51-
#else
39+
assert(stackLowestAddress != 0);
5240
assert((uintptr_t)stackLowestAddress % 16 == 0);
41+
assert(stackSize > 0);
5342
assert(stackSize % 16 == 0);
54-
assert(__builtin_wasm_tls_size() == 0 && "Cannot disable TLS with -sWASM_WORKERS_NO_TLS=1 when compiling code that does require TLS! Rebuild with -sWASM_WORKERS_NO_TLS=1 removed, or remove uses of TLS from the codebase.");
55-
return _emscripten_create_wasm_worker_no_tls((void*)stackLowestAddress, stackSize);
56-
#endif
43+
44+
// Guard against a programming oopsie: The target Worker's stack cannot be part of the calling
45+
// thread's stack.
46+
assert(emscripten_stack_get_base() <= stackLowestAddress || emscripten_stack_get_end() >= stackLowestAddress + stackSize
47+
&& "When creating a Wasm Worker, its stack should be located either in global data or on the heap, not on the calling thread's own stack!");
48+
49+
// The Worker's TLS area will be spliced off from the stack region.
50+
// We expect TLS area to need to be at most 16 bytes aligned
51+
assert(__builtin_wasm_tls_align() == 0 || 16 % __builtin_wasm_tls_align() == 0);
52+
53+
uint32_t tlsSize = (__builtin_wasm_tls_size() + 15) & -16;
54+
assert(stackSize > tlsSize);
55+
56+
return _emscripten_create_wasm_worker(stackLowestAddress, stackSize);
5757
}
5858

5959
emscripten_wasm_worker_t emscripten_malloc_wasm_worker(uint32_t stackSize)
6060
{
61-
#if WASM_WORKER_NO_TLS
62-
return emscripten_create_wasm_worker_no_tls(memalign(16, stackSize), stackSize);
63-
#else
64-
uint32_t tlsSize = __builtin_wasm_tls_size();
65-
return emscripten_create_wasm_worker_with_tls(memalign(16, stackSize), stackSize, tlsSize ? memalign(__builtin_wasm_tls_align(), tlsSize) : 0, tlsSize);
66-
#endif
61+
return emscripten_create_wasm_worker(memalign(16, stackSize), stackSize);
6762
}
6863

6964
void emscripten_wasm_worker_sleep(int64_t nsecs)

system/lib/wasm_worker/library_wasm_worker_stub.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@
88
#error __EMSCRIPTEN_WASM_WORKERS__ should not be defined when building this file!
99
#endif
1010

11-
emscripten_wasm_worker_t emscripten_create_wasm_worker_with_tls(void *stackLowestAddress, uint32_t stackSize, void *tlsAddress, uint32_t tlsSize) {
12-
return 0;
13-
}
14-
15-
emscripten_wasm_worker_t emscripten_create_wasm_worker_no_tls(void *stackLowestAddress, uint32_t stackSize) {
11+
emscripten_wasm_worker_t emscripten_create_wasm_worker(void *stackLowestAddress, uint32_t stackSize) {
1612
return 0;
1713
}
1814

tests/test_browser.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5088,6 +5088,11 @@ def test_system(self):
50885088
def test_wasm_worker_hello(self):
50895089
self.btest(test_file('wasm_worker/hello_wasm_worker.c'), expected='0', args=['-sWASM_WORKERS'])
50905090

5091+
# Tests the hello_wasm_worker.c documentation example code.
5092+
@also_with_minimal_runtime
5093+
def test_wasm_worker_hello_wasm2js(self):
5094+
self.btest(test_file('wasm_worker/hello_wasm_worker.c'), expected='0', args=['-sWASM_WORKERS', '-sWASM=0'])
5095+
50915096
# Tests the WASM_WORKERS=2 build mode, which embeds the Wasm Worker bootstrap JS script file to the main JS file.
50925097
@also_with_minimal_runtime
50935098
def test_wasm_worker_embedded(self):
@@ -5098,11 +5103,6 @@ def test_wasm_worker_embedded(self):
50985103
def test_wasm_worker_thread_stack(self):
50995104
self.btest(test_file('wasm_worker/thread_stack.c'), expected='0', args=['-sWASM_WORKERS'])
51005105

5101-
# Tests Wasm Worker thread stack setup without TLS support active
5102-
@also_with_minimal_runtime
5103-
def test_wasm_worker_thread_stack_no_tls(self):
5104-
self.btest(test_file('wasm_worker/thread_stack.c'), expected='0', args=['-sWASM_WORKERS', '-sWASM_WORKERS_NO_TLS'])
5105-
51065106
# Tests emscripten_malloc_wasm_worker() and emscripten_current_thread_is_wasm_worker() functions
51075107
@also_with_minimal_runtime
51085108
def test_wasm_worker_malloc(self):

tests/wasm_worker/c11__Thread_local.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,12 @@ void worker_main()
2525
}
2626

2727
char stack[1024];
28-
char tlsBase[1024];
2928

3029
int main()
3130
{
3231
EM_ASM(console.log($0), tls);
3332
assert(!emscripten_current_thread_is_wasm_worker());
3433
tls = 42;
35-
emscripten_wasm_worker_t worker = emscripten_create_wasm_worker_with_tls(stack, sizeof(stack), tlsBase, __builtin_wasm_tls_size());
34+
emscripten_wasm_worker_t worker = emscripten_create_wasm_worker(stack, sizeof(stack));
3635
emscripten_wasm_worker_post_function_v(worker, worker_main);
3736
}

tests/wasm_worker/cpp11_thread_local.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,12 @@ void worker_main()
2424
}
2525

2626
char stack[1024];
27-
char tlsBase[1024];
2827

2928
int main()
3029
{
3130
EM_ASM(console.log($0), tls);
3231
assert(!emscripten_current_thread_is_wasm_worker());
3332
tls = 42;
34-
emscripten_wasm_worker_t worker = emscripten_create_wasm_worker_with_tls(stack, sizeof(stack), tlsBase, __builtin_wasm_tls_size());
33+
emscripten_wasm_worker_t worker = emscripten_create_wasm_worker(stack, sizeof(stack));
3534
emscripten_wasm_worker_post_function_v(worker, worker_main);
3635
}

0 commit comments

Comments
 (0)