Skip to content

[wasm64] Fix and run all pthread browser tests #20527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 26, 2023
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
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,7 @@ jobs:
browser64.test_fetch_xhr_abort
browser64.test_fetch_sync_xhr
browser64.test_fetch_implicit_append
browser64.test_pthread_*
"
test-browser-firefox:
executor: bionic
Expand Down
5 changes: 4 additions & 1 deletion src/jsifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,13 @@ function(${args}) {
if (oneliner) {
body = `return ${body}`;
}
const rtnType = sig && sig.length ? sig[0] : null;
const proxyFunc = (MEMORY64 && rtnType == 'p') ? 'proxyToMainThreadPtr' : 'proxyToMainThread';
deps.push('$' + proxyFunc);
return `
function(${args}) {
if (ENVIRONMENT_IS_PTHREAD)
return proxyToMainThread(${proxiedFunctionTable.length}, ${+sync}${args ? ', ' : ''}${args});
return ${proxyFunc}(${proxiedFunctionTable.length}, ${+sync}${args ? ', ' : ''}${args});
${body}
}\n`
});
Expand Down
23 changes: 22 additions & 1 deletion src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,15 @@ var LibraryPThread = {
_exit(returnCode);
},
$proxyToMainThread__deps: ['$withStackSave', '_emscripten_run_on_main_thread_js'],
#if MEMORY64
// Calls proxyToMainThread but returns a bigint rather than a number
$proxyToMainThreadPtr__deps: ['$proxyToMainThread'],
$proxyToMainThreadPtr: function() {
return BigInt(proxyToMainThread.apply(null, arguments));
},
#endif
$proxyToMainThread__deps: ['$withStackSave', '_emscripten_run_on_main_thread_js'].concat(i53ConversionDeps),
$proxyToMainThread__docs: '/** @type{function(number, (number|boolean), ...(number|boolean))} */',
$proxyToMainThread: function(index, sync) {
// Additional arguments are passed after those two, which are the actual
Expand Down Expand Up @@ -1032,6 +1040,19 @@ var LibraryPThread = {
PThread.currentProxiedOperationCallerThread = callingThread;
var rtn = func.apply(null, proxiedJSCallArgs);
PThread.currentProxiedOperationCallerThread = 0;
#if MEMORY64
// In memory64 mode some proxied functions return bigint/pointer but
// our return type is i53/double.
if (typeof rtn == "bigint") {
rtn = bigintToI53Checked(rtn);
}
#endif
#if ASSERTIONS
// Proxied functions can return any type except bigint. All other types
// cooerce to f64/double (the return type of this function in C) but not
// bigint.
assert(typeof rtn != "bigint");
#endif
return rtn;
},
Expand Down
9 changes: 6 additions & 3 deletions src/preamble_minimal.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,15 @@ if (!ENVIRONMENT_IS_PTHREAD) {
Module['mem'] ||
#endif
new WebAssembly.Memory({
'initial': {{{ INITIAL_MEMORY >>> 16 }}}
'initial': {{{ INITIAL_MEMORY >>> 16 }}},
#if SHARED_MEMORY || !ALLOW_MEMORY_GROWTH || MAXIMUM_MEMORY != FOUR_GB
, 'maximum': {{{ (ALLOW_MEMORY_GROWTH && MAXIMUM_MEMORY != FOUR_GB ? MAXIMUM_MEMORY : INITIAL_MEMORY) >>> 16 }}}
'maximum': {{{ (ALLOW_MEMORY_GROWTH && MAXIMUM_MEMORY != FOUR_GB ? MAXIMUM_MEMORY : INITIAL_MEMORY) >>> 16 }}},
#endif
#if SHARED_MEMORY
, 'shared': true
'shared': true,
#endif
#if MEMORY64 == 1
'index': 'i64',
#endif
});
#if PTHREADS
Expand Down
11 changes: 11 additions & 0 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ def no_windows(note=''):
return lambda f: f


def no_wasm64(note=''):
assert not callable(note)

def decorated(f):
return skip_if(f, 'is_wasm64', note)
return decorated


def only_windows(note=''):
assert not callable(note)
if not WINDOWS:
Expand Down Expand Up @@ -574,6 +582,9 @@ def is_wasm(self):
def is_browser_test(self):
return False

def is_wasm64(self):
return self.get_setting('MEMORY64')

def check_dylink(self):
if self.get_setting('ALLOW_MEMORY_GROWTH') == 1 and not self.is_wasm():
self.skipTest('no dynamic linking with memory growth (without wasm)')
Expand Down
2 changes: 1 addition & 1 deletion test/pthread/call_sync_on_main_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ addToLibrary({
// This function returns the inner text of the given element
// Because it accesses the DOM, it must be called on the main thread.
getDomElementContents__proxy: 'sync',
getDomElementContents__sig: 'viii',
getDomElementContents__sig: 'pp',
getDomElementContents__deps: ['$stringToNewUTF8'],
getDomElementContents: function(domElementSelector) {
var selector = UTF8ToString(domElementSelector);
Expand Down
2 changes: 1 addition & 1 deletion test/pthread/test_futex_wake_all.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void *WaitingThread(void *arg)

int main()
{
for(int i = 0; i < NUM_THREADS; ++i)
for(intptr_t i = 0; i < NUM_THREADS; ++i)
Copy link
Member

Choose a reason for hiding this comment

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

Surely the number of threads fits in an int? 😄

lgtm regardless, but I'm curious if this fixed a specific issue? Maybe that the higher bits were undefined?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The specific issue here and elsewhere is that i is cast to a void* and passed to pthread_create as the argument to the thread start function.

Casting an int to void* is find on wasm32 but generates a warning on wasm64.

Copy link
Member

Choose a reason for hiding this comment

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

I see, thanks.

{
pthread_create(waitingThreads+i, 0, WaitingThread, (void*)i);
}
Expand Down
2 changes: 1 addition & 1 deletion test/pthread/test_large_pthread_allocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static void *thread_start(void *arg)
pthread_exit((void*)0);
}

void CreateThread(int idx) {
void CreateThread(intptr_t idx) {
int rc = pthread_create(&threads[idx], NULL, thread_start, (void*)idx);
assert(rc == 0);
}
Expand Down
9 changes: 5 additions & 4 deletions test/pthread/test_pthread_barrier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ void *thread_main(void *arg)
}

// Then each thread sums the one intermediate vector.
int totalSum = 0;
intptr_t totalSum = 0;
for(int i = 0; i < N; ++i)
totalSum += intermediate[i];

Expand All @@ -70,9 +70,10 @@ int main(int argc, char **argv)
int ret = pthread_barrier_init(&barr, NULL, THREADS);
assert(ret == 0);

for(int i = 0; i < THREADS; ++i) pthread_create(&thr[i], NULL, &thread_main, (void*)i);
for(int i = 0; i < THREADS; ++i)
{
for(intptr_t i = 0; i < THREADS; ++i) {
pthread_create(&thr[i], NULL, &thread_main, (void*)i);
}
for(int i = 0; i < THREADS; ++i) {
int totalSum = 0;
pthread_join(thr[i], (void**)&totalSum);
assert(totalSum == expectedTotalSum);
Expand Down
2 changes: 1 addition & 1 deletion test/pthread/test_pthread_cancel_cond_wait.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ volatile int res = 43;

static void cleanup_handler(void *arg) {
emscripten_log(EM_LOG_CONSOLE, "Called clean-up handler with arg %p", arg);
int a = reinterpret_cast<int>(arg);
int a = reinterpret_cast<intptr_t>(arg);
res -= a;

pthread_mutex_unlock(&mutex);
Expand Down
8 changes: 4 additions & 4 deletions test/pthread/test_pthread_create.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,20 @@ void *ThreadMain(void *arg)
}
}
// Ensure all elements are in place.
int numGood = 0;
intptr_t numGood = 0;
for(unsigned int i = 0; i < N; ++i)
if (n[i] == i) ++numGood;
else emscripten_errf("n[%d]=%d", i, n[i]);

emscripten_outf("Thread idx %ld with param %d: all done with result %d.", idx, param, numGood);
emscripten_outf("Thread idx %ld with param %d: all done with result %ld.", idx, param, numGood);
pthread_exit((void*)numGood);
}

pthread_t thread[NUM_THREADS];

int numThreadsToCreate = 1000;

void CreateThread(int i)
void CreateThread(intptr_t i)
{
static int counter = 1;
global_shared_data[i] = (counter++ * 12141231) & 0x7FFFFFFF; // Arbitrary random'ish data for perturbing the sort for this thread task.
Expand All @@ -81,7 +81,7 @@ int main()
{
if (thread[i])
{
int status;
intptr_t status;
int rc = pthread_join(thread[i], (void**)&status);
assert(rc == 0);
emscripten_errf("Main: Joined thread idx %d (param %d) with status %d", i, global_shared_data[i], (int)status);
Expand Down
6 changes: 3 additions & 3 deletions test/pthread/test_pthread_gcc_64bit_atomic_fetch_and_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ int main()
for(int i = 0; i < NUM_THREADS; ++i)
{
threadArg[i] = DUP(1 << i);
pthread_create(&thread[i], NULL, thread_fetch_and_or, (void*)&threadArg[i]);
pthread_create(&thread[i], NULL, thread_fetch_and_or, &threadArg[i]);
}
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
assert(fetch_and_or_data == HILO(65536 + (1<<(NUM_THREADS+1))-1, (1<<(NUM_THREADS+1))-1));
Expand All @@ -129,7 +129,7 @@ int main()
fetch_and_and_data = HILO(65536 + (1<<(NUM_THREADS+1))-1, (1<<(NUM_THREADS+1))-1);
for(int i = 0; i < NUM_THREADS; ++i)
{
threadArg[i] = DUP(~(1UL<<i));
threadArg[i] = DUP(~(1u<<i));
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this change us from an unsigned long to plain unsigned? We seem to store this to uint64_t, so isn't the former better, if it makes any difference?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes. DUP() here is supposed to take an i32 and use HILO() to construct an i64 where the low and high parts are the same.

The current code which uses UL works for wasm32 because UL is i32 but on wasm64 UL is i64 which breaks DUP / HILO macros.

I verified this was broken on 64-bit linux too... and this was the fix there.

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense, thanks.

pthread_create(&thread[i], NULL, thread_fetch_and_and, (void*)&threadArg[i]);
}
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
Expand All @@ -149,7 +149,7 @@ int main()
fetch_and_xor_data = HILO(32768 + (1<<NUM_THREADS), 1<<NUM_THREADS);
for(int i = 0; i < NUM_THREADS; ++i)
{
threadArg[i] = DUP(~(1UL<<i));
threadArg[i] = DUP(~(1u<<i));
pthread_create(&thread[i], NULL, thread_fetch_and_xor, (void*)&threadArg[i]);
}
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
Expand Down
4 changes: 2 additions & 2 deletions test/pthread/test_pthread_gcc_64bit_atomic_op_and_fetch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ int main()
and_and_fetch_data = HILO(65536 + (1<<(NUM_THREADS+1))-1, (1<<(NUM_THREADS+1))-1);
for(int i = 0; i < NUM_THREADS; ++i)
{
threadArg[i] = DUP(~(1UL<<i));
threadArg[i] = DUP(~(1U<<i));
pthread_create(&thread[i], NULL, thread_and_and_fetch, (void*)&threadArg[i]);
}
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
Expand All @@ -149,7 +149,7 @@ int main()
xor_and_fetch_data = HILO(32768 + (1<<NUM_THREADS), 1<<NUM_THREADS);
for(int i = 0; i < NUM_THREADS; ++i)
{
threadArg[i] = DUP(~(1UL<<i));
threadArg[i] = DUP(~(1U<<i));
pthread_create(&thread[i], NULL, thread_xor_and_fetch, (void*)&threadArg[i]);
}
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
Expand Down
6 changes: 3 additions & 3 deletions test/pthread/test_pthread_gcc_atomic_fetch_and_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ int main()
fetch_and_or_data = (1<<NUM_THREADS);
if (emscripten_has_threading_support())
{
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_fetch_and_or, (void*)(1<<i));
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_fetch_and_or, (void*)(1ll<<i));
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
assert(fetch_and_or_data == (1<<(NUM_THREADS+1))-1);
}
Expand All @@ -119,7 +119,7 @@ int main()
fetch_and_and_data = (1<<(NUM_THREADS+1))-1;
if (emscripten_has_threading_support())
{
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_fetch_and_and, (void*)(~(1<<i)));
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_fetch_and_and, (void*)(~(1ll<<i)));
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
assert(fetch_and_and_data == 1<<NUM_THREADS);
}
Expand All @@ -135,7 +135,7 @@ int main()
fetch_and_xor_data = 1<<NUM_THREADS;
if (emscripten_has_threading_support())
{
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_fetch_and_xor, (void*)(~(1<<i)));
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_fetch_and_xor, (void*)(~(1ll<<i)));
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
assert(fetch_and_xor_data == (1<<(NUM_THREADS+1))-1);
}
Expand Down
6 changes: 3 additions & 3 deletions test/pthread/test_pthread_gcc_atomic_op_and_fetch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ int main()
or_and_fetch_data = (1<<NUM_THREADS);
if (emscripten_has_threading_support())
{
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_or_and_fetch, (void*)(1<<i));
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_or_and_fetch, (void*)(1ll<<i));
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
assert(or_and_fetch_data == (1<<(NUM_THREADS+1))-1);
}
Expand All @@ -118,7 +118,7 @@ int main()
and_and_fetch_data = (1<<(NUM_THREADS+1))-1;
if (emscripten_has_threading_support())
{
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_and_and_fetch, (void*)(~(1<<i)));
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_and_and_fetch, (void*)(~(1ll<<i)));
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
assert(and_and_fetch_data == 1<<NUM_THREADS);
}
Expand All @@ -134,7 +134,7 @@ int main()
xor_and_fetch_data = 1<<NUM_THREADS;
if (emscripten_has_threading_support())
{
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_xor_and_fetch, (void*)(~(1<<i)));
for(int i = 0; i < NUM_THREADS; ++i) pthread_create(&thread[i], NULL, thread_xor_and_fetch, (void*)(~(1ll<<i)));
for(int i = 0; i < NUM_THREADS; ++i) pthread_join(thread[i], NULL);
assert(xor_and_fetch_data == (1<<(NUM_THREADS+1))-1);
}
Expand Down
4 changes: 2 additions & 2 deletions test/pthread/test_pthread_join.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ int main()
assert(EM_ASM_INT(return PThread.runningWorkers.length) == 0);
assert(EM_ASM_INT(return PThread.unusedWorkers.length) == 8); // This test should be run with a prepopulated pool of size 8.

int n = 20;
emscripten_outf("Main: Spawning thread to compute fib(%d)...", n);
intptr_t n = 20;
emscripten_outf("Main: Spawning thread to compute fib(%ld)...", n);
int s = pthread_create(&thr, NULL, thread_start, (void*)n);
assert(s == 0);
emscripten_out("Main: Waiting for thread to join");
Expand Down
2 changes: 1 addition & 1 deletion test/pthread/test_pthread_malloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static void *thread_start(void *arg)
int main()
{
pthread_t thr[NUM_THREADS];
for(int i = 0; i < NUM_THREADS; ++i)
for(intptr_t i = 0; i < NUM_THREADS; ++i)
pthread_create(&thr[i], NULL, thread_start, (void*)(i*N));
int result = 0;
for(int i = 0; i < NUM_THREADS; ++i) {
Expand Down
4 changes: 2 additions & 2 deletions test/pthread/test_pthread_preallocates_workers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ static void *thread_start(void *arg)
pthread_exit((void*)0);
}

void CreateThread(int idx) {
emscripten_outf("Main: Spawning thread %d...", idx);
void CreateThread(intptr_t idx) {
emscripten_outf("Main: Spawning thread %ld...", idx);
int rc = pthread_create(&threads[idx], NULL, thread_start, (void*)idx);
assert(rc == 0);
}
Expand Down
14 changes: 11 additions & 3 deletions test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from common import BrowserCore, RunnerCore, path_from_root, has_browser, EMTEST_BROWSER, Reporting
from common import create_file, parameterized, ensure_dir, disabled, test_file, WEBIDL_BINDER
from common import read_file, requires_v8, also_with_minimal_runtime, EMRUN
from common import read_file, requires_v8, also_with_minimal_runtime, EMRUN, no_wasm64
from tools import shared
from tools import ports
from tools import utils
Expand Down Expand Up @@ -119,6 +119,8 @@ def also_with_wasm2js(f):
def metafunc(self, with_wasm2js):
assert self.get_setting('WASM') is None
if with_wasm2js:
if self.is_wasm64():
self.skipTest('wasm2js is not compatible with MEMORY64')
self.set_setting('WASM', 0)
f(self)
else:
Expand Down Expand Up @@ -4108,15 +4110,15 @@ def test_pthread_custom_pthread_main_url(self):

# Test that it is possible to define "Module.locateFile" string to locate where worker.js will be loaded from.
create_file('shell.html', read_file(path_from_root('src/shell.html')).replace('var Module = {', 'var Module = { locateFile: function (path, prefix) {if (path.endsWith(".wasm")) {return prefix + path;} else {return "cdn/" + path;}}, '))
self.compile_btest(['main.cpp', '--shell-file', 'shell.html', '-sWASM=0', '-pthread', '-sPTHREAD_POOL_SIZE', '-o', 'test.html'], reporting=Reporting.JS_ONLY)
self.compile_btest(['main.cpp', '--shell-file', 'shell.html', '-pthread', '-sPTHREAD_POOL_SIZE', '-o', 'test.html'], reporting=Reporting.JS_ONLY)
shutil.move('test.worker.js', Path('cdn/test.worker.js'))
if os.path.exists('test.html.mem'):
shutil.copyfile('test.html.mem', Path('cdn/test.html.mem'))
self.run_browser('test.html', '/report_result?exit:0')

# Test that it is possible to define "Module.locateFile(foo)" function to locate where worker.js will be loaded from.
create_file('shell2.html', read_file(path_from_root('src/shell.html')).replace('var Module = {', 'var Module = { locateFile: function(filename) { if (filename == "test.worker.js") return "cdn/test.worker.js"; else return filename; }, '))
self.compile_btest(['main.cpp', '--shell-file', 'shell2.html', '-sWASM=0', '-pthread', '-sPTHREAD_POOL_SIZE', '-o', 'test2.html'], reporting=Reporting.JS_ONLY)
self.compile_btest(['main.cpp', '--shell-file', 'shell2.html', '-pthread', '-sPTHREAD_POOL_SIZE', '-o', 'test2.html'], reporting=Reporting.JS_ONLY)
delete_file('test.worker.js')
self.run_browser('test2.html', '/report_result?exit:0')

Expand Down Expand Up @@ -4187,6 +4189,8 @@ def test_pthread_global_data_initialization(self):

@requires_threads
def test_pthread_global_data_initialization_in_sync_compilation_mode(self):
if self.is_wasm64():
self.skipTest('wasm2js is not compatible with MEMORY64')
mem_init_modes = [[], ['-sWASM=0', '--memory-init-file', '0'], ['-sWASM=0', '--memory-init-file', '1']]
for mem_init_mode in mem_init_modes:
print(mem_init_mode)
Expand Down Expand Up @@ -4230,6 +4234,7 @@ def test_pthread_safe_stack(self):
# same stack size as the main thread normally would.
self.btest('core/test_safe_stack.c', expected='abort:stack overflow', args=['-pthread', '-sPROXY_TO_PTHREAD', '-sSTACK_OVERFLOW_CHECK=2', '-sSTACK_SIZE=64KB'])

@no_wasm64('TODO: ASAN in memory64')
@parameterized({
'leak': ['test_pthread_lsan_leak', ['-gsource-map']],
'no_leak': ['test_pthread_lsan_no_leak', []],
Expand All @@ -4239,6 +4244,7 @@ def test_pthread_safe_stack(self):
def test_pthread_lsan(self, name, args):
self.btest(Path('pthread', name + '.cpp'), expected='1', args=['-fsanitize=leak', '-sINITIAL_MEMORY=256MB', '-pthread', '-sPROXY_TO_PTHREAD', '--pre-js', test_file('pthread', name + '.js')] + args)

@no_wasm64('TODO: ASAN in memory64')
@parameterized({
# Reusing the LSan test files for ASan.
'leak': ['test_pthread_lsan_leak', ['-gsource-map']],
Expand All @@ -4248,10 +4254,12 @@ def test_pthread_lsan(self, name, args):
def test_pthread_asan(self, name, args):
self.btest(Path('pthread', name + '.cpp'), expected='1', args=['-fsanitize=address', '-sINITIAL_MEMORY=256MB', '-pthread', '-sPROXY_TO_PTHREAD', '--pre-js', test_file('pthread', name + '.js')] + args)

@no_wasm64('TODO: ASAN in memory64')
@requires_threads
def test_pthread_asan_use_after_free(self):
self.btest('pthread/test_pthread_asan_use_after_free.cpp', expected='1', args=['-fsanitize=address', '-sINITIAL_MEMORY=256MB', '-pthread', '-sPROXY_TO_PTHREAD', '--pre-js', test_file('pthread/test_pthread_asan_use_after_free.js')])

@no_wasm64('TODO: ASAN in memory64')
@no_firefox('https://github.com/emscripten-core/emscripten/issues/20006')
@also_with_wasmfs
@requires_threads
Expand Down