Thread creation, lifetime, and attributes
__pthread_invalid_keventfunction source
work queue support data
OS_NORETURN OS_COLD static void __pthread_invalid_keventfunction(void **events, int *nevents) { PTHREAD_CLIENT_CRASH(0, "Invalid kqworkq setup"); }
__pthread_invalid_workloopfunction source
OS_NORETURN OS_COLD static void __pthread_invalid_workloopfunction(uint64_t *workloop_id, void **events, int *nevents) { PTHREAD_CLIENT_CRASH(0, "Invalid kqwl setup"); }
_pthread_globals_init source
void _pthread_globals_init(void *ctx) { pthread_globals_t globals = ctx; for (thread_qos_t qos = 0; qos < THREAD_QOS_LAST; qos++) { globals->qmp_logical[qos] = QOS_PARALLELISM_NOT_QUERIED; globals->qmp_physical[qos] = QOS_PARALLELISM_NOT_QUERIED; globals->cluster_physical[qos] = QOS_PARALLELISM_NOT_QUERIED; } }
pthread_attr_destroy source · pthread_attr_destroy reference
int pthread_attr_destroy(pthread_attr_t *attr) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { if (MACH_PORT_VALID(attr->work_interval_port)) { ret = mach_port_mod_refs(mach_task_self(), attr->work_interval_port, MACH_PORT_RIGHT_SEND, -1); if (ret == KERN_SUCCESS) { attr->sig = 0; } } else { attr->sig = 0; ret = 0; } } return ret; }
pthread_attr_getdetachstate source · pthread_attr_getdetachstate reference
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { *detachstate = attr->detached; ret = 0; } return ret; }
pthread_attr_getinheritsched source · pthread_attr_getinheritsched reference
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { *inheritsched = attr->inherit; ret = 0; } return ret; }
_pthread_attr_get_schedparam source
static OS_ALWAYS_INLINE void _pthread_attr_get_schedparam(const pthread_attr_t *attr, struct sched_param *param) { if (attr->schedset) { *param = attr->param; } else { param->sched_priority = default_priority; param->quantum = 10; /* quantum isn't public yet */ } }
pthread_attr_getschedparam source · pthread_attr_getschedparam reference
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { _pthread_attr_get_schedparam(attr, param); ret = 0; } return ret; }
pthread_attr_getschedpolicy source · pthread_attr_getschedpolicy reference
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { *policy = attr->policy; ret = 0; } return ret; }
pthread_attr_init source · pthread_attr_init reference
int pthread_attr_init(pthread_attr_t *attr) { *attr = _pthread_attr_default; return 0; }
pthread_attr_setdetachstate source · pthread_attr_setdetachstate reference
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && (detachstate == PTHREAD_CREATE_JOINABLE || detachstate == PTHREAD_CREATE_DETACHED)) { attr->detached = detachstate; ret = 0; } return ret; }
pthread_attr_setinheritsched source · pthread_attr_setinheritsched reference
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && (inheritsched == PTHREAD_INHERIT_SCHED || inheritsched == PTHREAD_EXPLICIT_SCHED)) { attr->inherit = inheritsched; ret = 0; } return ret; }
pthread_attr_setschedparam source · pthread_attr_setschedparam reference
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { /* TODO: Validate sched_param fields */ attr->param = *param; attr->schedset = 1; ret = 0; } return ret; }
pthread_attr_setschedpolicy source · pthread_attr_setschedpolicy reference
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && (policy == SCHED_OTHER || policy == SCHED_RR || policy == SCHED_FIFO)) { if (!_PTHREAD_POLICY_IS_FIXEDPRI(policy)) { /* non-fixedpri policy should remove cpupercent */ attr->cpupercentset = 0; } attr->policy = policy; attr->policyset = 1; ret = 0; } return ret; }
pthread_attr_setscope source · pthread_attr_setscope reference
int pthread_attr_setscope(pthread_attr_t *attr, int scope) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { if (scope == PTHREAD_SCOPE_SYSTEM) { // No attribute yet for the scope. ret = 0; } else if (scope == PTHREAD_SCOPE_PROCESS) { ret = ENOTSUP; } } return ret; }
pthread_attr_getscope source · pthread_attr_getscope reference
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { *scope = PTHREAD_SCOPE_SYSTEM; ret = 0; } return ret; }
pthread_attr_getstackaddr source · pthread_attr_getstackaddr reference
int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { *stackaddr = attr->stackaddr; ret = 0; } return ret; }
pthread_attr_setstackaddr source · pthread_attr_setstackaddr reference
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && ((mach_vm_address_t)stackaddr & vm_page_mask) == 0) { attr->stackaddr = stackaddr; attr->defaultguardpage = false; attr->guardsize = 0; ret = 0; } return ret; }
_pthread_attr_stacksize source
static inline size_t _pthread_attr_stacksize(const pthread_attr_t *attr) { return attr->stacksize ? attr->stacksize : DEFAULT_STACK_SIZE; }
pthread_attr_getstacksize source · pthread_attr_getstacksize reference
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { *stacksize = _pthread_attr_stacksize(attr); ret = 0; } return ret; }
pthread_attr_setstacksize source · pthread_attr_setstacksize reference
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) { #if TARGET_OS_OSX // If the caller is doing something reasonable, help them out. if (stacksize % 0x1000 == 0) { stacksize = round_page(stacksize); } #endif // TARGET_OS_OSX int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && ((stacksize & vm_page_mask) == 0) && stacksize >= PTHREAD_STACK_MIN) { attr->stacksize = stacksize; ret = 0; } return ret; }
pthread_attr_getstack source · pthread_attr_getstack reference
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t * stacksize) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { *stackaddr = (void *)((uintptr_t)attr->stackaddr - attr->stacksize); *stacksize = _pthread_attr_stacksize(attr); ret = 0; } return ret; }
pthread_attr_setstack source · pthread_attr_setstack reference
Per SUSv3, the stackaddr is the base address, the lowest addressable byte
address. This is not the same as in pthread_attr_setstackaddr.
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && (((mach_vm_address_t)stackaddr & vm_page_mask) == 0) && ((stacksize & vm_page_mask) == 0) && stacksize >= PTHREAD_STACK_MIN) { attr->stackaddr = (void *)((uintptr_t)stackaddr + stacksize); attr->stacksize = stacksize; ret = 0; } return ret; }
pthread_attr_setguardsize source · pthread_attr_setguardsize reference
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) { #if TARGET_OS_OSX // If the caller is doing something reasonable, help them out. if (guardsize % 0x1000 == 0) { guardsize = round_page(guardsize); } #endif // TARGET_OS_OSX int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && (guardsize & vm_page_mask) == 0) { /* Guardsize of 0 is valid, means no guard */ attr->defaultguardpage = false; attr->guardsize = guardsize; ret = 0; } return ret; }
_pthread_attr_guardsize source
static inline size_t _pthread_attr_guardsize(const pthread_attr_t *attr) { return attr->defaultguardpage ? vm_page_size : attr->guardsize; }
pthread_attr_getguardsize source · pthread_attr_getguardsize reference
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG) { *guardsize = _pthread_attr_guardsize(attr); ret = 0; } return ret; }
pthread_attr_setcpupercent_np source
int pthread_attr_setcpupercent_np(pthread_attr_t *attr, int percent, unsigned long refillms) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && percent < UINT8_MAX && refillms < _PTHREAD_ATTR_REFILLMS_MAX && attr->policyset && _PTHREAD_POLICY_IS_FIXEDPRI(attr->policy)) { attr->cpupercent = percent; attr->refillms = (uint32_t)(refillms & 0x00ffffff); attr->cpupercentset = 1; ret = 0; } return ret; }
pthread_attr_setworkinterval_np source
int pthread_attr_setworkinterval_np(pthread_attr_t *attr, mach_port_t work_interval_port) { int ret = EINVAL; if (attr->sig == _PTHREAD_ATTR_SIG && MACH_PORT_VALID(work_interval_port)) { ret = mach_port_mod_refs(mach_task_self(), work_interval_port, MACH_PORT_RIGHT_SEND, 1); if (ret == KERN_SUCCESS) { attr->work_interval_port = work_interval_port; } } return ret; }
_pthread_allocate source
Allocate a thread structure, stack and guard page.
The thread structure may optionally be placed in the same allocation as the
stack, residing above the top of the stack. This cannot be done if a
custom stack address is provided.
Similarly the guard page cannot be allocated if a custom stack address is
provided.
The allocated thread structure is initialized with values that indicate how
it should be freed.
static pthread_t _pthread_allocate(const pthread_attr_t *attrs, void **stack, bool from_mach_thread) { mach_vm_address_t allocaddr = __pthread_stack_hint; size_t allocsize, guardsize, stacksize, pthreadoff; kern_return_t kr; pthread_t t; if (os_unlikely(attrs->stacksize != 0 && attrs->stacksize < PTHREAD_STACK_MIN)) { PTHREAD_CLIENT_CRASH(attrs->stacksize, "Stack size in attrs is too small"); } if (os_unlikely((mach_vm_address_t)attrs->stackaddr & vm_page_mask)) { PTHREAD_CLIENT_CRASH(attrs->stackaddr, "Unaligned stack addr in attrs"); } // Allocate a pthread structure if necessary if (attrs->stackaddr != NULL) { allocsize = PTHREAD_SIZE; guardsize = 0; pthreadoff = 0; // <rdar://problem/42588315> if the attrs struct specifies a custom // stack address but not a custom size, using ->stacksize here instead // of _pthread_attr_stacksize stores stacksize as zero, indicating // that the stack size is unknown. stacksize = attrs->stacksize; } else { … more in source
_pthread_deallocate source
OS_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"); } } }
_pthread_current_stack_address source
OS_NOINLINE static void* _pthread_current_stack_address(void) { int a; return &a; }
_pthread_joiner_wake source
static 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"); } } }
_pthread_dealloc_reply_port source
static void _pthread_dealloc_reply_port(pthread_t self) { mach_port_t port = _pthread_tsd_slot(self, MIG_REPLY); if (port != MACH_PORT_NULL) { // this will also set the TSD to MACH_PORT_NULL mig_dealloc_reply_port(port); } }
_pthread_dealloc_special_reply_port source
static void _pthread_dealloc_special_reply_port(pthread_t self) { mach_port_t port = _pthread_tsd_slot(self, MACH_SPECIAL_REPLY); if (port != MACH_PORT_NULL) { _pthread_tsd_slot(self, MACH_SPECIAL_REPLY) = MACH_PORT_NULL; thread_destruct_special_reply_port(port, THREAD_SPECIAL_REPLY_PORT_ALL); } }
_pthread_terminate source
Terminates the thread if called from the currently running thread.
OS_NORETURN OS_NOINLINE OS_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; … more in source
_pthread_terminate_invoke source
OS_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); }
_pthread_start source
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_tsd_slot(self, MACH_THREAD_SELF) == kport); _pthread_validate_signature(self); _pthread_markcancel_if_canceled(self, kport); _pthread_set_self_internal(self); __pthread_started_thread(self); _pthread_exit(self, (self->fun)(self->arg)); }
_pthread_struct_init source
OS_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_init_signature(t); _pthread_tsd_slot(t, PTHREAD_SELF) = t; _pthread_tsd_slot(t, ERRNO) = &t->err_no; if (attrs->schedset == 0) { _pthread_tsd_slot(t, PTHREAD_QOS_CLASS) = attrs->qosclass; } else { _pthread_tsd_slot(t, PTHREAD_QOS_CLASS) = _pthread_unspecified_priority(); } _pthread_tsd_slot(t, 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); … more in source
pthread_is_threaded_np source · pthread_is_threaded_np reference
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_mach_thread_np source · pthread_mach_thread_np reference
mach_port_t pthread_mach_thread_np(pthread_t t) { mach_port_t kport = MACH_PORT_NULL; (void)_pthread_is_valid(t, &kport); return kport; }
pthread_from_mach_thread_np source · pthread_from_mach_thread_np reference
pthread_t pthread_from_mach_thread_np(mach_port_t kernel_thread) { pthread_t p = NULL; /* No need to wait as mach port is already known */ _pthread_lock_lock(&_pthread_list_lock); TAILQ_FOREACH(p, &__pthread_head, tl_plist) { if (_pthread_tsd_slot(p, MACH_THREAD_SELF) == kernel_thread) { break; } } _pthread_lock_unlock(&_pthread_list_lock); return p; }
pthread_get_stacksize_np source · pthread_get_stacksize_np reference
size_t pthread_get_stacksize_np(pthread_t t) { size_t size = 0; if (t == NULL) { return ESRCH; // XXX bug? } #if TARGET_OS_OSX // The default rlimit based allocations will be provided with a stacksize // of the current limit and a freesize of the max. However, custom // allocations will just have the guard page to free. If we aren't in the // latter case, call into rlimit to determine the current stack size. In // the event that the current limit == max limit then we'll fall down the // fast path, but since it's unlikely that the limit is going to be lowered // after it's been change to the max, we should be fine. // // Of course, on arm rlim_cur == rlim_max and there's only the one guard // page. So, we can skip all this there. if (t == main_thread()) { size_t stacksize = t->stackaddr - t->stackbottom; if (stacksize + vm_page_size != t->freesize) { // We want to call getrlimit() just once, as it's relatively // expensive static size_t rlimit_stack; if (rlimit_stack == 0) { struct rlimit limit; … more in source
pthread_get_stackaddr_np source · pthread_get_stackaddr_np reference
void * pthread_get_stackaddr_np(pthread_t t) { // since the main thread will not get de-allocated from underneath us if (t == pthread_self() || t == main_thread()) { return t->stackaddr; } if (!_pthread_validate_thread_and_list_lock(t)) { return (void *)(uintptr_t)ESRCH; // XXX bug? } void *addr = t->stackaddr; _pthread_lock_unlock(&_pthread_list_lock); return addr; }
pthread_main_np source · pthread_main_np reference
returns non-zero if the current thread is the main thread
int pthread_main_np(void) { return pthread_self() == main_thread(); }
_pthread_threadid_slow source
static int _pthread_threadid_slow(pthread_t thread, uint64_t *thread_id) { unsigned int info_count = THREAD_IDENTIFIER_INFO_COUNT; mach_port_t thport = _pthread_tsd_slot(thread, MACH_THREAD_SELF); struct thread_identifier_info info; kern_return_t kr; kr = thread_info(thport, THREAD_IDENTIFIER_INFO, (thread_info_t)&info, &info_count); if (kr == KERN_SUCCESS && info.thread_id) { *thread_id = info.thread_id; #if __LP64__ os_atomic_store(&thread->thread_id, info.thread_id, relaxed); #else os_atomic_store_wide(&thread->thread_id, info.thread_id, relaxed); #endif return 0; } return EINVAL; }
pthread_threadid_np source · pthread_threadid_np reference
if we are passed in a pthread_t that is NULL, then we return the current
thread's thread_id. So folks don't have to call pthread_self, in addition to
us doing it, if they just want their thread_id.
int pthread_threadid_np(pthread_t thread, uint64_t *thread_id) { int res = 0; pthread_t self = pthread_self(); if (thread_id == NULL) { return EINVAL; } if (thread == NULL || thread == self) { *thread_id = self->thread_id; } else if (!_pthread_validate_thread_and_list_lock(thread)) { res = ESRCH; } else { #if __LP64__ *thread_id = os_atomic_load(&thread->thread_id, relaxed); #else *thread_id = os_atomic_load_wide(&thread->thread_id, relaxed); #endif if (os_unlikely(*thread_id == 0)) { // there is a race at init because the thread sets its own TID. // correct this by asking mach res = _pthread_threadid_slow(thread, thread_id); } _pthread_lock_unlock(&_pthread_list_lock); } return res; }
pthread_cpu_number_np source · pthread_cpu_number_np reference
int pthread_cpu_number_np(size_t *cpu_id) { if (cpu_id == NULL) { errno = EINVAL; return errno; } *cpu_id = _os_cpu_number(); return 0; }
pthread_getname_np source · pthread_getname_np reference
int pthread_getname_np(pthread_t thread, char *threadname, size_t len) { if (thread == pthread_self()) { strlcpy(threadname, thread->pthread_name, len); return 0; } if (!_pthread_validate_thread_and_list_lock(thread)) { return ESRCH; } strlcpy(threadname, thread->pthread_name, len); _pthread_lock_unlock(&_pthread_list_lock); return 0; }
pthread_setname_np source · pthread_setname_np reference
int pthread_setname_np(const char *name) { int res; pthread_t self = pthread_self(); size_t len = 0; if (name != NULL) { len = strlen(name); } _pthread_validate_signature(self); res = __proc_info(5, getpid(), 2, (uint64_t)0, (void*)name, (int)len); if (res == 0) { if (len > 0) { strlcpy(self->pthread_name, name, MAXTHREADNAMESIZE); } else { bzero(self->pthread_name, MAXTHREADNAMESIZE); } } return res; }
_pthread_jit_write_protect_global_init source
static void _pthread_jit_write_protect_global_init(void) { // On lockdown-default platforms, we lock down the allowlist if the process // is JIT-entitled at all. Otherwise, we lock down only if the process // specifically has the JIT-allowlist entitlement. #if _PTHREAD_CONFIG_JIT_LOCKDOWN_DEFAULT bool allowlist_feature_enabled = (__pthread_supported_features & PTHREAD_FEATURE_JIT_ENTITLED); #else bool allowlist_feature_enabled = (__pthread_supported_features & PTHREAD_FEATURE_JIT_ALLOWLIST); #endif if (os_thread_self_restrict_rwx_is_supported() && allowlist_feature_enabled) { union _pthread_jit_config_u *config = &_pthread_jit_config; uintptr_t alignment = ((uintptr_t)config) % PAGE_MAX_SIZE; if (alignment != 0) { PTHREAD_INTERNAL_CRASH(alignment, "jit config invalid alignment"); } // Remap the config page as permanent. Note that this discards any // previous content, so all changes to the config must come _after_ this // point. // // TODO: find a suitable tag for this memory (rdar://75202626) mach_vm_address_t allocaddr = (mach_vm_address_t)config; kern_return_t kr = mach_vm_map(mach_task_self(), &allocaddr, sizeof(*config), PAGE_MAX_SIZE - 1, … more in source
_pthread_jit_write_protect_bulk_image_load_callback source
static void _pthread_jit_write_protect_bulk_image_load_callback(unsigned int image_count, const struct mach_header *mhs[], __unused const char *paths[]) { union _pthread_jit_config_u *config = &_pthread_jit_config; if (config->allowlist_freeze_late) { _pthread_lock_lock(&_pthread_jit_config_lock); } // Ignore dlopen()s after the allowlist is frozen if (config->allowlist_frozen) { goto done; } for (unsigned int i = 0; i < image_count; i++) { const struct mach_header_64 *mh = (const struct mach_header_64 *)mhs[i]; if (mh->flags & MH_DYLIB_IN_CACHE) { // Internal code should use os_thread_self_restrict_rwx*() continue; } unsigned long size = 0; uint8_t *data = getsectiondata(mh, "__DATA_CONST", "__pth_jit_func", &size); if (!data || !size || (size % sizeof(pthread_jit_write_callback_t) != 0)) { continue; } … more in source
_pthread_jit_write_protect_freeze_config source
static void _pthread_jit_write_protect_freeze_config(void) { union _pthread_jit_config_u *config = &_pthread_jit_config; // By protecting config read-only with set_maximum, prevent an attack // from later altering its protection back to writeable. kern_return_t kr = mach_vm_protect(mach_task_self(), (mach_vm_address_t)config, sizeof(*config), /* set_maximum */ true, VM_PROT_READ); if (kr != KERN_SUCCESS) { PTHREAD_INTERNAL_CRASH(kr, "jit config vm_protect() failed"); } }
_pthread_jit_write_protect_late_init source
static void _pthread_jit_write_protect_late_init(void) { union _pthread_jit_config_u *config = &_pthread_jit_config; if (config->allowlist_enabled) { if (__pthread_supported_features & PTHREAD_FEATURE_JIT_ALLOWLIST) { // Iterate all initial images for allowed functions _dyld_register_for_bulk_image_loads( _pthread_jit_write_protect_bulk_image_load_callback); } if (!config->allowlist_freeze_late) { // Since there's no way to un-register the image load callback, just // remember that we're no longer interested in new images config->allowlist_frozen = true; _pthread_jit_write_protect_freeze_config(); } } }
pthread_jit_write_protect_np source · pthread_jit_write_protect_np reference
void pthread_jit_write_protect_np(int enable) { if (!os_thread_self_restrict_rwx_is_supported()) { return; } if (enable) { os_thread_self_restrict_rwx_to_rx(); } else { os_thread_self_restrict_rwx_to_rw(); } #if _PTHREAD_CONFIG_JIT_WRITE_PROTECT // Validate _after_ toggling. union _pthread_jit_config_u *config = &_pthread_jit_config; if (config->allowlist_enabled) { if (!enable) { os_thread_self_restrict_rwx_to_rx(); } PTHREAD_CLIENT_CRASH(0, "pthread_jit_write_protect_np() disallowed by allowlist"); } #endif // _PTHREAD_CONFIG_JIT_WRITE_PROTECT }
pthread_jit_write_protect_supported_np source · pthread_jit_write_protect_supported_np reference
TARGET_OS_OSX
int pthread_jit_write_protect_supported_np() { return os_thread_self_restrict_rwx_is_supported(); }
pthread_jit_write_with_callback_np source · pthread_jit_write_with_callback_np reference
int pthread_jit_write_with_callback_np(pthread_jit_write_callback_t callback, void *ctx) { #if _PTHREAD_CONFIG_JIT_WRITE_PROTECT if (!os_thread_self_restrict_rwx_is_supported()) { return callback(ctx); } // The toggle to RW must come before validation. If we validate before // toggling, an attack might try to jump into the middle of the body after // the validation. os_thread_self_restrict_rwx_to_rw(); union _pthread_jit_config_u *config = &_pthread_jit_config; if (config->allowlist_enabled) { if (!config->allowlist_frozen) { PTHREAD_CLIENT_CRASH(config->allowlist_freeze_late, "pthread_jit_write_with_callback_np() called before " "pthread_jit_write_freeze_callbacks_np()"); } uint32_t i; for (i = 0; i < config->allowlist_count; i++) { if (config->allowlist[i] == callback) { break; } } if (i == config->allowlist_count) { … more in source
pthread_jit_write_freeze_callbacks_np source · pthread_jit_write_freeze_callbacks_np reference
void pthread_jit_write_freeze_callbacks_np(void) { #if _PTHREAD_CONFIG_JIT_WRITE_PROTECT if (!os_thread_self_restrict_rwx_is_supported()) { return; } union _pthread_jit_config_u *config = &_pthread_jit_config; if (!config->allowlist_freeze_late) { PTHREAD_CLIENT_CRASH(0, "pthread_jit_write_freeze_callbacks_np() " "called but freeze-late entitlement is missing"); } _pthread_lock_lock(&_pthread_jit_config_lock); if (config->allowlist_frozen) { PTHREAD_CLIENT_CRASH(0, "pthread_jit_write_freeze_callbacks_np() already called"); } config->allowlist_frozen = true; _pthread_lock_unlock(&_pthread_jit_config_lock); _pthread_jit_write_protect_freeze_config(); #endif // _PTHREAD_CONFIG_JIT_WRITE_PROTECT }
__pthread_add_thread source
TARGET_OS_OSX || PTHREAD_TARGET_OS_IOS_ONLY
OS_ALWAYS_INLINE static inline void __pthread_add_thread(pthread_t t, mach_port_t self, bool from_mach_thread) { _pthread_lock_lock(&_pthread_list_lock, self); TAILQ_INSERT_TAIL(&__pthread_head, t, tl_plist); _pthread_count++; _pthread_lock_unlock(&_pthread_list_lock, self); if (!from_mach_thread) { // PR-26275485: Mach threads will likely crash trying to run // introspection code. Since the fall out from the introspection // code not seeing the injected thread is likely less than crashing // in the introspection code, just don't make the call. _pthread_introspection_thread_create(t); } }
__pthread_undo_add_thread source
OS_ALWAYS_INLINE static inline void __pthread_undo_add_thread(pthread_t t, mach_port_t self) { _pthread_lock_lock(&_pthread_list_lock, self); TAILQ_REMOVE(&__pthread_head, t, tl_plist); _pthread_count--; _pthread_lock_unlock(&_pthread_list_lock, self); }
__pthread_started_thread source
OS_ALWAYS_INLINE static inline void __pthread_started_thread(pthread_t t) { mach_port_t kport = _pthread_tsd_slot(t, MACH_THREAD_SELF); if (os_unlikely(!MACH_PORT_VALID(kport))) { PTHREAD_CLIENT_CRASH(kport, "Unable to allocate thread port, possible port leak"); } _pthread_introspection_thread_start(t); }
_pthread_create source
static int _pthread_create(pthread_t *thread, const pthread_attr_t *attrs, void *(*start_routine)(void *), void *arg, unsigned int create_flags) { pthread_t t = NULL; void *stack = NULL; bool from_mach_thread = (create_flags & _PTHREAD_CREATE_FROM_MACH_THREAD); mach_port_t self_kport; int rc = 0; if (attrs == NULL) { attrs = &_pthread_attr_default; } else if (attrs->sig != _PTHREAD_ATTR_SIG) { return EINVAL; } unsigned int flags = PTHREAD_START_CUSTOM; if (attrs->schedset != 0) { struct sched_param p; _pthread_attr_get_schedparam(attrs, &p); flags |= PTHREAD_START_SETSCHED; flags |= ((attrs->policy & PTHREAD_START_POLICY_MASK) << PTHREAD_START_POLICY_BITSHIFT); flags |= (p.sched_priority & PTHREAD_START_IMPORTANCE_MASK); } else if (attrs->qosclass != 0) { flags |= PTHREAD_START_QOSCLASS; flags |= (attrs->qosclass & PTHREAD_START_QOSCLASS_MASK); } if (create_flags & _PTHREAD_CREATE_SUSPENDED) { flags |= PTHREAD_START_SUSPENDED; } … more in source
pthread_create source · pthread_create reference
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { unsigned int flags = _PTHREAD_CREATE_NONE; return _pthread_create(thread, attr, start_routine, arg, flags); }
pthread_create_from_mach_thread source
int pthread_create_from_mach_thread(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { unsigned int flags = _PTHREAD_CREATE_FROM_MACH_THREAD; return _pthread_create(thread, attr, start_routine, arg, flags); }
pthread_create_suspended_np source · pthread_create_suspended_np reference
int pthread_create_suspended_np(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { unsigned int flags = _PTHREAD_CREATE_SUSPENDED; return _pthread_create(thread, attr, start_routine, arg, flags); }
pthread_install_workgroup_functions_np source
void pthread_install_workgroup_functions_np( const struct pthread_workgroup_functions_s *pwgf) { if (_pthread_workgroup_functions) { PTHREAD_CLIENT_CRASH((uintptr_t)_pthread_workgroup_functions, "workgroup functions already installed"); } _pthread_workgroup_functions = pwgf; }
pthread_create_with_workgroup_np source
int pthread_create_with_workgroup_np(pthread_t *thread, os_workgroup_t wg, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { if (!_pthread_workgroup_functions) { PTHREAD_CLIENT_CRASH(0, "workgroup functions not yet installed"); } return _pthread_workgroup_functions->pwgf_create_with_workgroup(thread, wg, attr, start_routine, arg); }
pthread_detach source · pthread_detach reference
int pthread_detach(pthread_t thread) { int res = 0; bool join = false, wake = false; if (!_pthread_validate_thread_and_list_lock(thread)) { return ESRCH; } if (!thread->tl_joinable) { res = EINVAL; } else if (thread->tl_exit_gate == MACH_PORT_DEAD) { // Join the thread if it's already exited. join = true; } else { thread->tl_joinable = false; // _pthread_joiner_prepost_wake uses this if (thread->tl_join_ctx) { (void)_pthread_joiner_prepost_wake(thread); wake = true; } } _pthread_lock_unlock(&_pthread_list_lock); if (join) { pthread_join(thread, NULL); } else if (wake) { _pthread_joiner_wake(thread); } return res; … more in source
pthread_kill source · pthread_kill reference
int pthread_kill(pthread_t th, int sig) { if (sig < 0 || sig > NSIG) { return EINVAL; } mach_port_t kport = MACH_PORT_NULL; { if (!_pthread_is_valid(th, &kport)) { return ESRCH; } } int ret = __pthread_kill(kport, sig); if (ret == -1) { ret = errno; } return ret; }
__pthread_workqueue_setkill source
int __pthread_workqueue_setkill(int enable) { { return __bsdthread_ctl(BSDTHREAD_CTL_WORKQ_ALLOW_KILL, enable, 0, 0); } }
_pthread_exit source
Terminate a thread.
OS_NORETURN static void _pthread_exit(pthread_t self, void *exit_value) { struct __darwin_pthread_handler_rec *handler; // Disable signal delivery while we clean up __disable_threadsignal(1); // Set cancel state to disable and type to deferred _pthread_setcancelstate_exit(self, exit_value); while ((handler = self->__cleanup_stack) != 0) { (handler->__routine)(handler->__arg); self->__cleanup_stack = handler->__next; } _pthread_tsd_cleanup(self); // Clear per-thread semaphore cache os_put_cached_semaphore(SEMAPHORE_NULL); _pthread_terminate_invoke(self, exit_value); }
pthread_exit source · pthread_exit reference
void pthread_exit(void *exit_value) { pthread_t self = pthread_self(); if (os_unlikely(self->wqthread)) { PTHREAD_CLIENT_CRASH(0, "pthread_exit() called from a thread " "not created by pthread_create()"); } _pthread_validate_signature(self); _pthread_exit(self, exit_value); }
pthread_self_is_exiting_np source
int pthread_self_is_exiting_np(void) { return (os_atomic_load(&pthread_self()->cancel_state, relaxed) & _PTHREAD_CANCEL_EXITING) != 0; }
pthread_getschedparam source · pthread_getschedparam reference
int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param) { if (!_pthread_validate_thread_and_list_lock(thread)) { return ESRCH; } if (policy) *policy = thread->tl_policy; if (param) *param = thread->tl_param; _pthread_lock_unlock(&_pthread_list_lock); return 0; }
pthread_setschedparam_internal source
OS_ALWAYS_INLINE static inline int pthread_setschedparam_internal(pthread_t thread, mach_port_t kport, int policy, const struct sched_param *param) { policy_base_data_t bases; policy_base_t base; mach_msg_type_number_t count; kern_return_t ret; if (os_unlikely(thread->wqthread)) { return ENOTSUP; } switch (policy) { case SCHED_OTHER: bases.ts.base_priority = param->sched_priority; base = (policy_base_t)&bases.ts; count = POLICY_TIMESHARE_BASE_COUNT; break; case SCHED_FIFO: bases.fifo.base_priority = param->sched_priority; base = (policy_base_t)&bases.fifo; count = POLICY_FIFO_BASE_COUNT; break; case SCHED_RR: bases.rr.base_priority = param->sched_priority; /* quantum isn't public yet */ bases.rr.quantum = param->quantum; base = (policy_base_t)&bases.rr; … more in source
pthread_setschedparam source · pthread_setschedparam reference
int pthread_setschedparam(pthread_t t, int policy, const struct sched_param *param) { mach_port_t kport = MACH_PORT_NULL; int bypass = 1; // since the main thread will not get de-allocated from underneath us if (t == pthread_self() || t == main_thread()) { _pthread_validate_signature(t); kport = _pthread_tsd_slot(t, MACH_THREAD_SELF); } else { bypass = 0; if (!_pthread_is_valid(t, &kport)) { return ESRCH; } } int res = pthread_setschedparam_internal(t, kport, policy, param); if (res) return res; if (bypass) { _pthread_lock_lock(&_pthread_list_lock); } else if (!_pthread_validate_thread_and_list_lock(t)) { // Ensure the thread is still valid. return ESRCH; } t->tl_policy = policy; t->tl_param = *param; _pthread_lock_unlock(&_pthread_list_lock); … more in source
sched_get_priority_min source · sched_get_priority_min reference
int sched_get_priority_min(int policy) { return default_priority - 16; }
sched_get_priority_max source · sched_get_priority_max reference
int sched_get_priority_max(int policy) { return default_priority + 16; }
pthread_equal source · pthread_equal reference
int pthread_equal(pthread_t t1, pthread_t t2) { return (t1 == t2); }
_pthread_set_self source
OS_NOINLINE void _pthread_set_self(pthread_t p) { #if VARIANT_DYLD if (os_likely(!p)) { return _pthread_set_self_dyld(); } #endif // VARIANT_DYLD _pthread_set_self_internal(p); _thread_set_tsd_base(&p->tsd[0]); }
_pthread_set_self_dyld source
_pthread_set_self_dyld is noinline+noexport to allow the option for
static libsyscall to adopt this as the entry point from mach_init if
desired
OS_NOINLINE static void _pthread_set_self_dyld(void) { pthread_t p = main_thread(); p->thread_id = __thread_selfid(); if (os_unlikely(p->thread_id == -1ull)) { PTHREAD_INTERNAL_CRASH(0, "failed to set thread_id"); } // <rdar://problem/40930651> pthread self and the errno address are the // bare minimium TSD setup that dyld needs to actually function. Without // this, TSD access will fail and crash if it uses bits of Libc prior to // library initialization. __pthread_init will finish the initialization // during library init. _pthread_tsd_slot(p, PTHREAD_SELF) = p; _pthread_tsd_slot(p, ERRNO) = &p->err_no; _thread_set_tsd_base(&p->tsd[0]); }
_pthread_set_self_internal source
VARIANT_DYLD
OS_ALWAYS_INLINE static inline void _pthread_set_self_internal(pthread_t p) { #if __LP64__ os_atomic_store(&p->thread_id, __thread_selfid(), relaxed); #else os_atomic_store_wide(&p->thread_id, __thread_selfid(), relaxed); #endif if (os_unlikely(p->thread_id == -1ull)) { PTHREAD_INTERNAL_CRASH(0, "failed to set thread_id"); } }
_os_once_acquire source
<rdar://problem/28984807> pthread_once should have an acquire barrier
OS_ALWAYS_INLINE static inline void _os_once_acquire(os_once_t *predicate, void *context, os_function_t function) { if (OS_EXPECT(os_atomic_load(predicate, acquire), ~0l) != ~0l) { _os_once(predicate, context, function); OS_COMPILER_CAN_ASSUME(*predicate == ~0l); } }
__pthread_once_handler source
static void __pthread_once_handler(void *context) { struct _pthread_once_context *ctx = context; pthread_cleanup_push((void*)__os_once_reset, &ctx->pthread_once->once); ctx->routine(); pthread_cleanup_pop(0); ctx->pthread_once->sig = _PTHREAD_ONCE_SIG; }
pthread_once source · pthread_once reference
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { struct _pthread_once_context ctx = { once_control, init_routine }; do { _os_once_acquire(&once_control->once, &ctx, __pthread_once_handler); } while (once_control->sig == _PTHREAD_ONCE_SIG_init); return 0; }
pthread_getconcurrency source · pthread_getconcurrency reference
int pthread_getconcurrency(void) { return pthread_concurrency; }
pthread_setconcurrency source · pthread_setconcurrency reference
int pthread_setconcurrency(int new_level) { if (new_level < 0) { return EINVAL; } pthread_concurrency = new_level; return 0; }
_pthread_strtoul source
Perform package initialization - called automatically when application starts
static unsigned long _pthread_strtoul(const char *p, const char **endptr, int base) { uintptr_t val = 0; // Expect hex string starting with "0x" if ((base == 16 || base == 0) && p && p[0] == '0' && p[1] == 'x') { p += 2; while (1) { char c = *p; if ('0' <= c && c <= '9') { val = (val << 4) + (c - '0'); } else if ('a' <= c && c <= 'f') { val = (val << 4) + (c - 'a' + 10); } else if ('A' <= c && c <= 'F') { val = (val << 4) + (c - 'A' + 10); } else { break; } ++p; } } *endptr = (char *)p; return val; }
parse_main_stack_params source
static int parse_main_stack_params(const char *apple[], void **stackaddr, size_t *stacksize, void **allocaddr, size_t *allocsize) { const char *p = _simple_getenv(apple, "main_stack"); if (!p) return 0; int ret = 0; const char *s = p; *stackaddr = _pthread_strtoul(s, &s, 16); if (*s != ',') goto out; *stacksize = _pthread_strtoul(s + 1, &s, 16); if (*s != ',') goto out; *allocaddr = _pthread_strtoul(s + 1, &s, 16); if (*s != ',') goto out; *allocsize = _pthread_strtoul(s + 1, &s, 16); if (*s != ',' && *s != 0) goto out; ret = 1; out: bzero((char *)p, strlen(p)); return ret; }
parse_ptr_munge_params source
static void parse_ptr_munge_params(const char *envp[], const char *apple[]) { const char *p, *s; uintptr_t token = 0; p = _simple_getenv(apple, "ptr_munge"); if (p) { token = _pthread_strtoul(p, &s, 16); bzero((char *)p, strlen(p)); } /* * In DEBUG we allow the environment variable to override the kernel * security setting, including setting it to 0 which is helpful during * debugging sessions. * * For other cases, the token must be set by the kernel or the environment * variable to a non 0 value. */ #if !DEBUG if (!token) { #endif p = _simple_getenv(envp, "PTHREAD_PTR_MUNGE_TOKEN"); if (p) { uintptr_t t = _pthread_strtoul(p, &s, 16); if (t) token = t; } #if !DEBUG } if (!token) { … more in source
parse_main_thread_port source
static void parse_main_thread_port(const char *apple[], mach_port_name_t *main_th) { const char *p, *s; p = _simple_getenv(apple, "th_port"); if (p) { *main_th = (mach_port_name_t)_pthread_strtoul(p, &s, 16); bzero((char *)p, strlen(p)); } }
__pthread_init source
int __pthread_init(const struct _libpthread_functions *pthread_funcs, const char *envp[], const char *apple[], const struct ProgramVars *vars __unused) { // Save our provided pushed-down functions if (pthread_funcs) { exitf = pthread_funcs->exit; if (pthread_funcs->version >= 2) { _pthread_malloc = pthread_funcs->malloc; _pthread_free = pthread_funcs->free; } } // libpthread.a in dyld "owns" the main thread structure itself and sets // up the tsd to point to it. So take the pthread_self() from there // and make it our main thread point. pthread_t thread = _pthread_self_direct(); if (os_unlikely(thread == NULL)) { PTHREAD_INTERNAL_CRASH(0, "PTHREAD_SELF TSD not initialized"); } _main_thread_ptr = thread; // this needs to be done early so that pthread_self() works in TSAN _pthread_init_signature(thread); // // Get host information // … more in source
__pthread_late_init source
NOTE: all releases aligned with macOS 11.4 export __pthread_late_init(), but
it is only currently called on TARGET_OS_OSX.
void __pthread_late_init(const char *envp[], const char *apple[], const struct ProgramVars *vars) { #if _PTHREAD_CONFIG_JIT_WRITE_PROTECT _pthread_jit_write_protect_late_init(); #endif // _PTHREAD_CONFIG_JIT_WRITE_PROTECT }
_pthread_main_thread_init source
!VARIANT_DYLD
void _pthread_main_thread_init(pthread_t p, mach_port_name_t main_thread_port) { TAILQ_INIT(&__pthread_head); _pthread_lock_init(&_pthread_list_lock); _pthread_lock_init(&p->lock); p->__cleanup_stack = NULL; p->tl_join_ctx = NULL; p->tl_exit_gate = MACH_PORT_NULL; if (main_thread_port != MACH_PORT_NULL) { _pthread_tsd_slot(p, MACH_THREAD_SELF) = main_thread_port; } else { // Can't get thread port from kernel or we are forking, fallback to mach_thread_self _pthread_tsd_slot(p, MACH_THREAD_SELF) = mach_thread_self(); } mach_port_t mig_reply_port = MACH_PORT_NULL; mach_port_options_t opts = { .flags = MPO_REPLY_PORT, }; mach_port_construct(mach_task_self(), &opts, NULL, &mig_reply_port); _pthread_tsd_slot(p, MIG_REPLY) = mig_reply_port; _pthread_tsd_slot(p, MACH_SPECIAL_REPLY) = MACH_PORT_NULL; _pthread_tsd_slot(p, SEMAPHORE_CACHE) = SEMAPHORE_NULL; // Initialize the list of threads with the new main thread. TAILQ_INSERT_HEAD(&__pthread_head, p, tl_plist); _pthread_count = 1; … more in source
_pthread_main_thread_postfork_init source
void _pthread_main_thread_postfork_init(pthread_t p) { _pthread_main_thread_init(p, MACH_PORT_NULL); _pthread_set_self_internal(p); }
sched_yield source · sched_yield reference
__attribute__((disable_tail_calls)) int sched_yield(void) { if (os_likely(pthread_yield_to_zero)) { swtch_pri(0); } else { thread_switch(THREAD_NULL, SWITCH_OPTION_NONE, 0); } return 0; }
_pthread_yield_to_enqueuer_4dispatch source
int _pthread_yield_to_enqueuer_4dispatch(unsigned long tsd_slot, void *expected_val, unsigned int timeout_ms) { mach_port_t enqueuer_port = MACH_PORT_NULL; _pthread_lock_lock(&_pthread_list_lock); /* If there are multiple enqueuers to the same queue, this logic does not * protect us from repeatedly yielding to the same thread which may not be * able to unblock us. But that is only a major problem if that thread is * constantly enqueueing to the same queue in a loop. We've not encountered * that scenario enough to handle that here. */ pthread_t p = NULL; TAILQ_FOREACH(p, &__pthread_head, tl_plist) { if (expected_val) { if (p->tsd[tsd_slot] == expected_val) { enqueuer_port = _pthread_tsd_slot(p, MACH_THREAD_SELF); break; } } else { if (p->tsd[tsd_slot] != NULL) { enqueuer_port = _pthread_tsd_slot(p, MACH_THREAD_SELF); break; } } } _pthread_lock_unlock(&_pthread_list_lock); … more in source
pthread_current_stack_contains_np source
Libsystem knows about this symbol and exports it to libsyscall
int pthread_current_stack_contains_np(const void *addr, size_t length) { uintptr_t begin = (uintptr_t) addr, end; uintptr_t stack_base = (uintptr_t) _pthread_self_direct()->stackbottom; uintptr_t stack_top = (uintptr_t) _pthread_self_direct()->stackaddr; if (stack_base == stack_top) { return -ENOTSUP; } if (__builtin_add_overflow(begin, length, &end)) { return -EINVAL; } return stack_base <= begin && end <= stack_top; }
_pthread_clear_qos_tsd source
Libsystem knows about this symbol and exports it to libsyscall
void _pthread_clear_qos_tsd(mach_port_t port) { pthread_priority_t pp = _pthread_unspecified_priority(); if (port == MACH_PORT_NULL || _pthread_mach_thread_self_direct() == port) { /* Clear the current thread's TSD, that can be done inline. */ _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS, pp); } else { pthread_t p; _pthread_lock_lock(&_pthread_list_lock); TAILQ_FOREACH(p, &__pthread_head, tl_plist) { mach_port_t kp = _pthread_tsd_slot(p, MACH_THREAD_SELF); if (port == kp) { _pthread_tsd_slot(p, PTHREAD_QOS_CLASS) = pp; break; } } _pthread_lock_unlock(&_pthread_list_lock); } }
pthread_stack_frame_decode_np source
uintptr_t pthread_stack_frame_decode_np(uintptr_t frame_addr, uintptr_t *return_addr) { struct frame_data *frame = (struct frame_data *)frame_addr; #if __has_feature(ptrauth_calls) /* In the case of Swift async backtraces, the context pointer read from * the OS stack is signed and will be passed here as `frame_addr`. * There is no harm in generally stripping this pointer which solves * this usecase. */ frame = ptrauth_strip(frame, ptrauth_key_process_dependent_data); #endif if (return_addr) { #if __has_feature(ptrauth_calls) *return_addr = (uintptr_t)ptrauth_strip((void *)frame->ret_addr, ptrauth_key_return_address); #else *return_addr = (uintptr_t)frame->ret_addr; #endif /* __has_feature(ptrauth_calls) */ } uintptr_t next_frame = frame->frame_addr_next; #if __has_feature(ptrauth_calls) next_frame = (uintptr_t)ptrauth_strip((void *)next_frame, ptrauth_key_frame_pointer); #endif /* __has_feature(ptrauth_calls) */ #if __LP64__ /* … more in source
_pthread_bsdthread_init source
void _pthread_bsdthread_init(struct _pthread_registration_data *data) { bzero(data, sizeof(*data)); data->version = sizeof(struct _pthread_registration_data); data->dispatch_queue_offset = __PTK_LIBDISPATCH_KEY0 * sizeof(void *); data->return_to_kernel_offset = __TSD_RETURN_TO_KERNEL * sizeof(void *); data->tsd_offset = offsetof(struct pthread_s, tsd); data->mach_thread_self_offset = __TSD_MACH_THREAD_SELF * sizeof(void *); data->joinable_offset_bits = CHAR_BIT * (offsetof(struct pthread_s, tl_policy) + 1); data->wq_quantum_expiry_offset = __PTK_LIBDISPATCH_KEY10 * sizeof(void *); int rv = __bsdthread_register(thread_start, start_wqthread, (int)PTHREAD_SIZE, (void*)data, (uintptr_t)sizeof(*data), data->dispatch_queue_offset); if (rv > 0) { int required_features = PTHREAD_FEATURE_FINEPRIO | PTHREAD_FEATURE_BSDTHREADCTL | PTHREAD_FEATURE_SETSELF | PTHREAD_FEATURE_QOS_MAINTENANCE | PTHREAD_FEATURE_QOS_DEFAULT; if ((rv & required_features) != required_features) { PTHREAD_INTERNAL_CRASH(rv, "Missing required kernel support"); } __pthread_supported_features = rv; } /* * TODO: differentiate between (-1, EINVAL) after fork (which has the side … more in source
_pthread_wqthread_legacy_worker_wrap source
OS_NOINLINE static void _pthread_wqthread_legacy_worker_wrap(pthread_workqueue_function2_arg_t workq_function2_arg) { /* Old thread priorities are inverted from where we have them in * the new flexible priority scheme. The highest priority is zero, * up to 2, with background at 3. */ pthread_workqueue_function_t func = (pthread_workqueue_function_t)__libdispatch_workerfunction; bool overcommit = (workq_function2_arg & _PTHREAD_PRIORITY_OVERCOMMIT_FLAG); int opts = overcommit ? WORKQ_ADDTHREADS_OPTION_OVERCOMMIT : 0; switch (_pthread_priority_thread_qos(workq_function2_arg)) { case THREAD_QOS_USER_INITIATED: return (*func)(WORKQ_HIGH_PRIOQUEUE, opts, NULL); case THREAD_QOS_LEGACY: /* B&I builders can't pass a QOS_CLASS_DEFAULT thread to dispatch, for fear of the QoS being * picked up by NSThread (et al) and transported around the system. So change the TSD to * make this thread look like QOS_CLASS_USER_INITIATED even though it will still run as legacy. */ _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS, _pthread_priority_make_from_thread_qos(THREAD_QOS_USER_INITIATED, 0, 0)); return (*func)(WORKQ_DEFAULT_PRIOQUEUE, opts, NULL); case THREAD_QOS_UTILITY: return (*func)(WORKQ_LOW_PRIOQUEUE, opts, NULL); case THREAD_QOS_BACKGROUND: return (*func)(WORKQ_BG_PRIOQUEUE, opts, NULL); } PTHREAD_INTERNAL_CRASH(workq_function2_arg, "Invalid pthread priority for the legacy interface"); }
_pthread_wqthread_priority source
OS_ALWAYS_INLINE static inline pthread_priority_t _pthread_wqthread_priority(int flags) { pthread_priority_t pp = 0; thread_qos_t qos; if (flags & WQ_FLAG_THREAD_KEVENT) { pp |= _PTHREAD_PRIORITY_NEEDS_UNBIND_FLAG; } if (flags & WQ_FLAG_THREAD_EVENT_MANAGER) { return pp | _PTHREAD_PRIORITY_EVENT_MANAGER_FLAG; } if (flags & WQ_FLAG_THREAD_OVERCOMMIT) { pp |= _PTHREAD_PRIORITY_OVERCOMMIT_FLAG; } if (flags & WQ_FLAG_THREAD_PRIO_QOS) { qos = (thread_qos_t)(flags & WQ_FLAG_THREAD_PRIO_MASK); pp = _pthread_priority_make_from_thread_qos(qos, 0, pp); } else if (flags & WQ_FLAG_THREAD_PRIO_SCHED) { pp |= _PTHREAD_PRIORITY_SCHED_PRI_MASK; pp |= (flags & WQ_FLAG_THREAD_PRIO_MASK); } else { PTHREAD_INTERNAL_CRASH(flags, "Missing priority"); } return pp; }
_pthread_wqthread_setup source
OS_NOINLINE static void _pthread_wqthread_setup(pthread_t self, mach_port_t kport, void *stacklowaddr, int flags) { void *stackaddr = self; size_t stacksize = (uintptr_t)self - (uintptr_t)stacklowaddr; _pthread_struct_init(self, &_pthread_attr_default, stackaddr, stacksize, PTHREAD_ALLOCADDR(stackaddr, stacksize), PTHREAD_ALLOCSIZE(stackaddr, stacksize)); _pthread_tsd_slot(self, MACH_THREAD_SELF) = kport; self->wqthread = 1; self->wqkillset = 0; self->tl_joinable = false; // Update the running thread count and set childrun bit. if (os_unlikely((flags & WQ_FLAG_THREAD_TSD_BASE_SET) == 0)) { PTHREAD_INTERNAL_CRASH(flags, "thread_set_tsd_base() wasn't called by the kernel"); } _pthread_set_self_internal(self); __pthread_add_thread(self, kport, false); __pthread_started_thread(self); }
_pthread_wqthread_exit source
OS_NORETURN OS_NOINLINE static void _pthread_wqthread_exit(pthread_t self) { const thread_qos_t WORKQ_THREAD_QOS_CLEANUP = THREAD_QOS_LEGACY; pthread_priority_t pp = _pthread_tsd_slot(self, PTHREAD_QOS_CLASS); thread_qos_t qos; qos = _pthread_priority_thread_qos(pp); if (qos == THREAD_QOS_UNSPECIFIED || qos > WORKQ_THREAD_QOS_CLEANUP) { // Reset QoS to something low for the cleanup process pp = _pthread_priority_make_from_thread_qos(WORKQ_THREAD_QOS_CLEANUP, 0, 0); _pthread_tsd_slot(self, PTHREAD_QOS_CLASS) = pp; } _pthread_exit(self, NULL); }
_pthread_wqthread source
workqueue entry point from kernel
void _pthread_wqthread(pthread_t self, mach_port_t kport, void *stacklowaddr, void *keventlist, int flags, int nkevents) { if ((flags & WQ_FLAG_THREAD_REUSE) == 0) { _pthread_wqthread_setup(self, kport, stacklowaddr, flags); } pthread_priority_t pp; if (flags & WQ_FLAG_THREAD_OUTSIDEQOS) { self->wq_outsideqos = 1; pp = _pthread_priority_make_from_thread_qos(THREAD_QOS_LEGACY, 0, _PTHREAD_PRIORITY_FALLBACK_FLAG); } else { self->wq_outsideqos = 0; pp = _pthread_wqthread_priority(flags); } self->tsd[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS] = (void *)pp; // Only used for conveying to libdispatch that the thread is coming out to // handle a cooperative request. pthread_workqueue_function2_arg_t workq_function2_arg = pp; if (flags & WQ_FLAG_THREAD_COOPERATIVE) { workq_function2_arg |= _PTHREAD_PRIORITY_COOPERATIVE_FLAG; } // avoid spills on the stack hard to keep used stack space minimal if (os_unlikely(nkevents == WORKQ_EXIT_THREAD_NKEVENT)) { … more in source
pthread_workqueue_setdispatchoffset_np source
void pthread_workqueue_setdispatchoffset_np(int offset) { __workq_kernreturn(WQOPS_QUEUE_NEWSPISUPP, NULL, offset, 0x00); }
pthread_workqueue_setup source
int pthread_workqueue_setup(struct pthread_workqueue_config *cfg, size_t cfg_size) { int rv = EBUSY; struct workq_dispatch_config wdc_cfg; size_t min_size = 0; if (cfg_size < sizeof(uint32_t)) { return EINVAL; } switch (cfg->version) { case 1: min_size = offsetof(struct pthread_workqueue_config, queue_label_offs); break; case 2: min_size = sizeof(struct pthread_workqueue_config); break; default: return EINVAL; } if (!cfg || cfg_size < min_size) { return EINVAL; } if (cfg->flags & ~PTHREAD_WORKQUEUE_CONFIG_SUPPORTED_FLAGS || cfg->version < PTHREAD_WORKQUEUE_CONFIG_MIN_SUPPORTED_VERSION) { return ENOTSUP; } … more in source
_pthread_workqueue_init_with_workloop source
int _pthread_workqueue_init_with_workloop(pthread_workqueue_function2_t queue_func, pthread_workqueue_function_kevent_t kevent_func, pthread_workqueue_function_workloop_t workloop_func, int offset, int flags) { struct pthread_workqueue_config cfg = { .version = PTHREAD_WORKQUEUE_CONFIG_VERSION, .flags = 0, .workq_cb = queue_func, .kevent_cb = kevent_func, .workloop_cb = workloop_func, .queue_serialno_offs = offset, .queue_label_offs = 0, }; return pthread_workqueue_setup(&cfg, sizeof(cfg)); }
_pthread_workqueue_init_with_kevent source
int _pthread_workqueue_init_with_kevent(pthread_workqueue_function2_t queue_func, pthread_workqueue_function_kevent_t kevent_func, int offset, int flags) { return _pthread_workqueue_init_with_workloop(queue_func, kevent_func, NULL, offset, flags); }
_pthread_workqueue_init source
int _pthread_workqueue_init(pthread_workqueue_function2_t func, int offset, int flags) { return _pthread_workqueue_init_with_kevent(func, NULL, offset, flags); }
pthread_workqueue_setdispatch_np source
int pthread_workqueue_setdispatch_np(pthread_workqueue_function_t worker_func) { struct pthread_workqueue_config cfg = { .version = PTHREAD_WORKQUEUE_CONFIG_VERSION, .flags = 0, .workq_cb = (uint64_t)(pthread_workqueue_function2_t)worker_func, .kevent_cb = 0, .workloop_cb = 0, .queue_serialno_offs = 0, .queue_label_offs = 0, }; return pthread_workqueue_setup(&cfg, sizeof(cfg)); }
_pthread_workqueue_supported source
int _pthread_workqueue_supported(void) { if (os_unlikely(!__pthread_supported_features)) { PTHREAD_INTERNAL_CRASH(0, "libpthread has not been initialized"); } return __pthread_supported_features; }
pthread_workqueue_addthreads_np source
int pthread_workqueue_addthreads_np(int queue_priority, int options, int numthreads) { int res = 0; // Cannot add threads without a worker function registered. if (__libdispatch_workerfunction == NULL) { return EPERM; } pthread_priority_t kp = 0; int compat_priority = queue_priority & WQ_FLAG_THREAD_PRIO_MASK; int flags = 0; if (options & WORKQ_ADDTHREADS_OPTION_OVERCOMMIT) { flags = _PTHREAD_PRIORITY_OVERCOMMIT_FLAG; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" kp = _pthread_qos_class_encode_workqueue(compat_priority, flags); #pragma clang diagnostic pop res = __workq_kernreturn(WQOPS_QUEUE_REQTHREADS, NULL, numthreads, (int)kp); if (res == -1) { res = errno; } return res; }
_pthread_workqueue_should_narrow source
bool _pthread_workqueue_should_narrow(pthread_priority_t pri) { int res = __workq_kernreturn(WQOPS_SHOULD_NARROW, NULL, (int)pri, 0); if (res == -1) { return false; } return res; }
_pthread_workqueue_addthreads source
int _pthread_workqueue_addthreads(int numthreads, pthread_priority_t priority) { int res = 0; if (__libdispatch_workerfunction == NULL) { return EPERM; } #if TARGET_OS_OSX // <rdar://problem/37687655> Legacy simulators fail to boot // // Older sims set the deprecated _PTHREAD_PRIORITY_ROOTQUEUE_FLAG wrongly, // which is aliased to _PTHREAD_PRIORITY_SCHED_PRI_FLAG and that XNU // validates and rejects. // // As a workaround, forcefully unset this bit that cannot be set here // anyway. priority &= ~_PTHREAD_PRIORITY_SCHED_PRI_FLAG; #endif res = __workq_kernreturn(WQOPS_QUEUE_REQTHREADS, NULL, numthreads, (int)priority); if (res == -1) { res = errno; } return res; }
_pthread_workqueue_add_cooperativethreads source
int _pthread_workqueue_add_cooperativethreads(int numthreads, pthread_priority_t priority) { int res = 0; if (__libdispatch_workerfunction == NULL) { return EPERM; } res = __workq_kernreturn(WQOPS_QUEUE_REQTHREADS2, NULL, numthreads, (int)priority); if (res == -1) { res = errno; } return res; }
_pthread_workqueue_set_event_manager_priority source
int _pthread_workqueue_set_event_manager_priority(pthread_priority_t priority) { int res = __workq_kernreturn(WQOPS_SET_EVENT_MANAGER_PRIORITY, NULL, (int)priority, 0); if (res == -1) { res = errno; } return res; }
_pthread_workqueue_allow_send_signals source
int _pthread_workqueue_allow_send_signals(int preserve_signum) { // For chroot, the syscall would just return EINVAL until the // build platform moves to SunburstE. sigset_t set = sigmask(preserve_signum); return __bsdthread_ctl(BSDTHREAD_CTL_WORKQ_ALLOW_SIGMASK, set, 0, 0); }
_pthread_workloop_create source
int _pthread_workloop_create(uint64_t workloop_id, uint64_t options, pthread_attr_t *attr) { struct kqueue_workloop_params params = { .kqwlp_version = sizeof(struct kqueue_workloop_params), .kqwlp_id = workloop_id, .kqwlp_flags = 0, }; if ((!attr) || (options & ~PTHREAD_WORKLOOP_CREATE_OPTIONS_MASK)) { return EINVAL; } if (attr->schedset) { params.kqwlp_flags |= KQ_WORKLOOP_CREATE_SCHED_PRI; params.kqwlp_sched_pri = attr->param.sched_priority; } if (attr->policyset) { params.kqwlp_flags |= KQ_WORKLOOP_CREATE_SCHED_POL; params.kqwlp_sched_pol = attr->policy; } if (attr->cpupercentset) { params.kqwlp_flags |= KQ_WORKLOOP_CREATE_CPU_PERCENT; params.kqwlp_cpu_percent = attr->cpupercent; params.kqwlp_cpu_refillms = attr->refillms; } if (MACH_PORT_VALID(attr->work_interval_port)) { … more in source
_pthread_workloop_destroy source
int _pthread_workloop_destroy(uint64_t workloop_id) { struct kqueue_workloop_params params = { .kqwlp_version = sizeof(struct kqueue_workloop_params), .kqwlp_id = workloop_id, }; int res = __kqueue_workloop_ctl(KQ_WORKLOOP_DESTROY, 0, ¶ms, sizeof(params)); if (res == -1) { res = errno; } return res; }
pthread_introspection_hook_install source
pthread_introspection_hook_t pthread_introspection_hook_install(pthread_introspection_hook_t hook) { pthread_introspection_hook_t prev; prev = _pthread_atomic_xchg_ptr((void**)&_pthread_introspection_hook, hook); return prev; }
_pthread_introspection_call_hook source
static inline void _pthread_introspection_call_hook(unsigned int event, pthread_t thread, void *addr, size_t size) { pthread_t self = pthread_self(); uint16_t old = self->introspection; self->introspection = (uint16_t)event; _pthread_introspection_hook(event, thread, addr, size); self->introspection = old; }
_pthread_introspection_hook_callout_thread_create source
OS_NOINLINE static void _pthread_introspection_hook_callout_thread_create(pthread_t t) { _pthread_introspection_call_hook(PTHREAD_INTROSPECTION_THREAD_CREATE, t, t, PTHREAD_SIZE); }
_pthread_introspection_thread_create source
static inline void _pthread_introspection_thread_create(pthread_t t) { if (os_fastpath(!_pthread_introspection_hook)) return; _pthread_introspection_hook_callout_thread_create(t); }
_pthread_introspection_hook_callout_thread_start source
OS_NOINLINE static void _pthread_introspection_hook_callout_thread_start(pthread_t t) { size_t freesize; void *freeaddr; if (t == main_thread()) { size_t stacksize = t->stackaddr - t->stackbottom; freesize = stacksize + t->guardsize; freeaddr = t->stackaddr - freesize; } else { freesize = t->freesize - PTHREAD_SIZE; freeaddr = t->freeaddr; } _pthread_introspection_call_hook(PTHREAD_INTROSPECTION_THREAD_START, t, freeaddr, freesize); }
_pthread_introspection_thread_start source
static inline void _pthread_introspection_thread_start(pthread_t t) { if (os_fastpath(!_pthread_introspection_hook)) return; _pthread_introspection_hook_callout_thread_start(t); }
_pthread_introspection_hook_callout_thread_terminate source
OS_NOINLINE static void _pthread_introspection_hook_callout_thread_terminate(pthread_t t) { size_t freesize; void *freeaddr; if (t == main_thread()) { size_t stacksize = t->stackaddr - t->stackbottom; freesize = stacksize + t->guardsize; freeaddr = t->stackaddr - freesize; } else { freesize = t->freesize - PTHREAD_SIZE; freeaddr = t->freeaddr; } _pthread_introspection_call_hook(PTHREAD_INTROSPECTION_THREAD_TERMINATE, t, freeaddr, freesize); }
_pthread_introspection_thread_terminate source
static inline void _pthread_introspection_thread_terminate(pthread_t t) { if (os_fastpath(!_pthread_introspection_hook)) return; _pthread_introspection_hook_callout_thread_terminate(t); }
_pthread_introspection_hook_callout_thread_destroy source
OS_NOINLINE static void _pthread_introspection_hook_callout_thread_destroy(pthread_t t) { _pthread_introspection_call_hook(PTHREAD_INTROSPECTION_THREAD_DESTROY, t, t, PTHREAD_SIZE); }
_pthread_introspection_thread_destroy source
static inline void _pthread_introspection_thread_destroy(pthread_t t) { if (os_fastpath(!_pthread_introspection_hook)) return; _pthread_introspection_hook_callout_thread_destroy(t); }
_pthread_once_context source struct
struct _pthread_once_context { pthread_once_t *pthread_once; void (*routine)(void); }