Skip to content

Run atexit handlers before terminating all threads #14481

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 8 commits into from
Sep 20, 2021

Conversation

kleisauke
Copy link
Collaborator

@kleisauke kleisauke commented Jun 18, 2021

Although not defined by the POSIX standard, it seems that on most
systems1, atexit handlers are run before threads are terminated.

Helps: #13837.
Fixes: #14993.

1: AFAIK, only on Windows when linking against Microsoft's Universal
C runtime (UCRT), threads are terminated before atexit handlers are
called. See:
https://www.luke1410.de/blog/2017/02/the-trouble-of-separate-module-atexit-stacks/

Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

The primary change (moving terminateAllThreads) lgtm!

@sbc100
Copy link
Collaborator

sbc100 commented Jun 18, 2021

@RReverser what do you think about the lsan change here?

@sbc100
Copy link
Collaborator

sbc100 commented Jun 18, 2021

I would have though that lsan would register it own exit handler or somehow..

@RReverser
Copy link
Collaborator

@RReverser what do you think about the lsan change here?

I don't understand its implications tbh.

@kleisauke kleisauke force-pushed the atexit-thread branch 2 times, most recently from d19a715 to 94b61a9 Compare June 20, 2021 09:06
#endif
#if USE_PTHREADS
PThread.terminateAllThreads();
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the per-thread exist handler should go up before the program-wide exit handlers.. (so up on line 25). If you do that perhaps we can avoid the lsan changes? I'm not sure.

So maybe the overall order should be:

  1. per-thread-exit handler for this thread (PThread.runExitHandlers();)
  2. kill all other threads (PThread.terminateAllThreads())
  3. program-wide exit handlercallRuntimeCallbacks(ATEXIT);s (callRuntimeCallbacks(ATEXIT);
  4. program-wide JS exit handlers (<<< ATEXITS >>>)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

PThread.terminateAllThreads() (2) needs to be run after the program-wide exit handlers (3) and possibly also after the program-wide JS exit handlers (4). To test this, see the added test_pthread_atexit in this PR which deadlocks on the main branch.

I've revised the order to:

  1. program-wide exit handlers (callRuntimeCallbacks(__ATEXIT__)).
  2. program-wide JS exit handlers ( <<< ATEXITS >>>).
  3. terminate all other threads (PThread.terminateAllThreads()).
  4. call exit handlers for the main thread (PThread.runExitHandlers();).
    (this could probably be removed after PR Move __cxa_thread_atexit to native code #14489, but I'm not sure)

I've reverted the LSan changes for now, and just suppressed _emscripten_builtin_pthread_create from being leaky with commit 95e23fa.

Copy link
Collaborator

Choose a reason for hiding this comment

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

PThread.terminateAllThreads() (2) needs to be run after the program-wide exit handlers (3) and possibly also after the program-wide JS exit handlers (4). To test this, see the added test_pthread_atexit in this PR which deadlocks on the main branch.

I still need to take a look at that test but this seems logically incorrect to me. The program wide exit handler run, for example, the C++ static destructors. If we don't kill all the threads before running these then we could have situation where there are threads still running with access to static C++ objects which have been destroyed. For example imagine std::cout has a static destructor and one of the background threads tries to write to cout during this time?

I've revised the order to:

  1. program-wide exit handlers (callRuntimeCallbacks(__ATEXIT__)).
  2. program-wide JS exit handlers ( <<< ATEXITS >>>).
  3. terminate all other threads (PThread.terminateAllThreads()).
  4. call exit handlers for the main thread (PThread.runExitHandlers();).
    (this could probably be removed after PR Move __cxa_thread_atexit to native code #14489, but I'm not sure)

I've reverted the LSan changes for now, and just suppressed _emscripten_builtin_pthread_create from being leaky with commit 95e23fa.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually I think maybe you are right. Looking at the musl code for exit() is currently does:

_Noreturn void exit(int code)                                                    
{                                                                                
  __funcs_on_exit();                                                             
  __libc_exit_fini();                                                            
  __stdio_exit();                                                                
  _Exit(code)                                                                                                                  
}     

I think that secondary threads are not killed until _Exit is called (which calls __syscall(SYS_exit_group, ec) which I think terminates all the threads).

I guess that static destruction can run while there still threads running.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yup .. i ran quick test :

class Foo {                                                                      
  public:                                                                        
  Foo() {                                                                        
    printf("Foo\n");                                                             
  }                                                                              
  ~Foo() {                                                                       
    printf("~Foo\n");                                                            
    alive = false;                                                               
  }                                                                              
  bool alive = true;                                                             
};                                                                               
                                                                                 
static Foo foo;                                                                  
                                                                                 
static _Atomic int threadCounter = 0;                                            
static _Atomic int running = 1;                                                  
static pthread_t thread;                                                         
                                                                                 
void *workerThread(void* arg) {                                                  
  threadCounter++;                                                               
                                                                                 
  while (running) {                                                              
    printf("foo: %d\n", foo.alive);                                              
  }                                                                              
                                                                                 
  threadCounter--;                                                               
                                                                                 
  return NULL;                                                                   
}    
                                                                                    
void mainAtExit() {                                                                 
  printf("mainAtExit\n");                                                           
  sleep(1);                                                                         
}                                                                                   
                                                                                                                               
int main(int argc, char* argv[]) {                                                  
  int rc = atexit(mainAtExit);                                                      
  assert(rc == 0);                                                                  
                                                                                    
  rc = pthread_create(&thread, NULL, workerThread, NULL);                           
  assert(rc == 0);                                                                  
  return 0;                                                                         
}     

Copy link
Collaborator

@sbc100 sbc100 Jun 22, 2021

Choose a reason for hiding this comment

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

So you are correct terminateAllThreads should come last.. and threads that are still alive that point might have a bad time but that is to be expected.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the investigation. Given that PThread.terminateAllThreads() isn't last now, do you think PThread.runExitHandlers() should occur before PThread.terminateAllThreads() (i.e. swapping 3 and 4 in the above order)?

Also, let me know if you prefer commit 5805465 as a separate PR.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, that would make a good separate PR I think. IIUC that is not a file that exists upstream right?

Copy link
Collaborator

Choose a reason for hiding this comment

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

terminateAllThreads() should come last of all I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

asan_emscripten.cpp does indeed not exist upstream. I've moved that commit to PR #14516, and moved PThread.terminateAllThreads() to occur as last.

@sbc100
Copy link
Collaborator

sbc100 commented Jun 22, 2021

I just uploaded #14512 which I'm pretty sure overlaps with this change. I can wait for this to land first if you prefer.

@kleisauke
Copy link
Collaborator Author

No worries, feel free to land that PR earlier.

@kleisauke
Copy link
Collaborator Author

kleisauke commented Jun 30, 2021

PTAL, I've rebased it now that PR #14512 has landed. Note that commit 6ecd0d3 somehow didn't work, see:
https://circleci.com/gh/emscripten-core/emscripten/437890

I reintroduced the __lsan_do_leak_check()-change with commit af4626c since that's the easiest thing to do. I found the underlying issue, commit 454c84c and 64889df fixes this.

@kleisauke
Copy link
Collaborator Author

... I've split the ASan/LSan related changes into separate PRs.

@kleisauke
Copy link
Collaborator Author

Rebased against the main branch. Note that commit 8640757 is probably not necessary after PR #14912, as that swaps the _free functions with _emscripten_builtin_free in PThread.freeThreadData, which is similar to the withBuiltinMalloc block.

Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

lgtm with a few comments.

Thanks again for all your work on this!

// ready for such calls, as this can be called after the sanitizers
// are finalized. Instead, call free directly.
withBuiltinMalloc(function () {
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

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

The other places that we use withBuiltinMalloc are not guarded with if like this. Instead withBuiltinMalloc becomes a no-op. So I think you can avoid the if checks here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried that initially, but got this exception when I compiled without sanitizers (for e.g. when running wasm2.test_pthread_atexit).

exception thrown: ReferenceError: withBuiltinMalloc is not defined

@@ -154,7 +154,18 @@ var LibraryPThread = {
}
PThread.runningWorkers = [];
},
#if USE_ASAN || USE_LSAN
freeThreadData__deps: ['$withBuiltinMalloc'],
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this an essential part of this change?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Did you mean this __deps line? Or the withBuiltinMalloc in general? The comment above withBuiltinMalloc should explain why that is necessary, but perhaps it can be elaborated on further.

(Perhaps this could be moved to native code and mark that function with the no_sanitize("address") attribute(?))

Copy link
Collaborator

Choose a reason for hiding this comment

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

The __deps line is necessary, but none of the conditionals should be needed.

Copy link
Collaborator Author

@kleisauke kleisauke Sep 9, 2021

Choose a reason for hiding this comment

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

Ah, I had to move the $withBuiltinMalloc to $PThread__deps instead, otherwise it caused this exception when building without sanitizers:

exception thrown: ReferenceError: withBuiltinMalloc is not defined


static _Atomic int threadCounter = 0;
static _Atomic int running = 1;
static pthread_t thread;
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should probably be consistent and either drop the static here or add it to all he functions below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I dropped those static variables with commit 786473b.

threadCounter++;

while (running)
emscripten_thread_sleep(1000);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why 1000 here? If we want to test to run as fast as possible should we use a shorter value? Or maybe pthread_cond_wait instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Originally this test case was based on the example from https://www.luke1410.de/blog/2017/02/the-trouble-of-separate-module-atexit-stacks/. But indeed, this can be adjusted by using pthread_cond_wait instead, which is much neater.

Done with commit 786473b.

@@ -2322,6 +2322,12 @@ def test_pthread_equal(self):
def test_pthread_dispatch_after_exit(self):
self.do_run_in_out_file_test('pthread/test_pthread_dispatch_after_exit.c', interleaved_output=False)

@node_pthreads
def test_pthread_atexit(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add a comment either here or on the C source file describing exactly what we are testing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea! Done with commit 786473b.

@kleisauke
Copy link
Collaborator Author

The diff is now probably best viewable with the Hide whitespace changes option (due to the re-indentation in PThread.freeThreadData).

// sanitizer code that checks some things about pthreads. We are not
// ready for such calls, as this can be called after the sanitizers
// are finalized. Instead, call free directly.
withBuiltinMalloc(function () {
Copy link
Collaborator

@sbc100 sbc100 Sep 9, 2021

Choose a reason for hiding this comment

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

Actually I think we can can instead just replace the calls to _free below with calls to _emscripten_builtin_free.

The withBuiltinMalloc is mostly useful for calling entire functions that need to operate in both builtin and non-builtin modes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done with commit 3c41330.

@sbc100
Copy link
Collaborator

sbc100 commented Sep 9, 2021

Can you try running bunch of asan and lsan tests locally to be sure this isn't going to generate any leaks/failures.

I suggest ./tests/runner asan.*thread* lsan.*thread*

@kleisauke
Copy link
Collaborator Author

kleisauke commented Sep 9, 2021

After applying this patch (which disables some LSan tests that never returns on the main branch):

--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -2323,6 +2323,7 @@ The current type of b is: 9
     self.do_run_in_out_file_test('pthread/test_pthread_dispatch_after_exit.c', interleaved_output=False)
 
   @node_pthreads
+  @no_lsan('never returns when using LSan')
   def test_pthread_nested_work_queue(self):
     self.set_setting('EXIT_RUNTIME')
     self.set_setting('PTHREAD_POOL_SIZE', 1)
@@ -2336,6 +2337,7 @@ The current type of b is: 9
     self.do_run_in_out_file_test('pthread/test_pthread_thread_local_storage.cpp')
 
   @node_pthreads
+  @no_lsan('never returns when using LSan')
   def test_pthread_cleanup(self):
     self.set_setting('EXIT_RUNTIME')
     self.set_setting('PTHREAD_POOL_SIZE', 4)
@@ -8306,6 +8308,7 @@ NODEFS is no longer included by default; build with -lnodefs.js
     self.do_run_in_out_file_test('pthread/test_pthread_c11_threads.c')
 
   @node_pthreads
+  @no_lsan('never returns when using LSan')
   def test_pthread_cxx_threads(self):
     self.set_setting('PROXY_TO_PTHREAD')
     self.clear_setting('ALLOW_MEMORY_GROWTH')

I see this (tested commit 51dcc28):

Details
$ ./tests/runner asan.*thread*
Test suites:
['test_core']
Running test_core: (29 tests)
Using 6 parallel test processes
test_pthread_specific (test_core.asan) ... skipped 'test relies on null pointer reads'
.. return
test_pthread_no_exit_process (test_core.asan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
.. exit
test_pthread_nested_work_queue (test_core.asan) ... ok (3.79s)
test_safe_stack_pthread (test_core.asan) ... ok (3.87s)
test_pthread_offset_converter_modularize (test_core.asan) ... ok (3.89s)
test_pthread_offset_converter (test_core.asan) ... ok (3.89s)
test_pthread_emmalloc (test_core.asan) ... skipped 'ASan does not support custom memory allocators'
test_pthread_dylink_tls (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_dylink_basics (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_dylink (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_thread_local_storage (test_core.asan) ... ok (4.20s)
test_pthread_equal (test_core.asan) ... ok (3.23s)
test_pthread_exit_process (test_core.asan) ... ok (3.54s)
.. pthread_exit
test_pthread_dispatch_after_exit (test_core.asan) ... ok (3.89s)
test_pthread_cxx_threads (test_core.asan) ... ok (4.31s)
test_pthread_exceptions (test_core.asan) ... ok (6.22s)
test_pthread_setspecific_mainthread (test_core.asan) ... ok (11.31s)
test_pthread_create_pool (test_core.asan) ... ok (4.03s)
test_pthread_create_proxy (test_core.asan) ... ok (4.28s)
test_pthread_create_embind_stack_check (test_core.asan) ... ok (4.71s)
test_main_thread_em_asm (test_core.asan) ... skipped 'Cannot use ASan: test depends exactly on heap size'
test_pthread_create (test_core.asan) ... ok (4.29s)
test_pthread_cleanup (test_core.asan) ... ok (3.99s)
test_Module_dynamicLibraries_pthreads (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_main_thread_em_asm_signatures (test_core.asan) ... ok (2.79s)
test_pthread_c11_threads (test_core.asan) ... ok (3.52s)
test_pthread_abort (test_core.asan) ... ok (3.54s)
test_atexit_threads (test_core.asan) ... ok (2.22s)
test_main_thread_async_em_asm (test_core.asan) ... ok (3.68s)

DONE: combining results on main thread

test_Module_dynamicLibraries_pthreads (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_atexit_threads (test_core.asan) ... ok
test_main_thread_async_em_asm (test_core.asan) ... ok
test_main_thread_em_asm (test_core.asan) ... skipped 'Cannot use ASan: test depends exactly on heap size'
test_main_thread_em_asm_signatures (test_core.asan) ... ok
test_pthread_abort (test_core.asan) ... ok
test_pthread_c11_threads (test_core.asan) ... ok
test_pthread_cleanup (test_core.asan) ... ok
test_pthread_create (test_core.asan) ... ok
test_pthread_create_embind_stack_check (test_core.asan) ... ok
test_pthread_create_pool (test_core.asan) ... ok
test_pthread_create_proxy (test_core.asan) ... ok
test_pthread_cxx_threads (test_core.asan) ... ok
test_pthread_dispatch_after_exit (test_core.asan) ... ok
test_pthread_dylink (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_dylink_basics (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_dylink_tls (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_emmalloc (test_core.asan) ... skipped 'ASan does not support custom memory allocators'
test_pthread_equal (test_core.asan) ... ok
test_pthread_exceptions (test_core.asan) ... ok
test_pthread_exit_process (test_core.asan) ... ok
test_pthread_nested_work_queue (test_core.asan) ... ok
test_pthread_no_exit_process (test_core.asan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
test_pthread_offset_converter (test_core.asan) ... ok
test_pthread_offset_converter_modularize (test_core.asan) ... ok
test_pthread_setspecific_mainthread (test_core.asan) ... ok
test_pthread_specific (test_core.asan) ... skipped 'test relies on null pointer reads'
test_pthread_thread_local_storage (test_core.asan) ... ok
test_safe_stack_pthread (test_core.asan) ... ok

----------------------------------------------------------------------
Ran 29 tests in 16.391s

OK (skipped=8)
$ ./tests/runner lsan.*thread*
Test suites:
['test_core']
Running test_core: (29 tests)
Using 6 parallel test processes
.. return
test_pthread_specific (test_core.lsan) ... ok (3.53s)
test_pthread_no_exit_process (test_core.lsan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
test_pthread_nested_work_queue (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_offset_converter (test_core.lsan) ... ok (4.26s)
.. exit
test_safe_stack_pthread (test_core.lsan) ... ok (4.50s)
test_pthread_offset_converter_modularize (test_core.lsan) ... ok (4.55s)
test_pthread_emmalloc (test_core.lsan) ... skipped 'LSan does not support custom memory allocators'
test_pthread_dylink_tls (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_basics (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
-- begin program output --
CreateThread 0
CreateThread 1
CreateThread 2
CreateThread 3
CreateThread 4
CreateThread 5
CreateThread 6
CreateThread 7
Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain
pthread sent an error! undefined:undefined: abort(Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain). Build with -s ASSERTIONS=1 for more info.

/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.js:145
   throw ex;
   ^
Error [RuntimeError]: abort(Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain). Build with -s ASSERTIONS=1 for more info.
    at abort (eval at importScripts (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:761:10)
    at ___assert_fail (eval at importScripts (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:1334:2)
    at ThreadMain(void*) (<anonymous>:wasm-function[49]:0x1092)
    at __lsan_thread_start_func (<anonymous>:wasm-function[157]:0xa28b)
    at Object.invokeEntryPoint (eval at importScripts (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:1313:27)
    at self.onmessage (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:132:48)
    at MessagePort.<anonymous> (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:24:5)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Emitted 'error' event on process instance at:
    at emitUnhandledRejectionOrErr (internal/event_target.js:545:11)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:358:9)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Thrown at:
    at /tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.js:145:4
    at emit (events.js:315:20)
    at internal/process/execution.js:163:25
-- end program output --
test_pthread_thread_local_storage (test_core.lsan) ... FAIL
test_pthread_cxx_threads (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_exit_process (test_core.lsan) ... ok (3.06s)
test_pthread_equal (test_core.lsan) ... ok (2.26s)
.. pthread_exit
test_pthread_dispatch_after_exit (test_core.lsan) ... ok (2.55s)
test_pthread_exceptions (test_core.lsan) ... ok (3.08s)
test_pthread_cleanup (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_create_proxy (test_core.lsan) ... ok (2.93s)
test_pthread_create_pool (test_core.lsan) ... ok (2.87s)
test_pthread_setspecific_mainthread (test_core.lsan) ... ok (9.48s)
test_pthread_create_embind_stack_check (test_core.lsan) ... ok (2.94s)
test_pthread_create (test_core.lsan) ... ok (2.81s)
-- begin program output --
thrd_current: 0x1303560
worker.js onmessage() captured an uncaught exception: RuntimeError: memory access out of bounds
pthread sent an error! undefined:undefined: memory access out of bounds

/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.js:145
   throw ex;
   ^
Error [RuntimeError]: memory access out of bounds
    at pthread_create (<anonymous>:wasm-function[177]:0xa78c)
    at thrd_create (<anonymous>:wasm-function[109]:0x3a9e)
    at main (<anonymous>:wasm-function[53]:0x11c9)
    at Module._main (eval at importScripts (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:38:16), <anonymous>:4936:59)
    at ___call_main (eval at importScripts (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:38:16), <anonymous>:1333:19)
    at _main_thread (<anonymous>:wasm-function[309]:0x125fb)
    at __lsan_thread_start_func (<anonymous>:wasm-function[176]:0xa705)
    at Object.invokeEntryPoint (eval at importScripts (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:38:16), <anonymous>:1308:27)
    at self.onmessage (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:132:48)
    at MessagePort.<anonymous> (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:24:5)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Emitted 'error' event on process instance at:
    at emitUnhandledRejectionOrErr (internal/event_target.js:545:11)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:358:9)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Thrown at:
    at /tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.js:145:4
    at emit (events.js:315:20)
    at internal/process/execution.js:163:25
-- end program output --
test_pthread_c11_threads (test_core.lsan) ... FAIL
test_Module_dynamicLibraries_pthreads (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_abort (test_core.lsan) ... ok (2.37s)
test_main_thread_em_asm_signatures (test_core.lsan) ... ok (1.50s)
test_atexit_threads (test_core.lsan) ... ok (1.37s)
test_main_thread_em_asm (test_core.lsan) ... ok (2.56s)
test_main_thread_async_em_asm (test_core.lsan) ... ok (2.35s)

DONE: combining results on main thread

test_Module_dynamicLibraries_pthreads (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_atexit_threads (test_core.lsan) ... ok
test_main_thread_async_em_asm (test_core.lsan) ... ok
test_main_thread_em_asm (test_core.lsan) ... ok
test_main_thread_em_asm_signatures (test_core.lsan) ... ok
test_pthread_abort (test_core.lsan) ... ok
test_pthread_c11_threads (test_core.lsan) ... FAIL
test_pthread_cleanup (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_create (test_core.lsan) ... ok
test_pthread_create_embind_stack_check (test_core.lsan) ... ok
test_pthread_create_pool (test_core.lsan) ... ok
test_pthread_create_proxy (test_core.lsan) ... ok
test_pthread_cxx_threads (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_dispatch_after_exit (test_core.lsan) ... ok
test_pthread_dylink (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_basics (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_tls (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_emmalloc (test_core.lsan) ... skipped 'LSan does not support custom memory allocators'
test_pthread_equal (test_core.lsan) ... ok
test_pthread_exceptions (test_core.lsan) ... ok
test_pthread_exit_process (test_core.lsan) ... ok
test_pthread_nested_work_queue (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_no_exit_process (test_core.lsan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
test_pthread_offset_converter (test_core.lsan) ... ok
test_pthread_offset_converter_modularize (test_core.lsan) ... ok
test_pthread_setspecific_mainthread (test_core.lsan) ... ok
test_pthread_specific (test_core.lsan) ... ok
test_pthread_thread_local_storage (test_core.lsan) ... FAIL
test_safe_stack_pthread (test_core.lsan) ... ok

======================================================================
FAIL: test_pthread_c11_threads (test_core.lsan)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/usr/lib64/python3.9/unittest/case.py", line 593, in run
    self._callTestMethod(testMethod)
  File "/usr/lib64/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/home/kleisauke/emscripten/tests/common.py", line 188, in decorated
    f(self, *args, **kwargs)
  File "/home/kleisauke/emscripten/tests/test_core.py", line 8308, in test_pthread_c11_threads
    self.do_run_in_out_file_test('pthread/test_pthread_c11_threads.c')
  File "/home/kleisauke/emscripten/tests/common.py", line 1018, in do_run_in_out_file_test
    self._build_and_run(srcfile, expected, **kwargs)
  File "/home/kleisauke/emscripten/tests/common.py", line 1061, in _build_and_run
    js_output = self.run_js(js_file, engine, args,
  File "/home/kleisauke/emscripten/tests/common.py", line 681, in run_js
    self.fail('JS subprocess failed (%s): %s.  Output:\n%s' % (error.cmd, error.returncode, ret))
  File "/usr/lib64/python3.9/unittest/case.py", line 670, in fail
    raise self.failureException(msg)
AssertionError: JS subprocess failed (/home/kleisauke/emsdk/node/14.15.5_64bit/bin/node --stack-trace-limit=50 --unhandled-rejections=throw --trace-uncaught --experimental-wasm-threads --experimental-wasm-bulk-memory /tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.js): 7.  Output:
thrd_current: 0x1303560
worker.js onmessage() captured an uncaught exception: RuntimeError: memory access out of bounds
pthread sent an error! undefined:undefined: memory access out of bounds

/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.js:145
   throw ex;
   ^
Error [RuntimeError]: memory access out of bounds
    at pthread_create (<anonymous>:wasm-function[177]:0xa78c)
    at thrd_create (<anonymous>:wasm-function[109]:0x3a9e)
    at main (<anonymous>:wasm-function[53]:0x11c9)
    at Module._main (eval at importScripts (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:38:16), <anonymous>:4936:59)
    at ___call_main (eval at importScripts (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:38:16), <anonymous>:1333:19)
    at _main_thread (<anonymous>:wasm-function[309]:0x125fb)
    at __lsan_thread_start_func (<anonymous>:wasm-function[176]:0xa705)
    at Object.invokeEntryPoint (eval at importScripts (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:38:16), <anonymous>:1308:27)
    at self.onmessage (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:132:48)
    at MessagePort.<anonymous> (/tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.worker.js:24:5)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Emitted 'error' event on process instance at:
    at emitUnhandledRejectionOrErr (internal/event_target.js:545:11)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:358:9)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Thrown at:
    at /tmp/tmpn28v17xo/emscripten_test_lsan_fyyhhv_j/test_pthread_c11_threads.js:145:4
    at emit (events.js:315:20)
    at internal/process/execution.js:163:25


======================================================================
FAIL: test_pthread_thread_local_storage (test_core.lsan)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/usr/lib64/python3.9/unittest/case.py", line 593, in run
    self._callTestMethod(testMethod)
  File "/usr/lib64/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/home/kleisauke/emscripten/tests/common.py", line 188, in decorated
    f(self, *args, **kwargs)
  File "/home/kleisauke/emscripten/tests/test_core.py", line 2337, in test_pthread_thread_local_storage
    self.do_run_in_out_file_test('pthread/test_pthread_thread_local_storage.cpp')
  File "/home/kleisauke/emscripten/tests/common.py", line 1018, in do_run_in_out_file_test
    self._build_and_run(srcfile, expected, **kwargs)
  File "/home/kleisauke/emscripten/tests/common.py", line 1061, in _build_and_run
    js_output = self.run_js(js_file, engine, args,
  File "/home/kleisauke/emscripten/tests/common.py", line 681, in run_js
    self.fail('JS subprocess failed (%s): %s.  Output:\n%s' % (error.cmd, error.returncode, ret))
  File "/usr/lib64/python3.9/unittest/case.py", line 670, in fail
    raise self.failureException(msg)
AssertionError: JS subprocess failed (/home/kleisauke/emsdk/node/14.15.5_64bit/bin/node --stack-trace-limit=50 --unhandled-rejections=throw --trace-uncaught --experimental-wasm-threads --experimental-wasm-bulk-memory /tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.js): 7.  Output:
CreateThread 0
CreateThread 1
CreateThread 2
CreateThread 3
CreateThread 4
CreateThread 5
CreateThread 6
CreateThread 7
Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain
pthread sent an error! undefined:undefined: abort(Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain). Build with -s ASSERTIONS=1 for more info.

/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.js:145
   throw ex;
   ^
Error [RuntimeError]: abort(Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain). Build with -s ASSERTIONS=1 for more info.
    at abort (eval at importScripts (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:761:10)
    at ___assert_fail (eval at importScripts (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:1334:2)
    at ThreadMain(void*) (<anonymous>:wasm-function[49]:0x1092)
    at __lsan_thread_start_func (<anonymous>:wasm-function[157]:0xa28b)
    at Object.invokeEntryPoint (eval at importScripts (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:1313:27)
    at self.onmessage (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:132:48)
    at MessagePort.<anonymous> (/tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.worker.js:24:5)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Emitted 'error' event on process instance at:
    at emitUnhandledRejectionOrErr (internal/event_target.js:545:11)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:358:9)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Thrown at:
    at /tmp/tmp4sqjvzqv/emscripten_test_lsan_mzj4qsib/test_pthread_thread_local_storage.js:145:4
    at emit (events.js:315:20)
    at internal/process/execution.js:163:25


----------------------------------------------------------------------
Ran 29 tests in 12.273s

FAILED (failures=2, skipped=9)

And with this PR (tested commit 4eb2200)

Details
$ ./tests/runner asan.*thread*
Test suites:
['test_core']
Running test_core: (30 tests)
Using 6 parallel test processes
test_pthread_specific (test_core.asan) ... skipped 'test relies on null pointer reads'
.. return
test_pthread_no_exit_process (test_core.asan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
.. exit
test_pthread_offset_converter_modularize (test_core.asan) ... ok (4.07s)
test_pthread_offset_converter (test_core.asan) ... ok (4.08s)
test_pthread_nested_work_queue (test_core.asan) ... ok (4.26s)
test_safe_stack_pthread (test_core.asan) ... ok (4.55s)
test_pthread_emmalloc (test_core.asan) ... skipped 'ASan does not support custom memory allocators'
test_pthread_dylink_tls (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_dylink_basics (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_dylink (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_thread_local_storage (test_core.asan) ... ok (4.69s)
test_pthread_equal (test_core.asan) ... ok (3.44s)
.. pthread_exit
test_pthread_exit_process (test_core.asan) ... ok (3.98s)
test_pthread_cxx_threads (test_core.asan) ... ok (3.95s)
test_pthread_dispatch_after_exit (test_core.asan) ... ok (4.38s)
test_pthread_exceptions (test_core.asan) ... ok (6.08s)
test_pthread_setspecific_mainthread (test_core.asan) ... ok (11.90s)
test_pthread_create_proxy (test_core.asan) ... ok (4.30s)
test_pthread_create_pool (test_core.asan) ... ok (4.19s)
test_pthread_create (test_core.asan) ... ok (4.32s)
test_pthread_create_embind_stack_check (test_core.asan) ... ok (5.12s)
test_main_thread_em_asm (test_core.asan) ... skipped 'Cannot use ASan: test depends exactly on heap size'
test_pthread_cleanup (test_core.asan) ... ok (4.21s)
test_pthread_atexit (test_core.asan) ... ok (3.92s)
test_Module_dynamicLibraries_pthreads (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_abort (test_core.asan) ... ok (3.79s)
test_main_thread_em_asm_signatures (test_core.asan) ... ok (2.88s)
test_pthread_c11_threads (test_core.asan) ... ok (4.24s)
test_atexit_threads (test_core.asan) ... ok (2.25s)
test_main_thread_async_em_asm (test_core.asan) ... ok (3.90s)

DONE: combining results on main thread

test_Module_dynamicLibraries_pthreads (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_atexit_threads (test_core.asan) ... ok
test_main_thread_async_em_asm (test_core.asan) ... ok
test_main_thread_em_asm (test_core.asan) ... skipped 'Cannot use ASan: test depends exactly on heap size'
test_main_thread_em_asm_signatures (test_core.asan) ... ok
test_pthread_abort (test_core.asan) ... ok
test_pthread_atexit (test_core.asan) ... ok
test_pthread_c11_threads (test_core.asan) ... ok
test_pthread_cleanup (test_core.asan) ... ok
test_pthread_create (test_core.asan) ... ok
test_pthread_create_embind_stack_check (test_core.asan) ... ok
test_pthread_create_pool (test_core.asan) ... ok
test_pthread_create_proxy (test_core.asan) ... ok
test_pthread_cxx_threads (test_core.asan) ... ok
test_pthread_dispatch_after_exit (test_core.asan) ... ok
test_pthread_dylink (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_dylink_basics (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_dylink_tls (test_core.asan) ... skipped 'no dynamic linking support in ASan yet'
test_pthread_emmalloc (test_core.asan) ... skipped 'ASan does not support custom memory allocators'
test_pthread_equal (test_core.asan) ... ok
test_pthread_exceptions (test_core.asan) ... ok
test_pthread_exit_process (test_core.asan) ... ok
test_pthread_nested_work_queue (test_core.asan) ... ok
test_pthread_no_exit_process (test_core.asan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
test_pthread_offset_converter (test_core.asan) ... ok
test_pthread_offset_converter_modularize (test_core.asan) ... ok
test_pthread_setspecific_mainthread (test_core.asan) ... ok
test_pthread_specific (test_core.asan) ... skipped 'test relies on null pointer reads'
test_pthread_thread_local_storage (test_core.asan) ... ok
test_safe_stack_pthread (test_core.asan) ... ok

----------------------------------------------------------------------
Ran 30 tests in 17.876s

OK (skipped=8)
$ ./tests/runner lsan.*thread*
Test suites:
['test_core']
Running test_core: (30 tests)
Using 6 parallel test processes
.. return
test_pthread_specific (test_core.lsan) ... ok (1.77s)
test_pthread_no_exit_process (test_core.lsan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
test_pthread_nested_work_queue (test_core.lsan) ... skipped 'never returns when using LSan'
.. exit
test_safe_stack_pthread (test_core.lsan) ... ok (2.61s)
test_pthread_offset_converter (test_core.lsan) ... ok (2.64s)
test_pthread_offset_converter_modularize (test_core.lsan) ... ok (2.66s)
test_pthread_emmalloc (test_core.lsan) ... skipped 'LSan does not support custom memory allocators'
test_pthread_dylink_tls (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_basics (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
-- begin program output --
CreateThread 0
CreateThread 1
CreateThread 2
CreateThread 3
CreateThread 4
CreateThread 5
CreateThread 6
CreateThread 7
Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain
pthread sent an error! undefined:undefined: abort(Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain). Build with -s ASSERTIONS=1 for more info.

/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.js:145
   throw ex;
   ^
Error [RuntimeError]: abort(Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain). Build with -s ASSERTIONS=1 for more info.
    at abort (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:762:10)
    at ___assert_fail (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:1335:2)
    at ThreadMain(void*) (<anonymous>:wasm-function[49]:0x1092)
    at __lsan_thread_start_func (<anonymous>:wasm-function[157]:0xa28b)
    at Object.invokeEntryPoint (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:1314:27)
    at self.onmessage (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:132:48)
    at MessagePort.<anonymous> (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:24:5)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Emitted 'error' event on process instance at:
    at emitUnhandledRejectionOrErr (internal/event_target.js:545:11)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:358:9)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Thrown at:
    at /tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.js:145:4
    at emit (events.js:315:20)
    at internal/process/execution.js:163:25
-- end program output --
test_pthread_thread_local_storage (test_core.lsan) ... FAIL
test_pthread_cxx_threads (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_exit_process (test_core.lsan) ... ok (2.70s)
test_pthread_equal (test_core.lsan) ... ok (2.20s)
.. pthread_exit
test_pthread_dispatch_after_exit (test_core.lsan) ... ok (2.60s)
-- begin program output --
main iter 1 : 0
<snip />
main iter 1594 : 200
done!

=================================================================
==42==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 131072 byte(s) in 1 object(s) allocated from:
    #0 0x99a2 in memalign+0x99a2 (/tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.wasm+0x99a2)
    #1 0x800005bd in ___pthread_create_js /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js:1469:15
    #2 0x15a3 in _do_call+0x15a3 (/tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.wasm+0x15a3)
    #3 0x11ec in emscripten_current_thread_process_queued_calls+0x11ec (/tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.wasm+0x11ec)
    #4 0x1cb8 in emscripten_main_thread_process_queued_calls+0x1cb8 (/tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.wasm+0x1cb8)
    #5 0x80001371 in Module._emscripten_main_thread_process_queued_calls /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js:4977:176
    #6 0x800004cb in Worker.worker.onmessage /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js:1227:5
    #7 0x800004fc in Worker.<anonymous> /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js:1276:12
    #8 0x8000013b in Worker.emit events.js:315:20
    #9 0x800000cf in MessagePort.<anonymous> internal/worker.js:207:53
    #10 0x80000162 in MessagePort.[nodejs.internal.kHybridDispatch] internal/event_target.js:354:41
    #11 0x80000012 in MessagePort.exports.emitMessage internal/per_context/messageport.js:18:26

SUMMARY: LeakSanitizer: 131072 byte(s) leaked in 1 allocation(s).
-- end program output --
test_pthread_create_proxy (test_core.lsan) ... FAIL
test_pthread_cleanup (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_exceptions (test_core.lsan) ... ok (3.83s)
test_pthread_atexit (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_create_pool (test_core.lsan) ... ok (2.73s)
test_pthread_setspecific_mainthread (test_core.lsan) ... ok (7.44s)
test_pthread_create_embind_stack_check (test_core.lsan) ... ok (3.13s)
test_pthread_create (test_core.lsan) ... ok (3.01s)
-- begin program output --
thrd_current: 0x1303560
worker.js onmessage() captured an uncaught exception: RuntimeError: memory access out of bounds
pthread sent an error! undefined:undefined: memory access out of bounds

/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.js:145
   throw ex;
   ^
Error [RuntimeError]: memory access out of bounds
    at pthread_create (<anonymous>:wasm-function[177]:0xa78c)
    at thrd_create (<anonymous>:wasm-function[109]:0x3a9e)
    at main (<anonymous>:wasm-function[53]:0x11c9)
    at Module._main (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:38:16), <anonymous>:5000:59)
    at ___call_main (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:38:16), <anonymous>:1334:19)
    at _main_thread (<anonymous>:wasm-function[309]:0x125fb)
    at __lsan_thread_start_func (<anonymous>:wasm-function[176]:0xa705)
    at Object.invokeEntryPoint (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:38:16), <anonymous>:1309:27)
    at self.onmessage (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:132:48)
    at MessagePort.<anonymous> (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:24:5)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Emitted 'error' event on process instance at:
    at emitUnhandledRejectionOrErr (internal/event_target.js:545:11)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:358:9)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
    at Worker.[kOnExit] (internal/worker.js:233:5)
    at Worker.<computed>.onexit (internal/worker.js:174:20)
Thrown at:
    at /tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.js:145:4
    at emit (events.js:315:20)
    at internal/process/execution.js:163:25
-- end program output --
test_pthread_c11_threads (test_core.lsan) ... FAIL
test_Module_dynamicLibraries_pthreads (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_main_thread_em_asm_signatures (test_core.lsan) ... ok (1.57s)
test_pthread_abort (test_core.lsan) ... ok (2.45s)
test_atexit_threads (test_core.lsan) ... ok (1.24s)
test_main_thread_em_asm (test_core.lsan) ... ok (2.66s)
test_main_thread_async_em_asm (test_core.lsan) ... ok (2.39s)

DONE: combining results on main thread

test_Module_dynamicLibraries_pthreads (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_atexit_threads (test_core.lsan) ... ok
test_main_thread_async_em_asm (test_core.lsan) ... ok
test_main_thread_em_asm (test_core.lsan) ... ok
test_main_thread_em_asm_signatures (test_core.lsan) ... ok
test_pthread_abort (test_core.lsan) ... ok
test_pthread_atexit (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_c11_threads (test_core.lsan) ... FAIL
test_pthread_cleanup (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_create (test_core.lsan) ... ok
test_pthread_create_embind_stack_check (test_core.lsan) ... ok
test_pthread_create_pool (test_core.lsan) ... ok
test_pthread_create_proxy (test_core.lsan) ... FAIL
test_pthread_cxx_threads (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_dispatch_after_exit (test_core.lsan) ... ok
test_pthread_dylink (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_basics (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_tls (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_emmalloc (test_core.lsan) ... skipped 'LSan does not support custom memory allocators'
test_pthread_equal (test_core.lsan) ... ok
test_pthread_exceptions (test_core.lsan) ... ok
test_pthread_exit_process (test_core.lsan) ... ok
test_pthread_nested_work_queue (test_core.lsan) ... skipped 'never returns when using LSan'
test_pthread_no_exit_process (test_core.lsan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
test_pthread_offset_converter (test_core.lsan) ... ok
test_pthread_offset_converter_modularize (test_core.lsan) ... ok
test_pthread_setspecific_mainthread (test_core.lsan) ... ok
test_pthread_specific (test_core.lsan) ... ok
test_pthread_thread_local_storage (test_core.lsan) ... FAIL
test_safe_stack_pthread (test_core.lsan) ... ok

======================================================================
FAIL: test_pthread_c11_threads (test_core.lsan)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/usr/lib64/python3.9/unittest/case.py", line 593, in run
    self._callTestMethod(testMethod)
  File "/usr/lib64/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/home/kleisauke/emscripten/tests/common.py", line 188, in decorated
    f(self, *args, **kwargs)
  File "/home/kleisauke/emscripten/tests/test_core.py", line 8307, in test_pthread_c11_threads
    self.do_run_in_out_file_test('pthread/test_pthread_c11_threads.c')
  File "/home/kleisauke/emscripten/tests/common.py", line 1018, in do_run_in_out_file_test
    self._build_and_run(srcfile, expected, **kwargs)
  File "/home/kleisauke/emscripten/tests/common.py", line 1061, in _build_and_run
    js_output = self.run_js(js_file, engine, args,
  File "/home/kleisauke/emscripten/tests/common.py", line 681, in run_js
    self.fail('JS subprocess failed (%s): %s.  Output:\n%s' % (error.cmd, error.returncode, ret))
  File "/usr/lib64/python3.9/unittest/case.py", line 670, in fail
    raise self.failureException(msg)
AssertionError: JS subprocess failed (/home/kleisauke/emsdk/node/14.15.5_64bit/bin/node --stack-trace-limit=50 --unhandled-rejections=throw --trace-uncaught --experimental-wasm-threads --experimental-wasm-bulk-memory /tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.js): 7.  Output:
thrd_current: 0x1303560
worker.js onmessage() captured an uncaught exception: RuntimeError: memory access out of bounds
pthread sent an error! undefined:undefined: memory access out of bounds

/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.js:145
   throw ex;
   ^
Error [RuntimeError]: memory access out of bounds
    at pthread_create (<anonymous>:wasm-function[177]:0xa78c)
    at thrd_create (<anonymous>:wasm-function[109]:0x3a9e)
    at main (<anonymous>:wasm-function[53]:0x11c9)
    at Module._main (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:38:16), <anonymous>:5000:59)
    at ___call_main (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:38:16), <anonymous>:1334:19)
    at _main_thread (<anonymous>:wasm-function[309]:0x125fb)
    at __lsan_thread_start_func (<anonymous>:wasm-function[176]:0xa705)
    at Object.invokeEntryPoint (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:38:16), <anonymous>:1309:27)
    at self.onmessage (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:132:48)
    at MessagePort.<anonymous> (/tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.worker.js:24:5)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Emitted 'error' event on process instance at:
    at emitUnhandledRejectionOrErr (internal/event_target.js:545:11)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:358:9)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
    at Worker.[kOnExit] (internal/worker.js:233:5)
    at Worker.<computed>.onexit (internal/worker.js:174:20)
Thrown at:
    at /tmp/tmppexoop9e/emscripten_test_lsan_0y_v7bmb/test_pthread_c11_threads.js:145:4
    at emit (events.js:315:20)
    at internal/process/execution.js:163:25


======================================================================
FAIL: test_pthread_create_proxy (test_core.lsan)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/usr/lib64/python3.9/unittest/case.py", line 593, in run
    self._callTestMethod(testMethod)
  File "/usr/lib64/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/home/kleisauke/emscripten/tests/common.py", line 188, in decorated
    f(self, *args, **kwargs)
  File "/home/kleisauke/emscripten/tests/test_core.py", line 8332, in test_pthread_create_proxy
    self.do_run_in_out_file_test('core/pthread/create.cpp')
  File "/home/kleisauke/emscripten/tests/common.py", line 1018, in do_run_in_out_file_test
    self._build_and_run(srcfile, expected, **kwargs)
  File "/home/kleisauke/emscripten/tests/common.py", line 1061, in _build_and_run
    js_output = self.run_js(js_file, engine, args,
  File "/home/kleisauke/emscripten/tests/common.py", line 681, in run_js
    self.fail('JS subprocess failed (%s): %s.  Output:\n%s' % (error.cmd, error.returncode, ret))
  File "/usr/lib64/python3.9/unittest/case.py", line 670, in fail
    raise self.failureException(msg)
AssertionError: JS subprocess failed (/home/kleisauke/emsdk/node/14.15.5_64bit/bin/node --stack-trace-limit=50 --unhandled-rejections=throw --trace-uncaught --experimental-wasm-threads --experimental-wasm-bulk-memory /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js): 23.  Output:
main iter 1 : 0
<snip />
main iter 1594 : 200
done!

=================================================================
==42==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 131072 byte(s) in 1 object(s) allocated from:
    #0 0x99a2 in memalign+0x99a2 (/tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.wasm+0x99a2)
    #1 0x800005bd in ___pthread_create_js /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js:1469:15
    #2 0x15a3 in _do_call+0x15a3 (/tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.wasm+0x15a3)
    #3 0x11ec in emscripten_current_thread_process_queued_calls+0x11ec (/tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.wasm+0x11ec)
    #4 0x1cb8 in emscripten_main_thread_process_queued_calls+0x1cb8 (/tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.wasm+0x1cb8)
    #5 0x80001371 in Module._emscripten_main_thread_process_queued_calls /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js:4977:176
    #6 0x800004cb in Worker.worker.onmessage /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js:1227:5
    #7 0x800004fc in Worker.<anonymous> /tmp/tmppexoop9e/emscripten_test_lsan_zwj2mfn2/create.js:1276:12
    #8 0x8000013b in Worker.emit events.js:315:20
    #9 0x800000cf in MessagePort.<anonymous> internal/worker.js:207:53
    #10 0x80000162 in MessagePort.[nodejs.internal.kHybridDispatch] internal/event_target.js:354:41
    #11 0x80000012 in MessagePort.exports.emitMessage internal/per_context/messageport.js:18:26

SUMMARY: LeakSanitizer: 131072 byte(s) leaked in 1 allocation(s).


======================================================================
FAIL: test_pthread_thread_local_storage (test_core.lsan)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/usr/lib64/python3.9/unittest/case.py", line 593, in run
    self._callTestMethod(testMethod)
  File "/usr/lib64/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/home/kleisauke/emscripten/tests/common.py", line 188, in decorated
    f(self, *args, **kwargs)
  File "/home/kleisauke/emscripten/tests/test_core.py", line 2345, in test_pthread_thread_local_storage
    self.do_run_in_out_file_test('pthread/test_pthread_thread_local_storage.cpp')
  File "/home/kleisauke/emscripten/tests/common.py", line 1018, in do_run_in_out_file_test
    self._build_and_run(srcfile, expected, **kwargs)
  File "/home/kleisauke/emscripten/tests/common.py", line 1061, in _build_and_run
    js_output = self.run_js(js_file, engine, args,
  File "/home/kleisauke/emscripten/tests/common.py", line 681, in run_js
    self.fail('JS subprocess failed (%s): %s.  Output:\n%s' % (error.cmd, error.returncode, ret))
  File "/usr/lib64/python3.9/unittest/case.py", line 670, in fail
    raise self.failureException(msg)
AssertionError: JS subprocess failed (/home/kleisauke/emsdk/node/14.15.5_64bit/bin/node --stack-trace-limit=50 --unhandled-rejections=throw --trace-uncaught --experimental-wasm-threads --experimental-wasm-bulk-memory /tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.js): 7.  Output:
CreateThread 0
CreateThread 1
CreateThread 2
CreateThread 3
CreateThread 4
CreateThread 5
CreateThread 6
CreateThread 7
Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain
pthread sent an error! undefined:undefined: abort(Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain). Build with -s ASSERTIONS=1 for more info.

/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.js:145
   throw ex;
   ^
Error [RuntimeError]: abort(Assertion failed: local_keys[i] == NUM_ITERS, at: /home/kleisauke/emscripten/tests/pthread/test_pthread_thread_local_storage.cpp,42,ThreadMain). Build with -s ASSERTIONS=1 for more info.
    at abort (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:762:10)
    at ___assert_fail (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:1335:2)
    at ThreadMain(void*) (<anonymous>:wasm-function[49]:0x1092)
    at __lsan_thread_start_func (<anonymous>:wasm-function[157]:0xa28b)
    at Object.invokeEntryPoint (eval at importScripts (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:38:16), <anonymous>:1314:27)
    at self.onmessage (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:132:48)
    at MessagePort.<anonymous> (/tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.worker.js:24:5)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Emitted 'error' event on process instance at:
    at emitUnhandledRejectionOrErr (internal/event_target.js:545:11)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:358:9)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
Thrown at:
    at /tmp/tmppexoop9e/emscripten_test_lsan_nol8pyfh/test_pthread_thread_local_storage.js:145:4
    at emit (events.js:315:20)
    at internal/process/execution.js:163:25


----------------------------------------------------------------------
Ran 30 tests in 10.579s

FAILED (failures=3, skipped=10)

So, it seems that lsan.test_pthread_create_proxy has regressed. And I also had to disable the lsan.test_pthread_atexit test (which was added to this PR). I'll investigate.

@kleisauke
Copy link
Collaborator Author

With commit b477ef7, I now see:

Details
$ ./tests/runner lsan.*thread*
Test suites:
['test_core']
Running test_core: (30 tests)
Using 6 parallel test processes
.. return
-- begin program output --
pthread_key_create = 0
pthread_setspecific = 0
pthread_getspecific = 123
valid pthread_setspecific for value NULL = 0
pthread_getspecific = 0
pthread_getspecific after key recreate = 0
pthread_key_delete = 0
pthread_key_delete repeated = 28
pthread_setspecific for value NULL = 28
pthread_key_delete just after created = 0
exception thrown: RuntimeError: memory access out of bounds
-- end program output --
test_pthread_specific (test_core.lsan) ... FAIL
test_pthread_no_exit_process (test_core.lsan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
.. exit
test_pthread_offset_converter (test_core.lsan) ... ok (2.98s)
test_safe_stack_pthread (test_core.lsan) ... ok (3.10s)
test_pthread_offset_converter_modularize (test_core.lsan) ... ok (3.36s)
test_pthread_thread_local_storage (test_core.lsan) ... ok (3.51s)
test_pthread_emmalloc (test_core.lsan) ... skipped 'LSan does not support custom memory allocators'
test_pthread_dylink_tls (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_basics (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_nested_work_queue (test_core.lsan) ... ok (3.06s)
.. pthread_exit
test_pthread_equal (test_core.lsan) ... ok (2.65s)
test_pthread_exit_process (test_core.lsan) ... ok (3.07s)
test_pthread_exceptions (test_core.lsan) ... ok (3.57s)
test_pthread_dispatch_after_exit (test_core.lsan) ... ok (3.38s)
test_pthread_cxx_threads (test_core.lsan) ... ok (3.33s)
test_pthread_setspecific_mainthread (test_core.lsan) ... ok (9.08s)
test_pthread_create_proxy (test_core.lsan) ... ok (3.26s)
test_pthread_create_pool (test_core.lsan) ... ok (3.29s)
test_pthread_create (test_core.lsan) ... ok (3.61s)
test_pthread_create_embind_stack_check (test_core.lsan) ... ok (3.84s)
test_pthread_cleanup (test_core.lsan) ... ok (3.32s)
test_pthread_abort (test_core.lsan) ... ok (3.00s)
test_pthread_atexit (test_core.lsan) ... ok (3.10s)
test_Module_dynamicLibraries_pthreads (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_c11_threads (test_core.lsan) ... ok (3.31s)
test_main_thread_em_asm_signatures (test_core.lsan) ... ok (2.03s)
test_atexit_threads (test_core.lsan) ... ok (1.25s)
test_main_thread_em_asm (test_core.lsan) ... ok (3.28s)
test_main_thread_async_em_asm (test_core.lsan) ... ok (2.44s)

DONE: combining results on main thread

test_Module_dynamicLibraries_pthreads (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_atexit_threads (test_core.lsan) ... ok
test_main_thread_async_em_asm (test_core.lsan) ... ok
test_main_thread_em_asm (test_core.lsan) ... ok
test_main_thread_em_asm_signatures (test_core.lsan) ... ok
test_pthread_abort (test_core.lsan) ... ok
test_pthread_atexit (test_core.lsan) ... ok
test_pthread_c11_threads (test_core.lsan) ... ok
test_pthread_cleanup (test_core.lsan) ... ok
test_pthread_create (test_core.lsan) ... ok
test_pthread_create_embind_stack_check (test_core.lsan) ... ok
test_pthread_create_pool (test_core.lsan) ... ok
test_pthread_create_proxy (test_core.lsan) ... ok
test_pthread_cxx_threads (test_core.lsan) ... ok
test_pthread_dispatch_after_exit (test_core.lsan) ... ok
test_pthread_dylink (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_basics (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_dylink_tls (test_core.lsan) ... skipped 'no dynamic linking support in LSan yet'
test_pthread_emmalloc (test_core.lsan) ... skipped 'LSan does not support custom memory allocators'
test_pthread_equal (test_core.lsan) ... ok
test_pthread_exceptions (test_core.lsan) ... ok
test_pthread_exit_process (test_core.lsan) ... ok
test_pthread_nested_work_queue (test_core.lsan) ... ok
test_pthread_no_exit_process (test_core.lsan) ... skipped 'https://github.com/emscripten-core/emscripten/issues/12945'
test_pthread_offset_converter (test_core.lsan) ... ok
test_pthread_offset_converter_modularize (test_core.lsan) ... ok
test_pthread_setspecific_mainthread (test_core.lsan) ... ok
test_pthread_specific (test_core.lsan) ... FAIL
test_pthread_thread_local_storage (test_core.lsan) ... ok
test_safe_stack_pthread (test_core.lsan) ... ok

======================================================================
FAIL: test_pthread_specific (test_core.lsan)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/usr/lib64/python3.9/unittest/case.py", line 593, in run
    self._callTestMethod(testMethod)
  File "/usr/lib64/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/home/kleisauke/emscripten/tests/test_core.py", line 207, in decorated
    f(self, *args, **kwargs)
  File "/home/kleisauke/emscripten/tests/test_core.py", line 2316, in test_pthread_specific
    self.do_run_in_out_file_test('pthread/specific.c')
  File "/home/kleisauke/emscripten/tests/common.py", line 1018, in do_run_in_out_file_test
    self._build_and_run(srcfile, expected, **kwargs)
  File "/home/kleisauke/emscripten/tests/common.py", line 1061, in _build_and_run
    js_output = self.run_js(js_file, engine, args,
  File "/home/kleisauke/emscripten/tests/common.py", line 681, in run_js
    self.fail('JS subprocess failed (%s): %s.  Output:\n%s' % (error.cmd, error.returncode, ret))
  File "/usr/lib64/python3.9/unittest/case.py", line 670, in fail
    raise self.failureException(msg)
AssertionError: JS subprocess failed (/home/kleisauke/emsdk/node/14.15.5_64bit/bin/node --stack-trace-limit=50 --unhandled-rejections=throw --trace-uncaught /tmp/tmp5tlxus5x/emscripten_test_lsan_uumnnos8/specific.js): 1.  Output:
pthread_key_create = 0
pthread_setspecific = 0
pthread_getspecific = 123
valid pthread_setspecific for value NULL = 0
pthread_getspecific = 0
pthread_getspecific after key recreate = 0
pthread_key_delete = 0
pthread_key_delete repeated = 28
pthread_setspecific for value NULL = 28
pthread_key_delete just after created = 0
exception thrown: RuntimeError: memory access out of bounds


----------------------------------------------------------------------
Ran 30 tests in 14.655s

FAILED (failures=1, skipped=6)

Note that test_pthread_specific is excluded for ASan, so perhaps something similar should be done for LSan as well:

@no_asan('test relies on null pointer reads')

@sbc100
Copy link
Collaborator

sbc100 commented Sep 10, 2021

Oh sorry I didn't mean to send you on an lsan fixing adventure, I had assumed that lsan tests were previously passing but its only asan I guess that we have coverage for.

I think this lsan work is good to have but maybe in separate PR?

@kleisauke
Copy link
Collaborator Author

No problem, it would indeed be better to resolve that in a separate PR. I'll try to open a PR for that next week.

I've reverted most of those changes for now, except for the change that regressed the lsan.test_pthread_create_proxy test case (i.e. the memalign -> emscripten_builtin_memalign change).

@sbc100
Copy link
Collaborator

sbc100 commented Sep 17, 2021

Can we rebase this and get it landed?

Although not defined by the POSIX standard, it seems that on most
systems, atexit handlers are run before threads are terminated.
@kleisauke
Copy link
Collaborator Author

Rebased as requested. Note that the memalign -> emscripten_builtin_memalign change could cause a failure in asan.test_pthread_cxx_threads, I noticed the same failure in PR #14912. See for example:
https://circleci.com/gh/emscripten-core/emscripten/454363 - commit 124726d
https://circleci.com/gh/emscripten-core/emscripten/450833 - commit 518c8f9

Should I investigate that failure further? Or do you think we should revert that change for now (resulting in a regression for the lsan.test_pthread_create_proxy test case)? I could also make this PR depend upon #14912, if that's easier.

src/postamble.js Outdated
@@ -452,9 +449,6 @@ function exit(status, implicit) {
function procExit(code) {
EXITSTATUS = code;
if (!keepRuntimeAlive()) {
#if USE_PTHREADS
PThread.terminateAllThreads();
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this terminateAllThreads is actually still useful for the case when _exit or _Exit is called with multiple threads running.

On node is probably doesn't matter since quit_t will bring down the whole process, but on the web it will mean we don't leak workers in this case.

This will always happen after any atexit processing has occurred.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought it was redundant to call terminateAllThreads here, since the only callers of procExit are:

procExit(status);

(which will be called just after exitRuntime, in case we don't need to keep the runtime alive)

And:

procExit(code);

(which will be only called by MINIMAL_RUNTIME and STANDALONE_WASM, AFAIK)

I added the terminateAllThreads in postamble_minimal.js to make sure that it still terminates all threads during exit when linking with -sMINIMAL_RUNTIME. But for STANDALONE_WASM pthreads are not supported at all, so this seemed redundant.

emscripten/emcc.py

Lines 2060 to 2061 in 565fb36

if settings.USE_PTHREADS:
exit_with_error('STANDALONE_WASM does not support pthreads yet')

Copy link
Collaborator

Choose a reason for hiding this comment

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

I thought it was redundant to call terminateAllThreads here, since the only callers of procExit are:

procExit(status);

(which will be called just after exitRuntime, in case we don't need to keep the runtime alive)
And:

procExit(code);

(which will be only called by MINIMAL_RUNTIME and STANDALONE_WASM, AFAIK)

This callsite is used if the user calls _exit or _Exit. Its basically the exit syscall. Its not just used in STANDALONE_WASM IIUC.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, yes. I missed this one:


(where __wasi_proc_exit is just an alias for proc_exit)

Commit ea90c63 fixes this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, this also seems to fix those asan.*thread* failures without having to change _free to _emscripten_builtin_free in PThread.freeThreadData. So, I've reverted that with commit 0f6f592.

@@ -2323,6 +2323,13 @@ def test_pthread_equal(self):
def test_pthread_dispatch_after_exit(self):
self.do_run_in_out_file_test('pthread/test_pthread_dispatch_after_exit.c', interleaved_output=False)

@node_pthreads
def test_pthread_atexit(self):
# Test whether we can terminate a running thread during atexit.
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about "Test that secondary threads are still running during atexit handlers"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've rephrased it as "Test to ensure threads are still running when atexit-registered functions are called" with commit ea90c63.

@sbc100
Copy link
Collaborator

sbc100 commented Sep 17, 2021

Rebased as requested. Note that the memalign -> emscripten_builtin_memalign change could cause a failure in asan.test_pthread_cxx_threads, I noticed the same failure in PR #14912. See for example:
https://circleci.com/gh/emscripten-core/emscripten/454363 - commit 124726d
https://circleci.com/gh/emscripten-core/emscripten/450833 - commit 518c8f9

Should I investigate that failure further? Or do you think we should revert that change for now (resulting in a regression for the lsan.test_pthread_create_proxy test case)? I could also make this PR depend upon #14912, if that's easier.

Yes, lets just revert that change and live with the lsan regression. It seems that we only currently run asan in CI so lsan regressions could be happening all the time without is knowing. I'm a little surprised that lsan can regress without asan also regressing though since asan (IIUC) includes lsan.

@sbc100
Copy link
Collaborator

sbc100 commented Sep 17, 2021

test_pthread_create_proxy

Oh.. I see the problem. When lsan we just link libc-mt.a (the normal libc) but for asan we build a special version (libc-mt-asan.a). This means that the libc we are using is compiled with HAS_SANITIZER being defined here:

#define HAS_SANITIZER

This seems like a pre-existing condition that we don't need to fix in this PR.

sbc100 added a commit that referenced this pull request Sep 17, 2021
This is second attempt at landing a version of
d5d5f69

The first time we tried it #13837 we ran into issues with
test_pthread_exit_process deadlocking which I tracked down to an issue
with `system/lib/libc/musl/src/thread/__wait.c` where it was blocking
the main thread forever rather then looping and calling
`emscripten_main_thread_process_queued_calls`.

Includes a version of #14481 so that should land before this does.

Fixes: #13194
@kleisauke
Copy link
Collaborator Author

Ah yes, you're right. I had forgotten about that and fixed that earlier in PR #13007. See commit dfd5717.

The memalign change was reverted with commit 240e07e.

The call to `PThread.terminateAllThreads()` in `procExit` that was
reintroduced made this unnecessary.
Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

Nice! So is this good to land now?

I left a couple of nits but otherwise looks good to me.

@@ -1 +1 @@
16368
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can this file be reverted now without causes a test failure? (looks like its within the tolerance maybe?).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It seems to be within the expected tolerance. I reverted it to its original state with commit 4f5be19.

pthread_mutex_lock(&count_lock);
if (count == 0)
pthread_cond_signal(&count_nonzero);
count++;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this can be simplified to just:

count = 1;
pthread_cond_signal(&count_nonzero);

I might even just rename int count to bool should_exit since we don't need to actually count anything.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Indeed, counting isn't necessary here (there are not multiple threads). Simplified with commit 4f5be19.

@kleisauke
Copy link
Collaborator Author

I think this is good now, feel free to land.

@sbc100 sbc100 merged commit 181cb72 into emscripten-core:main Sep 20, 2021
@kripken
Copy link
Member

kripken commented Sep 20, 2021

Great to see this land, thanks @kleisauke !

sbc100 added a commit that referenced this pull request Sep 20, 2021
This is second attempt at landing a version of
d5d5f69

The first time we tried it #13837 we ran into issues with
test_pthread_exit_process deadlocking which I tracked down to an issue
with `system/lib/libc/musl/src/thread/__wait.c` where it was blocking
the main thread forever rather then looping and calling
`emscripten_main_thread_process_queued_calls`.

Includes a version of #14481 so that should land before this does.

Fixes: #13194
sbc100 added a commit that referenced this pull request Sep 20, 2021
This is second attempt at landing a version of
d5d5f69

The first time we tried it #13837 we ran into issues with
test_pthread_exit_process deadlocking which I tracked down to an issue
with `system/lib/libc/musl/src/thread/__wait.c` where it was blocking
the main thread forever rather then looping and calling
`emscripten_main_thread_process_queued_calls`.

Includes a version of #14481 so that should land before this does.

Fixes: #13194
sbc100 added a commit that referenced this pull request Sep 20, 2021
This is second attempt at landing a version of
d5d5f69

The first time we tried it #13837 we ran into issues with
test_pthread_exit_process deadlocking which I tracked down to an issue
with `system/lib/libc/musl/src/thread/__wait.c` where it was blocking
the main thread forever rather then looping and calling
`emscripten_main_thread_process_queued_calls`.

Includes a version of #14481 so that should land before this does.

Fixes: #13194
@kleisauke kleisauke deleted the atexit-thread branch September 21, 2021 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

test_pthread_exit_process suspicious log message on Node
4 participants