Skip to content

Use pthread_join from musl #15604

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 2 commits into from
Jan 28, 2022
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
41 changes: 41 additions & 0 deletions system/lib/libc/musl/src/thread/pthread_join.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,75 @@ static void dummy1(pthread_t t)
}
weak_alias(dummy1, __tl_sync);

#ifdef __EMSCRIPTEN__ // XXX Emscripten add extern for __emscripten_thread_cleanup
extern void __emscripten_thread_cleanup(pthread_t thread);
#endif

static int __pthread_timedjoin_np(pthread_t t, void **res, const struct timespec *at)
{
#ifdef __EMSCRIPTEN__
// Attempt to join a thread which does not point to a valid thread, or
// does not exist anymore.
if (t->self != t) return ESRCH;
// Thread is attempting to join to itself. Already detached threads are
// handled below by returning EINVAL instead.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe mention that these extra checks are because we want to pass the posixtest suite? IIRC both these cases count as undefined behaviour so are not strictly necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do you mean the extra t->detach_state != DT_DETACHED check? AFAIK, that was needed for our test_main_pthread_join_detach and test_proxy_pthread_join_detach tests, and not needed for the posix tests.

I could also remove this check and update that test exception by partially reverting commit ca90a4b, if you want.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Lets leave that as a possible followup.

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 can confirm that the t->detach_state != DT_DETACHED check is only needed for the other.test_{proxy,main}_pthread_join_detach tests, see for e.g. work-in-progress commit 493f2e5 that updates its test asserts.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you want to block is this PR on that change or should we co ahead and land with a TODO here?

Copy link
Collaborator Author

@kleisauke kleisauke Jan 28, 2022

Choose a reason for hiding this comment

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

I added a TODO with commit 5e65f91, since removing this check would also mean that it causes an EDEADLK error when already-detached threads are attempting to detach itself (as pthread_detach re-uses code from pthread_join).

// TODO: The detached check here is just to satisfy the `other.test_{proxy,main}_pthread_join_detach` tests.
if (t->detach_state != DT_DETACHED && __pthread_self() == t) return EDEADLK;
#endif
int state, cs, r = 0;
__pthread_testcancel();
__pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
if (cs == PTHREAD_CANCEL_ENABLE) __pthread_setcancelstate(cs, 0);
while ((state = t->detach_state) && r != ETIMEDOUT && r != EINVAL) {
#ifdef __EMSCRIPTEN__
// The thread is (already) detached and therefore not joinable.
// This also handle cases where the thread becomes detached
// *during* the join.
if (state >= DT_DETACHED) {
// Even though the man page says this is undefined
// behaviour we ave several tests in the posixtest suite
// that depend on this.
r = EINVAL;
break;
}
#else
if (state >= DT_DETACHED) a_crash();
#endif
r = __timedwait_cp(&t->detach_state, state, CLOCK_REALTIME, at, 1);
}
__pthread_setcancelstate(cs, 0);
if (r == ETIMEDOUT || r == EINVAL) return r;
__tl_sync(t);
if (res) *res = t->result;
#ifdef __EMSCRIPTEN__
// Thread was exited during this call, be sure to clean it up.
if (state == DT_EXITED) __emscripten_thread_cleanup(t);
#else // XXX Emscripten map_base unused
if (t->map_base) __munmap(t->map_base, t->map_size);
#endif
return 0;
}

int __pthread_join(pthread_t t, void **res)
{
#ifdef __EMSCRIPTEN__ // XXX Emscripten check whether blocking is allowed.
emscripten_check_blocking_allowed();
#endif
return __pthread_timedjoin_np(t, res, 0);
}

static int __pthread_tryjoin_np(pthread_t t, void **res)
{
#ifdef __EMSCRIPTEN__ // XXX Emscripten call __pthread_timedjoin_np directly to avoid additional check
return t->detach_state==DT_JOINABLE ? EBUSY : __pthread_timedjoin_np(t, res, 0);
#else
return t->detach_state==DT_JOINABLE ? EBUSY : __pthread_join(t, res);
#endif
}

weak_alias(__pthread_tryjoin_np, pthread_tryjoin_np);
weak_alias(__pthread_timedjoin_np, pthread_timedjoin_np);
weak_alias(__pthread_join, pthread_join);
#ifdef __EMSCRIPTEN__ // XXX Emscripten add an extra alias for LSan
weak_alias(__pthread_join, emscripten_builtin_pthread_join);
#endif
12 changes: 5 additions & 7 deletions system/lib/pthread/pthread_create.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,16 +237,14 @@ void _emscripten_thread_exit(void* result) {
* call; the loser is responsible for freeing thread resources. */
int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING);

// Mark the thread as no longer running.
// When we publish this, the main thread is free to deallocate the thread
// object and we are done.
if (state == DT_DETACHED) {
self->detach_state = DT_EXITED;
__emscripten_thread_cleanup(self);
} else {
self->detach_state = DT_EXITING;
// wake any threads that might be waiting for us to exit
emscripten_futex_wake(&self->detach_state, INT_MAX);
// Mark the thread as no longer running, so it can be joined.
// Once we publish this, any threads that are waiting to join with us can
// proceed and this worker can be recycled and used on another thread.
a_store(&self->detach_state, DT_EXITED);
__wake(&self->detach_state, 1, 1); // Wake any joiner.
}
}

Expand Down
70 changes: 0 additions & 70 deletions system/lib/pthread/pthread_join.c

This file was deleted.

5 changes: 2 additions & 3 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -827,15 +827,14 @@ def get_files(self):
'syscall_cp.c', 'tls.c',
# TODO: Support this. See #12216.
'pthread_setname_np.c',
# TODO: These could be moved away from JS in the upcoming musl upgrade.
'pthread_join.c', 'pthread_testcancel.c',
# TODO: Prefer to use the musl implementation.
'pthread_testcancel.c',
]
libc_files += files_in_path(
path='system/lib/pthread',
filenames=[
'library_pthread.c',
'pthread_create.c',
'pthread_join.c',
'pthread_testcancel.c',
'emscripten_proxy_main.c',
'emscripten_thread_init.c',
Expand Down