blob: 364ce7794c0112a5ceae5cbb03bb2d78f475c148 [file] [log] [blame] [edit]
/*
* Copyright 2021 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/
#define _GNU_SOURCE
#include "pthread_impl.h"
#include "assert.h"
#include <pthread.h>
#include <stdbool.h>
#include <threads.h>
// See musl's pthread_create.c
extern int __cxa_thread_atexit(void (*)(void *), void *, void *);
extern int __pthread_create_js(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
extern void _emscripten_thread_init(int, int, int);
extern void __pthread_exit_run_handlers();
extern void __pthread_exit_done();
extern int8_t __dso_handle;
static void dummy_0()
{
}
weak_alias(dummy_0, __pthread_tsd_run_dtors);
void __run_cleanup_handlers(void* _unused) {
pthread_t self = __pthread_self();
while (self->cancelbuf) {
void (*f)(void *) = self->cancelbuf->__f;
void *x = self->cancelbuf->__x;
self->cancelbuf = self->cancelbuf->__next;
f(x);
}
}
void __do_cleanup_push(struct __ptcb *cb) {
struct pthread *self = __pthread_self();
cb->__next = self->cancelbuf;
self->cancelbuf = cb;
static thread_local bool registered = false;
if (!registered) {
__cxa_thread_atexit(__run_cleanup_handlers, NULL, &__dso_handle);
registered = true;
}
}
void __do_cleanup_pop(struct __ptcb *cb) {
__pthread_self()->cancelbuf = cb->__next;
}
int __pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) {
return __pthread_create_js(thread, attr, start_routine, arg);
}
void _emscripten_thread_exit(void* result) {
struct pthread *self = __pthread_self();
assert(self);
self->canceldisable = PTHREAD_CANCEL_DISABLE;
self->cancelasync = PTHREAD_CANCEL_DEFERRED;
self->result = result;
__pthread_exit_run_handlers();
// Call into the musl function that runs destructors of all thread-specific data.
__pthread_tsd_run_dtors();
if (self == emscripten_main_browser_thread_id()) {
// FIXME(sbc): When pthread_exit causes the entire application to exit
// we should be returning zero (according to the man page for pthread_exit).
exit((intptr_t)result);
return;
}
// 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.
self->threadStatus = 1;
emscripten_futex_wake(&self->threadStatus, INT_MAX); // wake all threads
// Not hosting a pthread anymore in this worker, reset the info structures to null.
_emscripten_thread_init(0, 0, 0); // Unregister the thread block inside the wasm module.
__pthread_exit_done();
}
// Mark as `no_sanitize("address"` since emscripten_pthread_exit destroys
// the current thread and runs its exit handlers. Without this asan injects
// a call to __asan_handle_no_return before emscripten_unwind_to_js_event_loop
// which seem to cause a crash later down the line.
__attribute__((no_sanitize("address")))
_Noreturn void __pthread_exit(void* retval) {
_emscripten_thread_exit(retval);
emscripten_unwind_to_js_event_loop();
}
weak_alias(__pthread_create, emscripten_builtin_pthread_create);
weak_alias(__pthread_create, pthread_create);
weak_alias(__pthread_exit, pthread_exit);