+ 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);
+ }
+
+ // Check if there is nothing to free because the thread has a custom
+ // stack allocation and is joinable.
+ if (freesize == 0) {
+ freeaddr = 0;
+ }
+ if (should_exit) {
+ exitf(0);
+ }
+ __bsdthread_terminate((void *)freeaddr, freesize, kport, custom_stack_sema);
+ PTHREAD_INTERNAL_CRASH(t, "thread didn't terminate");
+}
+
+PTHREAD_NORETURN
+static void
+_pthread_terminate_invoke(pthread_t t, void *exit_value)
+{
+#if PTHREAD_T_OFFSET
+ void *p = NULL;
+ // <rdar://problem/25688492> During pthread termination there is a race
+ // between pthread_join and pthread_terminate; if the joiner is responsible
+ // for cleaning up the pthread_t struct, then it may destroy some part of the
+ // stack with it on 16k OSes. So that this doesn't cause _pthread_terminate()
+ // to crash because its stack has been removed from under its feet, just make
+ // sure termination happens in a part of the stack that is not on the same
+ // page as the pthread_t.
+ if (trunc_page((uintptr_t)__builtin_frame_address(0)) ==
+ trunc_page((uintptr_t)t)) {
+ p = alloca(PTHREAD_T_OFFSET);
+ }
+ // And this __asm__ volatile is needed to stop the compiler from optimising
+ // away the alloca() completely.
+ __asm__ volatile ("" : : "r"(p) );
+#endif
+ _pthread_terminate(t, exit_value);
+}
+
+#pragma mark pthread start / body
+
+PTHREAD_NORETURN
+void
+_pthread_start(pthread_t self, mach_port_t kport,
+ __unused void *(*fun)(void *), __unused void *arg,
+ __unused size_t stacksize, unsigned int pflags)
+{
+ if (os_unlikely(pflags & PTHREAD_START_SUSPENDED)) {
+ PTHREAD_INTERNAL_CRASH(pflags,
+ "kernel without PTHREAD_START_SUSPENDED support");
+ }
+ if (os_unlikely((pflags & PTHREAD_START_TSD_BASE_SET) == 0)) {
+ PTHREAD_INTERNAL_CRASH(pflags,
+ "thread_set_tsd_base() wasn't called by the kernel");
+ }
+ PTHREAD_DEBUG_ASSERT(MACH_PORT_VALID(kport));
+ PTHREAD_DEBUG_ASSERT(_pthread_kernel_thread(self) == kport);
+ _pthread_markcancel_if_canceled(self, kport);
+
+ _pthread_set_self_internal(self);
+ __pthread_started_thread(self);
+ _pthread_exit(self, (self->fun)(self->arg));
+}
+
+PTHREAD_ALWAYS_INLINE
+static inline void
+_pthread_struct_init(pthread_t t, const pthread_attr_t *attrs,
+ void *stackaddr, size_t stacksize, void *freeaddr, size_t freesize)
+{
+ PTHREAD_DEBUG_ASSERT(t->sig != _PTHREAD_SIG);
+
+ t->sig = _PTHREAD_SIG;
+ t->tsd[_PTHREAD_TSD_SLOT_PTHREAD_SELF] = t;
+ t->tsd[_PTHREAD_TSD_SLOT_ERRNO] = &t->err_no;
+ if (attrs->schedset == 0) {
+ t->tsd[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS] = attrs->qosclass;
+ } else {
+ t->tsd[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS] =
+ _pthread_unspecified_priority();
+ }
+ t->tsd[_PTHREAD_TSD_SLOT_PTR_MUNGE] = _pthread_ptr_munge_token;
+ t->tl_has_custom_stack = (attrs->stackaddr != NULL);
+
+ _PTHREAD_LOCK_INIT(t->lock);
+
+ t->stackaddr = stackaddr;
+ t->stackbottom = stackaddr - stacksize;
+ t->freeaddr = freeaddr;
+ t->freesize = freesize;
+
+ t->guardsize = _pthread_attr_guardsize(attrs);
+ t->tl_joinable = (attrs->detached == PTHREAD_CREATE_JOINABLE);
+ t->inherit = attrs->inherit;
+ t->tl_policy = attrs->policy;
+ t->schedset = attrs->schedset;
+ _pthread_attr_get_schedparam(attrs, &t->tl_param);
+ t->cancel_state = PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED;
+}
+
+#pragma mark pthread public interface
+
+/* Need to deprecate this in future */
+int
+_pthread_is_threaded(void)
+{
+ return __is_threaded;
+}
+
+/* Non portable public api to know whether this process has(had) atleast one thread
+ * apart from main thread. There could be race if there is a thread in the process of
+ * creation at the time of call . It does not tell whether there are more than one thread
+ * at this point of time.
+ */
+int
+pthread_is_threaded_np(void)
+{
+ return __is_threaded;
+}
+
+
+PTHREAD_NOEXPORT_VARIANT
+mach_port_t
+pthread_mach_thread_np(pthread_t t)
+{
+ mach_port_t kport = MACH_PORT_NULL;
+ (void)_pthread_is_valid(t, &kport);