Don't run pthread exit handlers on application exit (#14751)

It should only be executed when a thread is exiting (or when
pthread_exit is explicitly called).

This reverts commit 6d3e78c partially.
diff --git a/src/library_pthread.js b/src/library_pthread.js
index 8fab9a3..4ad34b2 100644
--- a/src/library_pthread.js
+++ b/src/library_pthread.js
@@ -1034,8 +1034,10 @@
 
   __pthread_exit_js__deps: ['exit'],
   __pthread_exit_js: function(status) {
-    if (!ENVIRONMENT_IS_PTHREAD) _exit(status);
-    else PThread.threadExit(status);
+    if (!ENVIRONMENT_IS_PTHREAD) {
+      PThread.runExitHandlers();
+      _exit(status);
+    } else PThread.threadExit(status);
     // pthread_exit is marked noReturn, so we must not return from it.
     throw 'unwind';
   },
diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js
index 0906e77..b2e1389 100644
--- a/src/postamble_minimal.js
+++ b/src/postamble_minimal.js
@@ -24,9 +24,6 @@
   var ret = _main();
 
 #if EXIT_RUNTIME
-#if USE_PTHREADS
-  PThread.runExitHandlers();
-#endif
   callRuntimeCallbacks(__ATEXIT__);
   <<< ATEXITS >>>
 #endif
@@ -223,7 +220,7 @@
   initRuntime(asm);
 #if USE_PTHREADS && PTHREAD_POOL_SIZE
   if (!ENVIRONMENT_IS_PTHREAD) loadWasmModuleToWorkers();
-#if !PTHREAD_POOL_DELAY_LOAD  
+#if !PTHREAD_POOL_DELAY_LOAD
   else
 #endif
     ready();
diff --git a/src/preamble.js b/src/preamble.js
index 1ba0e06..7700b24 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -419,9 +419,6 @@
   checkStackCookie();
 #endif
 #if USE_PTHREADS
-#if EXIT_RUNTIME
-  PThread.runExitHandlers();
-#endif
   if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
 #endif
 #if EXIT_RUNTIME
diff --git a/tests/pthread/test_pthread_setspecific_mainthread.c b/tests/pthread/test_pthread_setspecific_mainthread.c
index bd6bf9f..350aec9 100644
--- a/tests/pthread/test_pthread_setspecific_mainthread.c
+++ b/tests/pthread/test_pthread_setspecific_mainthread.c
@@ -4,12 +4,19 @@
 // found in the LICENSE file.
 
 #include <assert.h>
-#include <stdio.h>
 #include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 void destructor(void* arg) {
+#if defined(RETURN) || defined(EXIT)
+  // The destructors for thread-specific data should only be executed
+  // when a thread exits or when pthread_exit is explicitly called.
+  assert(0 && "pthread key dtor should not be executed on application exit");
+#else
   printf("destructor: %ld\n", (long)arg);
   assert(arg == (void*)42);
+#endif
 }
 
 int main() {
@@ -20,5 +27,12 @@
   pthread_setspecific(key, (void*)42);
   val = pthread_getspecific(key);
   assert(val == (void*)42);
+  printf("done!\n");
+#ifdef RETURN
   return 0;
+#elif defined(EXIT)
+  exit(0);
+#else
+  pthread_exit(0);
+#endif
 }
diff --git a/tests/pthread/test_pthread_setspecific_mainthread.out b/tests/pthread/test_pthread_setspecific_mainthread.out
index 64ffb72..398e99f 100644
--- a/tests/pthread/test_pthread_setspecific_mainthread.out
+++ b/tests/pthread/test_pthread_setspecific_mainthread.out
@@ -1 +1,2 @@
+done!
 destructor: 42
diff --git a/tests/test_core.py b/tests/test_core.py
index d1b8fd6..0ff1a08 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -2335,6 +2335,11 @@
   @node_pthreads
   def test_pthread_setspecific_mainthread(self):
     self.set_setting('EXIT_RUNTIME')
+    print('.. return')
+    self.do_runf(test_file('pthread/test_pthread_setspecific_mainthread.c'), 'done!', emcc_args=['-DRETURN'])
+    print('.. exit')
+    self.do_runf(test_file('pthread/test_pthread_setspecific_mainthread.c'), 'done!', emcc_args=['-DEXIT'])
+    print('.. pthread_exit')
     self.do_run_in_out_file_test('pthread/test_pthread_setspecific_mainthread.c')
 
   @node_pthreads