Buckets:
| /* | |
| * 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. | |
| */ | |
| // Comment this line to enable tracing of thread creation and destruction: | |
| // #define PTHREAD_DEBUG | |
| // See musl's pthread_create.c | |
| static void dummy_0() {} | |
| weak_alias(dummy_0, __pthread_tsd_run_dtors); | |
| static void __run_cleanup_handlers() { | |
| 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; | |
| } | |
| void __do_cleanup_pop(struct __ptcb *cb) { | |
| __pthread_self()->cancelbuf = cb->__next; | |
| } | |
| static FILE *volatile dummy_file = 0; | |
| weak_alias(dummy_file, __stdin_used); | |
| weak_alias(dummy_file, __stdout_used); | |
| weak_alias(dummy_file, __stderr_used); | |
| static void init_file_lock(FILE *f) { | |
| if (f && f->lock<0) f->lock = 0; | |
| } | |
| static int tl_lock_count; | |
| static int tl_lock_waiters; | |
| volatile int __thread_list_lock; | |
| void __tl_lock(void) { | |
| int tid = __pthread_self()->tid; | |
| int val = __thread_list_lock; | |
| if (val == tid) { | |
| tl_lock_count++; | |
| return; | |
| } | |
| while ((val = a_cas(&__thread_list_lock, 0, tid))) | |
| __wait(&__thread_list_lock, &tl_lock_waiters, val, 0); | |
| } | |
| void __tl_unlock(void) { | |
| if (tl_lock_count) { | |
| tl_lock_count--; | |
| return; | |
| } | |
| a_store(&__thread_list_lock, 0); | |
| if (tl_lock_waiters) __wake(&__thread_list_lock, 1, 0); | |
| } | |
| void __tl_sync(pthread_t td) | |
| { | |
| a_barrier(); | |
| int val = __thread_list_lock; | |
| if (!val) return; | |
| __wait(&__thread_list_lock, &tl_lock_waiters, val, 0); | |
| if (tl_lock_waiters) __wake(&__thread_list_lock, 1, 0); | |
| } | |
| /* pthread_key_create.c overrides this */ | |
| static volatile size_t dummy = 0; | |
| weak_alias(dummy, __pthread_tsd_size); | |
| 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 do here should be considered leaks. | |
| // See: lsan_interceptors.cpp. | |
| if (!res) { | |
| return EINVAL; | |
| } | |
| if (!libc.threaded) { | |
| for (FILE *f=*__ofl_lock(); f; f=f->next) | |
| init_file_lock(f); | |
| __ofl_unlock(); | |
| init_file_lock(__stdin_used); | |
| init_file_lock(__stdout_used); | |
| init_file_lock(__stderr_used); | |
| libc.threaded = 1; | |
| } | |
| pthread_attr_t attr = { 0 }; | |
| if (attrp && attrp != __ATTRP_C11_THREAD) attr = *attrp; | |
| if (!attr._a_stacksize) { | |
| attr._a_stacksize = __default_stacksize; | |
| } | |
| // Allocate memory for new thread. The layout of the thread block is | |
| // as follows. From low to high address: | |
| // | |
| // 1. pthread struct (sizeof struct pthread) | |
| // 2. tls data (__builtin_wasm_tls_size()) | |
| // 3. tsd pointers (__pthread_tsd_size) | |
| // 4. stack (__default_stacksize AKA -sDEFAULT_PTHREAD_STACK_SIZE) | |
| size_t size = sizeof(struct pthread); | |
| if (__builtin_wasm_tls_size()) { | |
| size += __builtin_wasm_tls_size() + __builtin_wasm_tls_align() - 1; | |
| } | |
| size += __pthread_tsd_size + TSD_ALIGN - 1; | |
| size_t zero_size = size; | |
| if (!attr._a_stackaddr) { | |
| size += attr._a_stacksize + STACK_ALIGN - 1; | |
| } | |
| // Allocate all the data for the new thread and zero-initialize all parts | |
| // except for the stack. | |
| unsigned char* block = emscripten_builtin_malloc(size); | |
| memset(block, 0, zero_size); | |
| uintptr_t offset = (uintptr_t)block; | |
| // 1. pthread struct | |
| struct pthread *new = (struct pthread*)offset; | |
| offset += sizeof(struct pthread); | |
| new->map_base = block; | |
| new->map_size = size; | |
| // 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 = _emscripten_get_next_tid(); | |
| // pthread struct robust_list head should point to itself. | |
| new->robust_list.head = &new->robust_list.head; | |
| if (attr._a_detach) { | |
| new->detach_state = DT_DETACHED; | |
| } else { | |
| new->detach_state = DT_JOINABLE; | |
| } | |
| new->stack_size = attr._a_stacksize; | |
| // 2. tls data | |
| if (__builtin_wasm_tls_size()) { | |
| offset = ROUND_UP(offset, __builtin_wasm_tls_align()); | |
| new->tls_base = (void*)offset; | |
| offset += __builtin_wasm_tls_size(); | |
| } | |
| // 3. tsd slots | |
| if (__pthread_tsd_size) { | |
| offset = ROUND_UP(offset, TSD_ALIGN); | |
| new->tsd = (void*)offset; | |
| offset += __pthread_tsd_size; | |
| } | |
| // 4. stack data | |
| // musl stores top of the stack in pthread_t->stack (i.e. the high | |
| // end from which it grows down). | |
| if (attr._a_stackaddr) { | |
| new->stack = (void*)attr._a_stackaddr; | |
| } else { | |
| offset = ROUND_UP(offset + new->stack_size, STACK_ALIGN); | |
| new->stack = (void*)offset; | |
| } | |
| // Check that we didn't use more data than we allocated. | |
| assert(offset < (uintptr_t)block + size); | |
| _emscripten_thread_profiler_init(new); | |
| _emscripten_thread_mailbox_init(new); | |
| struct pthread *self = __pthread_self(); | |
| dbg("start __pthread_create: new=%p new_end=%p stack=%p->%p " | |
| "stack_size=%zu tls_base=%p", | |
| new, | |
| new + 1, | |
| (char*)new->stack - new->stack_size, | |
| new->stack, | |
| new->stack_size, | |
| new->tls_base); | |
| // thread may already be running/exited after the _pthread_create_js call below | |
| __tl_lock(); | |
| new->next = self->next; | |
| new->prev = self; | |
| new->next->prev = new; | |
| new->prev->next = new; | |
| __tl_unlock(); | |
| // Set libc.need_locks before calling __pthread_create_js since | |
| // by the time it returns the thread could be running and we | |
| // want libc.need_locks to be set from the moment it starts. | |
| if (!libc.threads_minus_1++) libc.need_locks = 1; | |
| // Assign the pthread_t object over immediately, so that by the time pthread_create_js() | |
| // is dispatched to a pthread and the pthread main runs, the value will be visible to | |
| // the thread to examine. | |
| // Use __atomic_store_n() instead of a_store() to avoid splicing the pointer. | |
| __atomic_store_n(res, new, __ATOMIC_SEQ_CST); | |
| int rtn = __pthread_create_js(new, &attr, entry, arg); | |
| if (rtn != 0) { | |
| // Reset the pthread_t return value to zero (we assigned to it above, | |
| // so by clearing it here we won't litter bits to caller) | |
| __atomic_store_n(res, 0, __ATOMIC_SEQ_CST); | |
| if (!--libc.threads_minus_1) libc.need_locks = 0; | |
| // undo previous addition to the thread list | |
| __tl_lock(); | |
| new->next->prev = new->prev; | |
| new->prev->next = new->next; | |
| new->next = new->prev = new; | |
| __tl_unlock(); | |
| return rtn; | |
| } | |
| dbg("done __pthread_create next=%p prev=%p new=%p", | |
| self->next, | |
| self->prev, | |
| new); | |
| return 0; | |
| } | |
| /* | |
| * Called from JS main thread to free data associated with a thread | |
| * that is no longer running. | |
| */ | |
| void _emscripten_thread_free_data(pthread_t t) { | |
| // A thread can never free its own thread data. | |
| assert(t != pthread_self()); | |
| if (t->profilerBlock) { | |
| emscripten_builtin_free(t->profilerBlock); | |
| } | |
| // Free all the entire thread block (called map_base because | |
| // musl normally allocates this using mmap). This region | |
| // includes the pthread structure itself. | |
| unsigned char* block = t->map_base; | |
| dbg("_emscripten_thread_free_data thread=%p map_base=%p", t, block); | |
| // To aid in debugging, set the entire region to zero. | |
| memset(block, 0, sizeof(struct pthread)); | |
| emscripten_builtin_free(block); | |
| } | |
| void _emscripten_thread_exit(void* result) { | |
| struct pthread *self = __pthread_self(); | |
| assert(self); | |
| self->canceldisable = PTHREAD_CANCEL_DISABLE; | |
| self->cancelasync = 0; | |
| self->result = result; | |
| _emscripten_thread_mailbox_shutdown(self); | |
| // Run any handlers registered with pthread_cleanup_push | |
| __run_cleanup_handlers(); | |
| // Call into the musl function that runs destructors of all thread-specific data. | |
| __pthread_tsd_run_dtors(); | |
| __tl_lock(); | |
| /* Process robust list in userspace to handle non-pshared mutexes | |
| * and the detached thread case where the robust list head will | |
| * be invalid when the kernel would process it. */ | |
| __vm_lock(); | |
| volatile void *volatile *rp; | |
| while ((rp=self->robust_list.head) && rp != &self->robust_list.head) { | |
| pthread_mutex_t *m = (void *)((char *)rp | |
| - offsetof(pthread_mutex_t, _m_next)); | |
| int waiters = m->_m_waiters; | |
| int priv = (m->_m_type & 128) ^ 128; | |
| self->robust_list.pending = rp; | |
| self->robust_list.head = *rp; | |
| int cont = a_swap(&m->_m_lock, 0x40000000); | |
| self->robust_list.pending = 0; | |
| if (cont < 0 || waiters) | |
| __wake(&m->_m_lock, 1, priv); | |
| } | |
| __vm_unlock(); | |
| if (!--libc.threads_minus_1) libc.need_locks = 0; | |
| self->next->prev = self->prev; | |
| self->prev->next = self->next; | |
| self->prev = self->next = self; | |
| __tl_unlock(); | |
| if (emscripten_is_main_runtime_thread()) { | |
| exit(0); | |
| return; | |
| } | |
| // Not hosting a pthread anymore in this worker set __pthread_self to NULL | |
| __set_thread_state(NULL, 0, 0, 1); | |
| /* This atomic potentially competes with a concurrent pthread_detach | |
| * call; the loser is responsible for freeing thread resources. */ | |
| int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING); | |
| if (state == DT_DETACHED) { | |
| _emscripten_thread_cleanup(self); | |
| } else { | |
| // Mark the thread as no longer running, so it can be joined. | |
| // Once we publish this, any threads that are waiting to join with us can | |
| // proceed and this worker can be recycled and used on another thread. | |
| // When dynamic linking is enabled we need to keep track of zombie threads | |
| _emscripten_thread_exit_joinable(self); | |
| a_store(&self->detach_state, DT_EXITED); | |
| __wake(&self->detach_state, 1, 1); // Wake any joiner. | |
| } | |
| } | |
| // 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, emscripten_builtin_pthread_exit); | |
| weak_alias(__pthread_exit, pthread_exit); | |
Xet Storage Details
- Size:
- 11.2 kB
- Xet hash:
- dbae8e865a26ba62b56d9c0d8474ab8b84287bf2c72ab7e1ec6bbaaccda67c0d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.