Move a part of pthread_create to native code. NFC (#14858)

This commit is contained in:
Sam Clegg 2021-08-13 13:12:15 -07:00 коммит произвёл GitHub
Родитель bc2263af6b
Коммит 7c8be3dab1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 56 добавлений и 50 удалений

Просмотреть файл

@ -164,9 +164,6 @@ var LibraryPThread = {
freeThreadData: function(pthread) { freeThreadData: function(pthread) {
if (!pthread) return; if (!pthread) return;
if (pthread.threadInfoStruct) { if (pthread.threadInfoStruct) {
var tlsMemory = {{{ makeGetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.tsd, 'i32') }}};
{{{ makeSetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.tsd, 0, 'i32') }}};
_free(tlsMemory);
#if PTHREADS_PROFILING #if PTHREADS_PROFILING
var profilerBlock = {{{ makeGetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 'i32') }}}; var profilerBlock = {{{ makeGetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 'i32') }}};
{{{ makeSetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 0, 'i32') }}}; {{{ makeSetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 0, 'i32') }}};
@ -479,7 +476,6 @@ var LibraryPThread = {
pthread.worker.postMessage({ 'cmd': 'cancel' }); pthread.worker.postMessage({ 'cmd': 'cancel' });
}, },
$spawnThread__deps: ['$zeroMemory'],
$spawnThread: function(threadParams) { $spawnThread: function(threadParams) {
if (ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! spawnThread() can only ever be called from main application thread!'; if (ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! spawnThread() can only ever be called from main application thread!';
@ -493,10 +489,6 @@ var LibraryPThread = {
if (!threadParams.pthread_ptr) throw 'Internal error, no pthread ptr!'; if (!threadParams.pthread_ptr) throw 'Internal error, no pthread ptr!';
PThread.runningWorkers.push(worker); PThread.runningWorkers.push(worker);
// Allocate memory for thread-local storage and initialize it to zero.
var tlsMemory = _malloc({{{ cDefine('PTHREAD_KEYS_MAX') * 4 }}});
zeroMemory(tlsMemory, {{{ cDefine('PTHREAD_KEYS_MAX') * 4 }}});
var stackHigh = threadParams.stackBase + threadParams.stackSize; var stackHigh = threadParams.stackBase + threadParams.stackSize;
// Create a pthread info object to represent this thread. // Create a pthread info object to represent this thread.
@ -512,18 +504,12 @@ var LibraryPThread = {
// spawnThread is always called with a zero-initialized thread struct so // spawnThread is always called with a zero-initialized thread struct so
// no need to set any valudes to zero here. // no need to set any valudes to zero here.
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.detached }}} >> 2), threadParams.detached); Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.detached }}} >> 2), threadParams.detached);
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.tsd }}} >> 2), tlsMemory); // Init thread-local-storage memory array.
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.tid }}} >> 2), pthread.threadInfoStruct); // Main thread ID.
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.stack_size }}} >> 2), threadParams.stackSize); Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.stack_size }}} >> 2), threadParams.stackSize);
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.stack }}} >> 2), stackHigh); Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.stack }}} >> 2), stackHigh);
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.attr }}} >> 2), threadParams.stackSize); Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.attr }}} >> 2), threadParams.stackSize);
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.attr }}} + 8 >> 2), stackHigh); Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.attr }}} + 8 >> 2), stackHigh);
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.attr }}} + 12 >> 2), threadParams.detached); Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.attr }}} + 12 >> 2), threadParams.detached);
var global_libc = _emscripten_get_global_libc();
var global_locale = global_libc + {{{ C_STRUCTS.libc.global_locale }}};
Atomics.store(HEAPU32, tis + ({{{ C_STRUCTS.pthread.locale }}} >> 2), global_locale);
#if PTHREADS_PROFILING #if PTHREADS_PROFILING
PThread.createProfilerBlock(pthread.threadInfoStruct); PThread.createProfilerBlock(pthread.threadInfoStruct);
#endif #endif
@ -565,7 +551,7 @@ var LibraryPThread = {
#endif #endif
return navigator['hardwareConcurrency']; return navigator['hardwareConcurrency'];
}, },
__pthread_create_js__sig: 'iiiii', __pthread_create_js__sig: 'iiiii',
__pthread_create_js__deps: ['$spawnThread', 'pthread_self', 'memalign', 'emscripten_sync_run_in_main_thread_4'], __pthread_create_js__deps: ['$spawnThread', 'pthread_self', 'memalign', 'emscripten_sync_run_in_main_thread_4'],
__pthread_create_js: function(pthread_ptr, attr, start_routine, arg) { __pthread_create_js: function(pthread_ptr, attr, start_routine, arg) {
@ -573,10 +559,6 @@ var LibraryPThread = {
err('Current environment does not support SharedArrayBuffer, pthreads are not available!'); err('Current environment does not support SharedArrayBuffer, pthreads are not available!');
return {{{ cDefine('EAGAIN') }}}; return {{{ cDefine('EAGAIN') }}};
} }
if (!pthread_ptr) {
err('pthread_create called with a null thread pointer!');
return {{{ cDefine('EINVAL') }}};
}
// List of JS objects that will transfer ownership to the Worker hosting the thread // List of JS objects that will transfer ownership to the Worker hosting the thread
var transferList = []; var transferList = [];
@ -729,26 +711,12 @@ var LibraryPThread = {
assert(stackBase > 0); assert(stackBase > 0);
} }
// Allocate thread block (pthread_t structure).
var threadInfoStruct = _malloc({{{ C_STRUCTS.pthread.__size__ }}});
// zero-initialize thread structure.
zeroMemory(threadInfoStruct, {{{ C_STRUCTS.pthread.__size__ }}});
{{{ makeSetValue('pthread_ptr', 0, 'threadInfoStruct', 'i32') }}};
// The pthread struct has a field that points to itself - this is used as a
// magic ID to detect whether the pthread_t structure is 'alive'.
{{{ makeSetValue('threadInfoStruct', C_STRUCTS.pthread.self, 'threadInfoStruct', 'i32') }}};
// pthread struct robust_list head should point to itself.
var headPtr = threadInfoStruct + {{{ C_STRUCTS.pthread.robust_list }}};
{{{ makeSetValue('headPtr', 0, 'headPtr', 'i32') }}};
#if OFFSCREENCANVAS_SUPPORT #if OFFSCREENCANVAS_SUPPORT
// Register for each of the transferred canvases that the new thread now // Register for each of the transferred canvases that the new thread now
// owns the OffscreenCanvas. // owns the OffscreenCanvas.
for (var i in offscreenCanvases) { for (var i in offscreenCanvases) {
// pthread ptr to the thread that owns this canvas. // pthread ptr to the thread that owns this canvas.
{{{ makeSetValue('offscreenCanvases[i].canvasSharedPtr', 8, 'threadInfoStruct', 'i32') }}}; {{{ makeSetValue('offscreenCanvases[i].canvasSharedPtr', 8, 'pthread_ptr', 'i32') }}};
} }
#endif #endif
@ -758,7 +726,7 @@ var LibraryPThread = {
allocatedOwnStack: allocatedOwnStack, allocatedOwnStack: allocatedOwnStack,
detached: detached, detached: detached,
startRoutine: start_routine, startRoutine: start_routine,
pthread_ptr: threadInfoStruct, pthread_ptr: pthread_ptr,
arg: arg, arg: arg,
#if OFFSCREENCANVAS_SUPPORT #if OFFSCREENCANVAS_SUPPORT
moduleCanvasId: moduleCanvasId, moduleCanvasId: moduleCanvasId,
@ -964,7 +932,7 @@ var LibraryPThread = {
__cxa_thread_atexit__sig: 'vii', __cxa_thread_atexit__sig: 'vii',
__cxa_thread_atexit: function(routine, arg) { __cxa_thread_atexit: function(routine, arg) {
PThread.threadExitHandlers.push(function() { {{{ makeDynCall('vi', 'routine') }}}(arg) }); PThread.threadExitHandlers.push(function() { {{{ makeDynCall('vi', 'routine') }}}(arg) });
}, },
__cxa_thread_atexit_impl: '__cxa_thread_atexit', __cxa_thread_atexit_impl: '__cxa_thread_atexit',

Просмотреть файл

@ -1,7 +1,14 @@
#include "pthread_impl.h" #include "pthread_impl.h"
#include <threads.h> #include <threads.h>
#ifdef __EMSCRIPTEN__
// Fix for lsan. Since lsan wraps calls to the public `pthread_create` function
// if we call the internal __pthread_create function here to don't the wrapping
// See pthread_create wrapper in compiler-rt/lib/lsan/lsan_interceptors.cpp.
#define __pthread_create pthread_create
#else
int __pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict); int __pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict);
#endif
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
{ {

Просмотреть файл

@ -156,13 +156,12 @@ void emscripten_async_waitable_close(em_queued_call* call) {
extern double emscripten_receive_on_main_thread_js(int functionIndex, int numCallArgs, double* args); extern double emscripten_receive_on_main_thread_js(int functionIndex, int numCallArgs, double* args);
extern int _emscripten_notify_thread_queue(pthread_t targetThreadId, pthread_t mainThreadId); extern int _emscripten_notify_thread_queue(pthread_t targetThreadId, pthread_t mainThreadId);
extern int __pthread_create_js(struct pthread *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
#if defined(__has_feature) #if defined(__has_feature)
#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer) #if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
#define HAS_SANITIZER #define HAS_SANITIZER
#include <sanitizer/lsan_interface.h> #include <sanitizer/lsan_interface.h>
int emscripten_builtin_pthread_create(pthread_t *thread, const pthread_attr_t* attr,
void *(*callback)(void *), void *arg);
#endif #endif
#endif #endif
@ -178,13 +177,13 @@ static void _do_call(em_queued_call* q) {
// On non-main threads, pthread_create gets proxied to the main thread, where LSan is not // On non-main threads, pthread_create gets proxied to the main thread, where LSan is not
// disabled. This makes it necessary for us to disable LSan here, so that it does not detect // disabled. This makes it necessary for us to disable LSan here, so that it does not detect
// pthread's internal allocations as leaks. // pthread's internal allocations as leaks.
// If/when we remove all the allocations from __pthread_create_js we could also remove this.
__lsan_disable(); __lsan_disable();
#endif
q->returnValue.i = q->returnValue.i =
emscripten_builtin_pthread_create(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp); __pthread_create_js(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp);
#ifdef HAS_SANITIZER
__lsan_enable(); __lsan_enable();
#else
q->returnValue.i =
pthread_create(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp);
#endif #endif
break; break;
case EM_PROXIED_CREATE_CONTEXT: case EM_PROXIED_CREATE_CONTEXT:
@ -892,8 +891,8 @@ void __emscripten_pthread_data_constructor(void) {
// The pthread struct has a field that points to itself - this is used as // The pthread struct has a field that points to itself - this is used as
// a magic ID to detect whether the pthread_t structure is 'alive'. // a magic ID to detect whether the pthread_t structure is 'alive'.
__main_pthread.self = &__main_pthread; __main_pthread.self = &__main_pthread;
// pthread struct robust_list head should point to itself. // pthread struct robust_list head should point to itself.
__main_pthread.robust_list.head = &__main_pthread.robust_list; __main_pthread.robust_list.head = &__main_pthread.robust_list.head;
// Main thread ID. // Main thread ID.
__main_pthread.tid = (long)&__main_pthread; __main_pthread.tid = (long)&__main_pthread;

Просмотреть файл

@ -10,12 +10,14 @@
#include "assert.h" #include "assert.h"
#include <pthread.h> #include <pthread.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include <threads.h> #include <threads.h>
#include <emscripten/emmalloc.h>
// See musl's pthread_create.c // See musl's pthread_create.c
extern int __cxa_thread_atexit(void (*)(void *), void *, void *); 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 int __pthread_create_js(struct pthread *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
extern void _emscripten_thread_init(int, int, int); extern void _emscripten_thread_init(int, int, int);
extern void __pthread_exit_run_handlers(); extern void __pthread_exit_run_handlers();
extern void __pthread_exit_done(); extern void __pthread_exit_done();
@ -51,8 +53,35 @@ void __do_cleanup_pop(struct __ptcb *cb) {
__pthread_self()->cancelbuf = cb->__next; __pthread_self()->cancelbuf = cb->__next;
} }
int __pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) { int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg) {
return __pthread_create_js(thread, attr, start_routine, arg); // Note on LSAN: lsan intercepts/wraps calls to pthread_create so any
// allocation we we do here should be considered leaks.
// See: lsan_interceptors.cpp.
if (!res) {
return EINVAL;
}
// Allocate thread block (pthread_t structure).
struct pthread *new = malloc(sizeof(struct pthread));
// zero-initialize thread structure.
memset(new, 0, sizeof(struct pthread));
// The pthread struct has a field that points to itself - this is used as a
// magic ID to detect whether the pthread_t structure is 'alive'.
new->self = new;
new->tid = (uintptr_t)new;
// pthread struct robust_list head should point to itself.
new->robust_list.head = &new->robust_list.head;
new->locale = &libc.global_locale;
// Allocate memory for thread-local storage and initialize it to zero.
new->tsd = malloc(PTHREAD_KEYS_MAX * sizeof(void*));
memset(new->tsd, 0, PTHREAD_KEYS_MAX * sizeof(void*));
*res = new;
return __pthread_create_js(new, attrp, entry, arg);
} }
void _emscripten_thread_exit(void* result) { void _emscripten_thread_exit(void* result) {
@ -75,6 +104,11 @@ void _emscripten_thread_exit(void* result) {
return; return;
} }
// We have the call the buildin free here since lsan handling for this thread
// gets shut down during __pthread_tsd_run_dtors.
emscripten_builtin_free(self->tsd);
self->tsd = NULL;
// Mark the thread as no longer running. // 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. // When we publish this, the main thread is free to deallocate the thread object and we are done.
self->threadStatus = 1; self->threadStatus = 1;

Просмотреть файл

@ -14,7 +14,6 @@ M
N N
O O
P P
Q
v v
w w
x x

Просмотреть файл

@ -1,7 +1,6 @@
$GetQueue $GetQueue
$__emscripten_pthread_data_constructor $__emscripten_pthread_data_constructor
$__errno_location $__errno_location
$__pthread_create
$__pthread_mutex_lock $__pthread_mutex_lock
$__pthread_mutex_trylock $__pthread_mutex_trylock
$__pthread_mutex_unlock $__pthread_mutex_unlock
@ -27,7 +26,6 @@ $em_queued_call_free
$em_queued_call_malloc $em_queued_call_malloc
$emscripten_async_run_in_main_thread $emscripten_async_run_in_main_thread
$emscripten_current_thread_process_queued_calls $emscripten_current_thread_process_queued_calls
$emscripten_get_global_libc
$emscripten_main_thread_process_queued_calls $emscripten_main_thread_process_queued_calls
$emscripten_proxy_main $emscripten_proxy_main
$emscripten_run_in_main_runtime_thread_js $emscripten_run_in_main_runtime_thread_js
@ -38,6 +36,7 @@ $emscripten_tls_init
$free_tls $free_tls
$init_mparams $init_mparams
$main $main
$memset
$sbrk $sbrk
$stackAlloc $stackAlloc
$stackRestore $stackRestore