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) {
if (!pthread) return;
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
var profilerBlock = {{{ makeGetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 'i32') }}};
{{{ makeSetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.profilerBlock, 0, 'i32') }}};
@ -479,7 +476,6 @@ var LibraryPThread = {
pthread.worker.postMessage({ 'cmd': 'cancel' });
},
$spawnThread__deps: ['$zeroMemory'],
$spawnThread: function(threadParams) {
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!';
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;
// 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
// 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.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 }}} >> 2), stackHigh);
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 }}} + 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
PThread.createProfilerBlock(pthread.threadInfoStruct);
#endif
@ -565,7 +551,7 @@ var LibraryPThread = {
#endif
return navigator['hardwareConcurrency'];
},
__pthread_create_js__sig: 'iiiii',
__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) {
@ -573,10 +559,6 @@ var LibraryPThread = {
err('Current environment does not support SharedArrayBuffer, pthreads are not available!');
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
var transferList = [];
@ -729,26 +711,12 @@ var LibraryPThread = {
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
// Register for each of the transferred canvases that the new thread now
// owns the OffscreenCanvas.
for (var i in offscreenCanvases) {
// 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
@ -758,7 +726,7 @@ var LibraryPThread = {
allocatedOwnStack: allocatedOwnStack,
detached: detached,
startRoutine: start_routine,
pthread_ptr: threadInfoStruct,
pthread_ptr: pthread_ptr,
arg: arg,
#if OFFSCREENCANVAS_SUPPORT
moduleCanvasId: moduleCanvasId,
@ -964,7 +932,7 @@ var LibraryPThread = {
__cxa_thread_atexit__sig: 'vii',
__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',

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

@ -1,7 +1,14 @@
#include "pthread_impl.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);
#endif
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 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 __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
#define HAS_SANITIZER
#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
@ -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
// disabled. This makes it necessary for us to disable LSan here, so that it does not detect
// pthread's internal allocations as leaks.
// If/when we remove all the allocations from __pthread_create_js we could also remove this.
__lsan_disable();
#endif
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();
#else
q->returnValue.i =
pthread_create(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp);
#endif
break;
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
// a magic ID to detect whether the pthread_t structure is 'alive'.
__main_pthread.self = &__main_pthread;
// pthread struct robust_list head should point to itself.
__main_pthread.robust_list.head = &__main_pthread.robust_list;
// pthread struct robust_list head should point to itself.
__main_pthread.robust_list.head = &__main_pthread.robust_list.head;
// Main thread ID.
__main_pthread.tid = (long)&__main_pthread;

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

@ -10,12 +10,14 @@
#include "assert.h"
#include <pthread.h>
#include <stdbool.h>
#include <string.h>
#include <threads.h>
#include <emscripten/emmalloc.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 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 __pthread_exit_run_handlers();
extern void __pthread_exit_done();
@ -51,8 +53,35 @@ 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);
int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict 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) {
@ -75,6 +104,11 @@ void _emscripten_thread_exit(void* result) {
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.
// When we publish this, the main thread is free to deallocate the thread object and we are done.
self->threadStatus = 1;

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

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

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

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