Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 3 additions & 44 deletions src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -879,42 +879,14 @@ var LibraryPThread = {
#endif
},

_emscripten_do_pthread_join__deps: ['$cleanupThread', 'pthread_testcancel', 'emscripten_main_thread_process_queued_calls', 'emscripten_futex_wait', 'pthread_self', 'emscripten_main_browser_thread_id',
__pthread_join_js__deps: ['$cleanupThread', 'pthread_testcancel', 'emscripten_main_thread_process_queued_calls', 'emscripten_futex_wait', 'pthread_self', 'emscripten_main_browser_thread_id',
#if ASSERTIONS || IN_TEST_HARNESS || !MINIMAL_RUNTIME || !ALLOW_BLOCKING_ON_MAIN_THREAD
'emscripten_check_blocking_allowed'
#endif
],
_emscripten_do_pthread_join: function(thread, status, block) {
if (!thread) {
err('pthread_join attempted on a null thread pointer!');
return {{{ cDefine('ESRCH') }}};
}
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
if (self !== thread) {
err('pthread_join attempted on thread 0x' + thread.toString(16) + ', which does not point to a valid thread, or does not exist anymore!');
return {{{ cDefine('ESRCH') }}};
}
var detach_state = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.detach_state }}}) >> 2);
if (detach_state == {{{ cDefine('DT_DETACHED') }}}) {
err('Attempted to join thread 0x' + thread.toString(16) + ', which was already detached!');
return {{{ cDefine('EINVAL') }}}; // The thread is already detached, can no longer join it!
}

if (detach_state == {{{ cDefine('DT_EXITED') }}}) {
err('Attempted to join thread 0x' + thread.toString(16) + ', which was already joined!');
return {{{ cDefine('EINVAL') }}};
}
if (ENVIRONMENT_IS_PTHREAD && _pthread_self() == thread) {
err('PThread ' + thread + ' is attempting to join to itself!');
return {{{ cDefine('EDEADLK') }}};
}
else if (!ENVIRONMENT_IS_PTHREAD && _emscripten_main_browser_thread_id() == thread) {
err('Main thread ' + thread + ' is attempting to join to itself!');
return {{{ cDefine('EDEADLK') }}};
}

__pthread_join_js: function(thread, status, tryjoin) {
#if ASSERTIONS || IN_TEST_HARNESS || !MINIMAL_RUNTIME || !ALLOW_BLOCKING_ON_MAIN_THREAD
if (block) {
if (!tryjoin) {
_emscripten_check_blocking_allowed();
}
#endif
Expand All @@ -932,9 +904,6 @@ var LibraryPThread = {
else postMessage({ 'cmd': 'cleanupThread', 'thread': thread });
return 0;
}
if (!block) {
return {{{ cDefine('EBUSY') }}};
}
_pthread_testcancel();
// In main runtime thread (the thread that initialized the Emscripten C
// runtime and launched main()), assist pthreads in performing operations
Expand All @@ -944,16 +913,6 @@ var LibraryPThread = {
}
},

__pthread_join_js__deps: ['_emscripten_do_pthread_join'],
__pthread_join_js: function(thread, status) {
return __emscripten_do_pthread_join(thread, status, true);
},

pthread_tryjoin_np__deps: ['_emscripten_do_pthread_join'],
pthread_tryjoin_np: function(thread, status) {
return __emscripten_do_pthread_join(thread, status, false);
},

pthread_kill__deps: ['$killThread', 'emscripten_main_browser_thread_id'],
pthread_kill: function(thread, signal) {
if (signal < 0 || signal >= 65/*_NSIG*/) return {{{ cDefine('EINVAL') }}};
Expand Down
36 changes: 33 additions & 3 deletions system/lib/pthread/pthread_join.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,39 @@
#include "pthread_impl.h"
#include <pthread.h>

extern int __pthread_join_js(pthread_t thread, void **retval);
int __pthread_join(pthread_t thread, void **retval) {
return __pthread_join_js(thread, retval);
extern int __pthread_join_js(pthread_t t, void **res, int tryjoin);

static int __pthread_join_internal(pthread_t t, void **res, int tryjoin) {
if (t->self != t) {
// attempt to join a thread which does not point to a valid thread, or does
// not exist anymore.
return ESRCH;
}
// TODO(sbc): IIUC __pthread_join_js currently doesn't handle the case
// when the thread becomes detached/joined *during* the join. This pre-check
// can potentially be removed once it does.
int state = t->detach_state;
if (state == DT_DETACHED || state == DT_EXITED) {
// The thread is detached or already joined, and therefore not joinable
return EINVAL;
}
if (t == __pthread_self()) {
// thread is attempting to join to itself.
return EDEADLK;
}
return __pthread_join_js(t, res, tryjoin);
}

int __pthread_join(pthread_t t, void **res) {
return __pthread_join_internal(t, res, 0);
}

// Taken directly from system/lib/libc/musl/src/thread/pthread_join.c
int __pthread_tryjoin_np(pthread_t t, void **res)
{
return t->detach_state==DT_JOINABLE ? EBUSY : __pthread_join_internal(t, res, 1);
}

weak_alias(__pthread_join, emscripten_builtin_pthread_join);
weak_alias(__pthread_tryjoin_np, pthread_tryjoin_np);
weak_alias(__pthread_join, pthread_join);
11 changes: 11 additions & 0 deletions tests/other/test_pthread_self_join_detach.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@
#include <stdio.h>
#include <string.h>

#ifdef __EMSCRIPTEN__
#include <emscripten/threading.h>
#endif

int main() {
/*
* When running in PROXY_TO_PTHREAD mode the main thread
* is already detached
*/
#ifdef __EMSCRIPTEN__
int is_detached = !emscripten_is_main_browser_thread();
#else
int is_detached = 0;
#endif
pthread_t self = pthread_self();

/*
Expand All @@ -20,6 +26,7 @@ int main() {
* detached
*/
int ret = pthread_join(self, NULL);
printf("pthread_join -> %s\n", strerror(ret));
if (is_detached) {
assert(ret == EINVAL);
} else {
Expand All @@ -38,6 +45,10 @@ int main() {
assert(ret == 0);
}

ret = pthread_join(self, NULL);
printf("pthread_join -> %s\n", strerror(ret));
assert(ret == EINVAL);

puts("passed");

return 0;
Expand Down