Move a part of pthread_create to native code. NFC (#14858)
This commit is contained in:
Родитель
bc2263af6b
Коммит
7c8be3dab1
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче