Skip to content

Commit 8690b9a

Browse files
committed
Don't use wasm global data sections when compiling with multithreading enabled so that pthread_create() will not reset the global data members. Instead use a separate .mem file or base64-embedded data in JS file for memory initialization. See WebAssembly/threads#62
1 parent a1cf520 commit 8690b9a

File tree

3 files changed

+47
-2
lines changed

3 files changed

+47
-2
lines changed

emcc.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1666,7 +1666,7 @@ def repl(m):
16661666
if DEBUG:
16671667
# Copy into temp dir as well, so can be run there too
16681668
shared.safe_copy(memfile, os.path.join(shared.get_emscripten_temp_dir(), os.path.basename(memfile)))
1669-
if not shared.Settings.BINARYEN or 'asmjs' in shared.Settings.BINARYEN_METHOD or 'interpret-asm2wasm' in shared.Settings.BINARYEN_METHOD:
1669+
if not shared.Settings.BINARYEN or 'asmjs' in shared.Settings.BINARYEN_METHOD or 'interpret-asm2wasm' in shared.Settings.BINARYEN_METHOD or shared.Settings.USE_PTHREADS:
16701670
return 'memoryInitializer = "%s";' % shared.JS.get_subresource_location(memfile, embed_memfile(options))
16711671
else:
16721672
return 'memoryInitializer = null;'
@@ -2258,9 +2258,12 @@ def do_binaryen(final, target, asm_target, options, memfile, wasm_binary_target,
22582258
if options.opt_level > 0:
22592259
cmd.append(shared.Building.opt_level_to_str(options.opt_level, options.shrink_level))
22602260
# import mem init file if it exists, and if we will not be using asm.js as a binaryen method (as it needs the mem init file, of course)
2261+
# Note that importing (embedding) memory init file into the .wasm module as a data section is not compatible with multithreading in WebAssembly,
2262+
# because each thread would reinitialize the global data section at thread creation time, so only embed a data section to the generated
2263+
# .wasm file if not using multithreading
22612264
mem_file_exists = options.memory_init_file and os.path.exists(memfile)
22622265
ok_binaryen_method = 'asmjs' not in shared.Settings.BINARYEN_METHOD and 'interpret-asm2wasm' not in shared.Settings.BINARYEN_METHOD
2263-
import_mem_init = mem_file_exists and ok_binaryen_method
2266+
import_mem_init = mem_file_exists and ok_binaryen_method and not shared.Settings.USE_PTHREADS
22642267
if import_mem_init:
22652268
cmd += ['--mem-init=' + memfile]
22662269
if not shared.Settings.RELOCATABLE:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include <assert.h>
2+
#include <pthread.h>
3+
#include <emscripten.h>
4+
5+
int globalData = 1;
6+
7+
void *thread_main(void *arg)
8+
{
9+
EM_ASM(Module.print('hello from pthread 1: ' + $0), globalData);
10+
assert(globalData == 10);
11+
12+
globalData = 20;
13+
EM_ASM(Module.print('hello from pthread 2: ' + $0), globalData);
14+
assert(globalData == 20);
15+
return 0;
16+
}
17+
18+
int main()
19+
{
20+
EM_ASM(Module.print('hello from main 1: ' + $0), globalData);
21+
assert(globalData == 1);
22+
23+
globalData = 10;
24+
EM_ASM(Module.print('hello from main 2: ' + $0), globalData);
25+
assert(globalData == 10);
26+
27+
pthread_t thread;
28+
pthread_create(&thread, NULL, thread_main, NULL);
29+
pthread_join(thread, 0);
30+
31+
EM_ASM(Module.print('hello from main 3: ' + $0), globalData);
32+
assert(globalData == 20);
33+
#ifdef REPORT_RESULT
34+
REPORT_RESULT(globalData);
35+
#endif
36+
}

tests/test_browser.py

+6
Original file line numberDiff line numberDiff line change
@@ -3370,6 +3370,12 @@ def test_pthread_call_async_on_main_thread(self):
33703370
self.btest_wasm(path_from_root('tests', 'pthread', 'call_async_on_main_thread.c'), expected='7', args=['-O3', '-s', 'USE_PTHREADS=1', '-DPROXY_TO_PTHREAD=0', '--js-library', path_from_root('tests', 'pthread', 'call_async_on_main_thread.js')])
33713371
self.btest_wasm(path_from_root('tests', 'pthread', 'call_async_on_main_thread.c'), expected='7', args=['-Oz', '-DPROXY_TO_PTHREAD=0', '--js-library', path_from_root('tests', 'pthread', 'call_async_on_main_thread.js')])
33723372

3373+
# Tests that spawning a new thread does not cause a reinitialization of the global data section of the application memory area.
3374+
def test_pthread_global_data_initialization(self):
3375+
for mem_init_mode in [[], ['--memory-init-file', '0'], ['--memory-init-file', '1'], ['--memory-init-file', '2']]:
3376+
for args in [[], ['-O3']]:
3377+
self.btest_wasm(path_from_root('tests', 'pthread', 'test_pthread_global_data_initialization.c'), expected='20', args=args+mem_init_mode+['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1'])
3378+
33733379
# test atomicrmw i64
33743380
def test_atomicrmw_i64(self):
33753381
Popen([PYTHON, EMCC, path_from_root('tests', 'atomicrmw_i64.ll'), '-s', 'USE_PTHREADS=1', '-s', 'IN_TEST_HARNESS=1', '-o', 'test.html']).communicate()

0 commit comments

Comments
 (0)