+ if (kr != KERN_SUCCESS) {
+ kr = mach_vm_allocate(mach_task_self(), &allocaddr, allocsize,
+ VM_MAKE_TAG(VM_MEMORY_STACK)| VM_FLAGS_ANYWHERE);
+ } else if (__syscall_logger && !from_mach_thread) {
+ // libsyscall will not output malloc stack logging events when
+ // VM_MEMORY_STACK is passed in to facilitate mach thread promotion.
+ // To avoid losing the stack traces for normal p-thread create
+ // operations, libpthread must pretend to be the vm syscall and log
+ // the allocations. <rdar://36418708>
+ int eventTypeFlags = stack_logging_type_vm_allocate |
+ stack_logging_type_mapped_file_or_shared_mem;
+ __syscall_logger(eventTypeFlags | VM_MAKE_TAG(VM_MEMORY_STACK),
+ (uintptr_t)mach_task_self(), (uintptr_t)allocsize, 0,
+ (uintptr_t)allocaddr, 0);
+ }
+
+ if (kr != KERN_SUCCESS) {
+ *stack = NULL;
+ return NULL;
+ } else if (__syscall_logger && !from_mach_thread) {
+ // libsyscall will not output malloc stack logging events when
+ // VM_MEMORY_STACK is passed in to facilitate mach thread promotion.
+ // To avoid losing the stack traces for normal p-thread create
+ // operations, libpthread must pretend to be the vm syscall and log
+ // the allocations. <rdar://36418708>
+ int eventTypeFlags = stack_logging_type_vm_allocate;
+ __syscall_logger(eventTypeFlags | VM_MAKE_TAG(VM_MEMORY_STACK),
+ (uintptr_t)mach_task_self(), (uintptr_t)allocsize, 0,
+ (uintptr_t)allocaddr, 0);
+ }
+
+ // The stack grows down.
+ // Set the guard page at the lowest address of the
+ // newly allocated stack. Return the highest address
+ // of the stack.
+ if (guardsize) {
+ (void)mach_vm_protect(mach_task_self(), allocaddr, guardsize,
+ FALSE, VM_PROT_NONE);
+ }
+
+ // Thread structure resides at the top of the stack (when using a
+ // custom stack, allocsize == PTHREAD_SIZE, so places the pthread_t
+ // at allocaddr).
+ t = (pthread_t)(allocaddr + pthreadoff);
+ if (attrs->stackaddr) {
+ *stack = attrs->stackaddr;
+ } else {
+ *stack = t;
+ }
+
+ _pthread_struct_init(t, attrs, *stack, stacksize, allocaddr, allocsize);
+ return t;
+}
+
+PTHREAD_NOINLINE
+void
+_pthread_deallocate(pthread_t t, bool from_mach_thread)
+{
+ kern_return_t ret;
+
+ // Don't free the main thread.
+ if (t != main_thread()) {
+ if (!from_mach_thread) { // see __pthread_add_thread
+ _pthread_introspection_thread_destroy(t);
+ }
+ ret = mach_vm_deallocate(mach_task_self(), t->freeaddr, t->freesize);
+ if (ret != KERN_SUCCESS) {
+ PTHREAD_INTERNAL_CRASH(ret, "Unable to deallocate stack");
+ }
+ }
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreturn-stack-address"
+
+PTHREAD_NOINLINE
+static void*
+_pthread_current_stack_address(void)
+{
+ int a;
+ return &a;
+}
+
+#pragma clang diagnostic pop
+
+void
+_pthread_joiner_wake(pthread_t thread)
+{
+ uint32_t *exit_gate = &thread->tl_exit_gate;
+
+ for (;;) {
+ int ret = __ulock_wake(UL_UNFAIR_LOCK | ULF_NO_ERRNO, exit_gate, 0);
+ if (ret == 0 || ret == -ENOENT) {
+ return;
+ }
+ if (ret != -EINTR) {
+ PTHREAD_INTERNAL_CRASH(-ret, "pthread_join() wake failure");
+ }
+ }
+}
+
+// Terminates the thread if called from the currently running thread.
+PTHREAD_NORETURN PTHREAD_NOINLINE PTHREAD_NOT_TAIL_CALLED
+static void
+_pthread_terminate(pthread_t t, void *exit_value)
+{
+ _pthread_introspection_thread_terminate(t);
+
+ uintptr_t freeaddr = (uintptr_t)t->freeaddr;
+ size_t freesize = t->freesize;
+ bool should_exit;
+
+ // the size of just the stack
+ size_t freesize_stack = t->freesize;
+
+ // We usually pass our structure+stack to bsdthread_terminate to free, but
+ // if we get told to keep the pthread_t structure around then we need to
+ // adjust the free size and addr in the pthread_t to just refer to the
+ // structure and not the stack. If we do end up deallocating the
+ // structure, this is useless work since no one can read the result, but we
+ // can't do it after the call to pthread_remove_thread because it isn't
+ // safe to dereference t after that.
+ if ((void*)t > t->freeaddr && (void*)t < t->freeaddr + t->freesize){
+ // Check to ensure the pthread structure itself is part of the
+ // allocation described by freeaddr/freesize, in which case we split and
+ // only deallocate the area below the pthread structure. In the event of a
+ // custom stack, the freeaddr/size will be the pthread structure itself, in
+ // which case we shouldn't free anything (the final else case).
+ freesize_stack = trunc_page((uintptr_t)t - (uintptr_t)freeaddr);
+
+ // describe just the remainder for deallocation when the pthread_t goes away
+ t->freeaddr += freesize_stack;
+ t->freesize -= freesize_stack;
+ } else if (t == main_thread()) {
+ freeaddr = t->stackaddr - pthread_get_stacksize_np(t);
+ uintptr_t stackborder = trunc_page((uintptr_t)_pthread_current_stack_address());
+ freesize_stack = stackborder - freeaddr;
+ } else {
+ freesize_stack = 0;
+ }
+
+ mach_port_t kport = _pthread_kernel_thread(t);
+ bool keep_thread_struct = false, needs_wake = false;
+ semaphore_t custom_stack_sema = MACH_PORT_NULL;
+
+ _pthread_dealloc_special_reply_port(t);
+ _pthread_dealloc_reply_port(t);
+
+ _PTHREAD_LOCK(_pthread_list_lock);
+
+ // This piece of code interacts with pthread_join. It will always:
+ // - set tl_exit_gate to MACH_PORT_DEAD (thread exited)
+ // - set tl_exit_value to the value passed to pthread_exit()
+ // - decrement _pthread_count, so that we can exit the process when all
+ // threads exited even if not all of them were joined.
+ t->tl_exit_gate = MACH_PORT_DEAD;
+ t->tl_exit_value = exit_value;
+ should_exit = (--_pthread_count <= 0);
+
+ // If we see a joiner, we prepost that the join has to succeed,
+ // and the joiner is committed to finish (even if it was canceled)
+ if (t->tl_join_ctx) {
+ custom_stack_sema = _pthread_joiner_prepost_wake(t); // unsets tl_joinable
+ needs_wake = true;
+ }
+
+ // Joinable threads that have no joiner yet are kept on the thread list
+ // so that pthread_join() can later discover the thread when it is joined,
+ // and will have to do the pthread_t cleanup.
+ if (t->tl_joinable) {
+ t->tl_joiner_cleans_up = keep_thread_struct = true;
+ } else {
+ TAILQ_REMOVE(&__pthread_head, t, tl_plist);
+ }
+
+ _PTHREAD_UNLOCK(_pthread_list_lock);
+
+ if (needs_wake) {
+ // When we found a waiter, we want to drop the very contended list lock
+ // before we do the syscall in _pthread_joiner_wake(). Then, we decide
+ // who gets to cleanup the pthread_t between the joiner and the exiting
+ // thread:
+ // - the joiner tries to set tl_join_ctx to NULL
+ // - the exiting thread tries to set tl_joiner_cleans_up to true
+ // Whoever does it first commits the other guy to cleanup the pthread_t
+ _pthread_joiner_wake(t);
+ _PTHREAD_LOCK(_pthread_list_lock);
+ if (t->tl_join_ctx) {
+ t->tl_joiner_cleans_up = true;
+ keep_thread_struct = true;
+ }
+ _PTHREAD_UNLOCK(_pthread_list_lock);
+ }
+
+ //
+ // /!\ dereferencing `t` past this point is not safe /!\
+ //
+
+ if (keep_thread_struct || t == main_thread()) {
+ // Use the adjusted freesize of just the stack that we computed above.
+ freesize = freesize_stack;
+ } else {
+ _pthread_introspection_thread_destroy(t);
+ }