Skip to content

Commit

Permalink
Ensure Node worker threads are exited gracefully
Browse files Browse the repository at this point in the history
Resolves (partially): emscripten-core#9763.
  • Loading branch information
kleisauke committed Jan 12, 2021
1 parent c168284 commit 38b4bde
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 29 deletions.
9 changes: 0 additions & 9 deletions src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,6 @@ var LibraryPThread = {
worker.on('error', function(data) {
worker.onerror(data);
});
worker.on('exit', function(data) {
// TODO: update the worker queue?
// See: https://github.com/emscripten-core/emscripten/issues/9763
});
}
#endif

Expand Down Expand Up @@ -958,11 +954,6 @@ var LibraryPThread = {
if (!ENVIRONMENT_IS_PTHREAD) _exit(status);
else PThread.threadExit(status);
// pthread_exit is marked noReturn, so we must not return from it.
if (ENVIRONMENT_IS_NODE) {
// exit the pthread properly on node, as a normal JS exception will halt
// the entire application.
process.exit(status);
}
throw 'unwind';
},
Expand Down
45 changes: 45 additions & 0 deletions tests/core/pthread/test_pthread_exit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>

/* This test aims to check the following assertion:
*
* pthread_exit() terminates the current thread and
* allows a call to pthread_join() on this thread retrieve
* the argument (if the thread is not detached).
*
* We do this twice to ensure that the thread has exited
* gracefully and that we are able to recreate the thread.
*/

pthread_t t;
static int thread_state;

#define NUM_THREADS 2

#define NOT_CREATED_THREAD 1
#define EXITING_THREAD 2

void* thread_exit(void* arg) {
printf("calling pthread_exit\n");
thread_state = EXITING_THREAD;
pthread_exit(arg);
printf("pthread_exit() did not terminate the thread\n");
return NULL;
}

int main() {
printf("main\n");
for (int i = 0; i < NUM_THREADS; ++i) {
thread_state = NOT_CREATED_THREAD;
int rc = pthread_create(&t, NULL, thread_exit, (void*)42);
assert(rc == 0);
void* thread_rtn = 0;
rc = pthread_join(t, &thread_rtn);
assert(rc == 0);
assert(thread_state == EXITING_THREAD);
printf("done join -- thread exited with %ld\n", (intptr_t)thread_rtn);
}
return 0;
}
5 changes: 5 additions & 0 deletions tests/core/pthread/test_pthread_exit.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
main
calling pthread_exit
done join -- thread exited with 42
calling pthread_exit
done join -- thread exited with 42
14 changes: 14 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8072,6 +8072,14 @@ def test_pthread_create(self):
self.set_setting('EXIT_RUNTIME')
self.do_run_in_out_file_test('tests', 'core', 'pthread', 'create.cpp')

@node_pthreads
def test_pthread_create_2(self):
self.set_setting('PTHREAD_POOL_SIZE', '8')
self.set_setting('EXIT_RUNTIME')
if not self.has_changed_setting('INITIAL_MEMORY'):
self.set_setting('INITIAL_MEMORY', '64mb')
self.do_runf(path_from_root('tests', 'pthread', 'test_pthread_create.cpp'), '')

@node_pthreads
def test_pthread_c11_threads(self):
self.set_setting('PROXY_TO_PTHREAD')
Expand Down Expand Up @@ -8107,6 +8115,12 @@ def test_pthread_exceptions(self):
self.emcc_args += ['-fexceptions']
self.do_run_in_out_file_test('tests', 'core', 'pthread', 'exceptions.cpp')

@node_pthreads
def test_pthread_exit(self):
self.set_setting('PTHREAD_POOL_SIZE', '2')
self.set_setting('EXIT_RUNTIME')
self.do_run_in_out_file_test('tests', 'core', 'pthread', 'test_pthread_exit.c')

@node_pthreads
def test_pthread_exit_process(self):
self.set_setting('PROXY_TO_PTHREAD')
Expand Down
21 changes: 1 addition & 20 deletions tests/test_posixtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,29 +139,10 @@ def get_pthread_tests():
disabled = {
**unsupported,
'test_pthread_create_11_1': 'never returns',
'test_pthread_barrier_wait_2_1': 'never returns',
'test_pthread_cond_timedwait_2_6': 'never returns',
'test_pthread_cond_timedwait_4_3': 'never returns',
'test_pthread_attr_setscope_5_1': 'internally skipped (PTS_UNTESTED)',
'test_pthread_cond_wait_2_3': 'never returns',
'test_pthread_create_5_1': 'never returns',
'test_pthread_exit_1_2': 'never returns',
'test_pthread_exit_2_2': 'never returns',
'test_pthread_exit_3_2': 'never returns',
'test_pthread_exit_4_1': 'never returns',
'test_pthread_getcpuclockid_1_1': 'never returns',
'test_pthread_key_create_1_2': 'never returns',
'test_pthread_rwlock_rdlock_1_1': 'fails with "main: Unexpected thread state"',
'test_pthread_rwlock_timedrdlock_1_1': 'fails with "main: Unexpected thread state"',
'test_pthread_rwlock_timedrdlock_3_1': 'fails with "main: Unexpected thread state"',
'test_pthread_rwlock_timedrdlock_5_1': 'fails with "main: Unexpected thread state"',
'test_pthread_rwlock_timedwrlock_1_1': 'fails with "main: Unexpected thread state"',
'test_pthread_rwlock_timedwrlock_3_1': 'fails with "main: Unexpected thread state"',
'test_pthread_rwlock_timedwrlock_5_1': 'fails with "main: Unexpected thread state"',
'test_pthread_rwlock_wrlock_1_1': 'fails with "main: Unexpected thread state"',
'test_pthread_rwlock_trywrlock_1_1': 'fails with "main: Unexpected thread state"',
'test_pthread_spin_destroy_3_1': 'never returns',
'test_pthread_spin_init_4_1': 'never returns',
}


Expand All @@ -176,7 +157,7 @@ def f(self):
'-Wno-int-conversion',
'-sUSE_PTHREADS',
'-sEXIT_RUNTIME',
'-sTOTAL_MEMORY=268435456',
'-sTOTAL_MEMORY=256mb',
'-sPTHREAD_POOL_SIZE=40']
if browser:
# Only are only needed for browser tests of the was btest
Expand Down

0 comments on commit 38b4bde

Please sign in to comment.