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) {
|
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
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
|
@ -893,7 +892,7 @@ void __emscripten_pthread_data_constructor(void) {
|
||||||
// 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
|
||||||
|
|
Загрузка…
Ссылка в новой задаче