libpthread/src/qos.c

1/* 2 * Copyright (c) 2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "internal.h" 25 26#include <mach/mach_vm.h> 27#include <unistd.h> 28#include <spawn.h> 29#include <spawn_private.h> 30#include <pthread/spawn.h> 31#include <sys/spawn_internal.h> 32#include <sys/ulock.h> 33 34#define PTHREAD_OVERRIDE_SIGNATURE (0x6f766572) 35#define PTHREAD_OVERRIDE_SIG_DEAD (0x7265766f) 36 37#if !defined(VARIANT_STATIC) 38// internally redirected upcalls in case qos overrides are used 39// before __pthread_init has run 40PTHREAD_NOEXPORT void * 41malloc(size_t sz) 42{ 43 if (os_likely(_pthread_malloc)) { 44 return _pthread_malloc(sz); 45 } else { 46 return NULL; 47 } 48} 49 50PTHREAD_NOEXPORT void 51free(void *p) 52{ 53 if (os_likely(_pthread_free)) { 54 _pthread_free(p); 55 } 56} 57#endif // VARIANT_STATIC 58 59struct pthread_override_s 60{ 61 uint32_t sig; 62 mach_port_t kthread; 63 pthread_t pthread; 64 pthread_priority_t priority; 65 bool malloced; 66}; 67 68thread_qos_t 69_pthread_qos_class_to_thread_qos(qos_class_t qos) 70{ 71 switch (qos) { 72 case QOS_CLASS_USER_INTERACTIVE: return THREAD_QOS_USER_INTERACTIVE; 73 case QOS_CLASS_USER_INITIATED: return THREAD_QOS_USER_INITIATED; 74 case QOS_CLASS_DEFAULT: return THREAD_QOS_LEGACY; 75 case QOS_CLASS_UTILITY: return THREAD_QOS_UTILITY; 76 case QOS_CLASS_BACKGROUND: return THREAD_QOS_BACKGROUND; 77 case QOS_CLASS_MAINTENANCE: return THREAD_QOS_MAINTENANCE; 78 default: return THREAD_QOS_UNSPECIFIED; 79 } 80} 81 82static inline qos_class_t 83_pthread_qos_class_from_thread_qos(thread_qos_t tqos) 84{ 85 static const qos_class_t thread_qos_to_qos_class[THREAD_QOS_LAST] = { 86 [THREAD_QOS_UNSPECIFIED] = QOS_CLASS_UNSPECIFIED, 87 [THREAD_QOS_MAINTENANCE] = QOS_CLASS_MAINTENANCE, 88 [THREAD_QOS_BACKGROUND] = QOS_CLASS_BACKGROUND, 89 [THREAD_QOS_UTILITY] = QOS_CLASS_UTILITY, 90 [THREAD_QOS_LEGACY] = QOS_CLASS_DEFAULT, 91 [THREAD_QOS_USER_INITIATED] = QOS_CLASS_USER_INITIATED, 92 [THREAD_QOS_USER_INTERACTIVE] = QOS_CLASS_USER_INTERACTIVE, 93 }; 94 if (os_unlikely(tqos >= THREAD_QOS_LAST)) return QOS_CLASS_UNSPECIFIED; 95 return thread_qos_to_qos_class[tqos]; 96} 97 98static inline thread_qos_t 99_pthread_validate_qos_class_and_relpri(qos_class_t qc, int relpri) 100{ 101 if (relpri > 0 || relpri < QOS_MIN_RELATIVE_PRIORITY) { 102 return THREAD_QOS_UNSPECIFIED; 103 } 104 return _pthread_qos_class_to_thread_qos(qc); 105} 106 107static inline void 108_pthread_priority_split(pthread_priority_t pp, qos_class_t *qc, int *relpri) 109{ 110 thread_qos_t qos = _pthread_priority_thread_qos(pp); 111 if (qc) *qc = _pthread_qos_class_from_thread_qos(qos); 112 if (relpri) *relpri = _pthread_priority_relpri(pp); 113} 114 115void 116_pthread_set_main_qos(pthread_priority_t qos) 117{ 118 _main_qos = (uint32_t)qos; 119} 120 121int 122pthread_attr_set_qos_class_np(pthread_attr_t *attr, qos_class_t qc, int relpri) 123{ 124 thread_qos_t qos = _pthread_validate_qos_class_and_relpri(qc, relpri); 125 if (attr->sig != _PTHREAD_ATTR_SIG || attr->schedset) { 126 return EINVAL; 127 } 128 129 attr->qosclass = _pthread_priority_make_from_thread_qos(qos, relpri, 0); 130 attr->qosset = 1; 131 attr->schedset = 0; 132 return 0; 133} 134 135int 136pthread_attr_get_qos_class_np(pthread_attr_t *attr, qos_class_t *qc, int *relpri) 137{ 138 if (attr->sig != _PTHREAD_ATTR_SIG) { 139 return EINVAL; 140 } 141 142 _pthread_priority_split(attr->qosset ? attr->qosclass : 0, qc, relpri); 143 return 0; 144} 145 146int 147pthread_set_qos_class_self_np(qos_class_t qc, int relpri) 148{ 149 thread_qos_t qos = _pthread_validate_qos_class_and_relpri(qc, relpri); 150 if (!qos) { 151 return EINVAL; 152 } 153 154 pthread_priority_t pp = _pthread_priority_make_from_thread_qos(qos, relpri, 0); 155 return _pthread_set_properties_self(_PTHREAD_SET_SELF_QOS_FLAG, pp, 0); 156} 157 158int 159pthread_set_qos_class_np(pthread_t thread, qos_class_t qc, int relpri) 160{ 161 if (thread != pthread_self()) { 162 /* The kext now enforces this anyway, if we check here too, it allows us to call 163 * _pthread_set_properties_self later if we can. 164 */ 165 return EPERM; 166 } 167 _pthread_validate_signature(thread); 168 return pthread_set_qos_class_self_np(qc, relpri); 169} 170 171int 172pthread_get_qos_class_np(pthread_t thread, qos_class_t *qc, int *relpri) 173{ 174 pthread_priority_t pp = _pthread_tsd_slot(thread, PTHREAD_QOS_CLASS); 175 _pthread_priority_split(pp, qc, relpri); 176 return 0; 177} 178 179qos_class_t 180qos_class_self(void) 181{ 182 pthread_priority_t pp; 183 pp = _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS); 184 return _pthread_qos_class_from_thread_qos(_pthread_priority_thread_qos(pp)); 185} 186 187qos_class_t 188qos_class_main(void) 189{ 190 pthread_priority_t pp = _main_qos; 191 return _pthread_qos_class_from_thread_qos(_pthread_priority_thread_qos(pp)); 192} 193 194pthread_priority_t 195_pthread_qos_class_encode(qos_class_t qc, int relpri, unsigned long flags) 196{ 197 thread_qos_t qos = _pthread_qos_class_to_thread_qos(qc); 198 return _pthread_priority_make_from_thread_qos(qos, relpri, flags); 199} 200 201qos_class_t 202_pthread_qos_class_decode(pthread_priority_t pp, int *relpri, unsigned long *flags) 203{ 204 qos_class_t qc; 205 _pthread_priority_split(pp, &qc, relpri); 206 if (flags) *flags = (pp & _PTHREAD_PRIORITY_FLAGS_MASK); 207 return qc; 208} 209 210pthread_priority_t 211_pthread_qos_class_and_override_encode(qos_class_t qc, int relpri, unsigned long flags, qos_class_t qco) 212{ 213 thread_qos_t req = _pthread_qos_class_to_thread_qos(qc); 214 thread_qos_t ovr = _pthread_qos_class_to_thread_qos(qco); 215 return _pthread_priority_make_from_thread_qos_and_override(req, relpri, ovr, flags); 216} 217 218qos_class_t 219_pthread_qos_class_and_override_decode(pthread_priority_t pp, int *relpri, 220 unsigned long *flags, qos_class_t *qco) 221{ 222 thread_qos_t req = _pthread_priority_thread_qos(pp); 223 thread_qos_t ovr = _pthread_priority_thread_override_qos(pp); 224 225 if (qco) *qco = _pthread_qos_class_from_thread_qos(ovr); 226 if (flags) *flags = (pp & _PTHREAD_PRIORITY_FLAGS_MASK); 227 if (relpri) *relpri = _pthread_priority_relpri(pp); 228 229 return _pthread_qos_class_from_thread_qos(req); 230} 231 232pthread_priority_t 233_pthread_sched_pri_encode(int sched_pri, unsigned long flags) 234{ 235 return _pthread_priority_make_from_sched_pri(sched_pri, flags); 236} 237 238int 239_pthread_sched_pri_decode(pthread_priority_t pp, unsigned long *flags) 240{ 241 // Strip out SCHED_PRI flag and keep the rest 242 if (flags) 243 *flags = (pp & (_PTHREAD_PRIORITY_FLAGS_MASK & ~_PTHREAD_PRIORITY_SCHED_PRI_FLAG)); 244 245 return _pthread_priority_sched_pri(pp); 246} 247 248// Encode a legacy workqueue API priority into a pthread_priority_t. This API 249// is deprecated and can be removed when the simulator no longer uses it. 250pthread_priority_t 251_pthread_qos_class_encode_workqueue(int queue_priority, unsigned long flags) 252{ 253 thread_qos_t qos; 254 switch (queue_priority) { 255 case WORKQ_HIGH_PRIOQUEUE: qos = THREAD_QOS_USER_INTERACTIVE; break; 256 case WORKQ_DEFAULT_PRIOQUEUE: qos = THREAD_QOS_LEGACY; break; 257 case WORKQ_NON_INTERACTIVE_PRIOQUEUE: 258 case WORKQ_LOW_PRIOQUEUE: qos = THREAD_QOS_UTILITY; break; 259 case WORKQ_BG_PRIOQUEUE: qos = THREAD_QOS_BACKGROUND; break; 260 default: 261 PTHREAD_CLIENT_CRASH(queue_priority, "Invalid priority"); 262 } 263 return _pthread_priority_make_from_thread_qos(qos, 0, flags); 264} 265 266#define _PTHREAD_SET_SELF_OUTSIDE_QOS_SKIP \ 267 (_PTHREAD_SET_SELF_QOS_FLAG | _PTHREAD_SET_SELF_FIXEDPRIORITY_FLAG | \ 268 _PTHREAD_SET_SELF_TIMESHARE_FLAG | \ 269 _PTHREAD_SET_SELF_ALTERNATE_CLUSTER) 270 271int 272_pthread_set_properties_self(_pthread_set_flags_t flags, 273 pthread_priority_t priority, mach_port_t voucher) 274{ 275 276 pthread_t self = pthread_self(); 277 _pthread_set_flags_t kflags = flags; 278 int rv = 0; 279 280 _pthread_validate_signature(self); 281 282 if (self->wq_outsideqos && (flags & _PTHREAD_SET_SELF_OUTSIDE_QOS_SKIP)) { 283 // A number of properties cannot be altered if we are a workloop 284 // thread that has outside of QoS properties applied to it. 285 kflags &= ~_PTHREAD_SET_SELF_OUTSIDE_QOS_SKIP; 286 if (kflags == 0) goto skip; 287 } 288 289 rv = __bsdthread_ctl(BSDTHREAD_CTL_SET_SELF, priority, voucher, kflags); 290 291skip: 292 // Set QoS TSD if we succeeded, or only failed the voucher portion of the 293 // call. Additionally, if we skipped setting QoS because of outside-of-QoS 294 // attributes then we still want to set the TSD in userspace. 295 if ((flags & _PTHREAD_SET_SELF_QOS_FLAG) != 0) { 296 if (rv == 0 || errno == ENOENT) { 297 298 // Mask away any override information in the priority and only put req QoS 299 // information in the TSD 300 priority &= ~(_PTHREAD_PRIORITY_OVERRIDE_QOS_FLAG | _PTHREAD_PRIORITY_VALID_OVERRIDE_QOS_MASK); 301 302 _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS, priority); 303 } 304 } 305 306 if (rv) { 307 rv = errno; 308 } 309 return rv; 310} 311 312int 313pthread_set_fixedpriority_self(void) 314{ 315 return _pthread_set_properties_self(_PTHREAD_SET_SELF_FIXEDPRIORITY_FLAG, 0, 0); 316} 317 318int 319pthread_set_timeshare_self(void) 320{ 321 return _pthread_set_properties_self(_PTHREAD_SET_SELF_TIMESHARE_FLAG, 0, 0); 322} 323 324int 325pthread_prefer_alternate_cluster_self(void) 326{ 327 return _pthread_set_properties_self(_PTHREAD_SET_SELF_ALTERNATE_CLUSTER, 0, 0); 328} 329 330 331pthread_override_t 332pthread_override_qos_class_start_np(pthread_t thread, qos_class_t qc, int relpri) 333{ 334 pthread_override_t rv; 335 kern_return_t kr; 336 thread_qos_t qos; 337 int res = 0; 338 339 /* For now, we don't have access to malloc. So we'll have to vm_allocate this, which means the tiny struct is going 340 * to use an entire page. 341 */ 342 bool did_malloc = true; 343 344 qos = _pthread_validate_qos_class_and_relpri(qc, relpri); 345 if (qos == THREAD_QOS_UNSPECIFIED) { 346 return (_Nonnull pthread_override_t)NULL; 347 } 348 349 mach_vm_address_t vm_addr = malloc(sizeof(struct pthread_override_s)); 350 if (!vm_addr) { 351 vm_addr = vm_page_size; 352 did_malloc = false; 353 354 kr = mach_vm_allocate(mach_task_self(), &vm_addr, 355 round_page(sizeof(struct pthread_override_s)), 356 VM_MAKE_TAG(VM_MEMORY_LIBDISPATCH) | VM_FLAGS_ANYWHERE); 357 if (kr != KERN_SUCCESS) { 358 errno = ENOMEM; 359 return (_Nonnull pthread_override_t)NULL; 360 } 361 } 362 363 rv = (pthread_override_t)vm_addr; 364 rv->sig = PTHREAD_OVERRIDE_SIGNATURE; 365 rv->pthread = thread; 366 rv->kthread = pthread_mach_thread_np(thread); 367 rv->priority = _pthread_priority_make_from_thread_qos(qos, relpri, 0); 368 rv->malloced = did_malloc; 369 370 /* To ensure that the kernel port that we keep stays valid, we retain it here. */ 371 kr = mach_port_mod_refs(mach_task_self(), rv->kthread, MACH_PORT_RIGHT_SEND, 1); 372 if (kr != KERN_SUCCESS) { 373 res = EINVAL; 374 } 375 376 if (res == 0) { 377 res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_START, rv->kthread, rv->priority, (uintptr_t)rv); 378 379 if (res != 0) { 380 mach_port_mod_refs(mach_task_self(), rv->kthread, MACH_PORT_RIGHT_SEND, -1); 381 } 382 } 383 384 if (res != 0) { 385 if (did_malloc) { 386 free(rv); 387 } else { 388 mach_vm_deallocate(mach_task_self(), vm_addr, round_page(sizeof(struct pthread_override_s))); 389 } 390 rv = NULL; 391 } 392 return (_Nonnull pthread_override_t)rv; 393} 394 395int 396pthread_override_qos_class_end_np(pthread_override_t override) 397{ 398 kern_return_t kr; 399 int res = 0; 400 401 /* Double-free is a fault. Swap the signature and check the old one. */ 402 if (_pthread_atomic_xchg_uint32_relaxed(&override->sig, PTHREAD_OVERRIDE_SIG_DEAD) != PTHREAD_OVERRIDE_SIGNATURE) { 403 __builtin_trap(); 404 } 405 406 /* Always consumes (and deallocates) the pthread_override_t object given. */ 407 res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_END, override->kthread, (uintptr_t)override, 0); 408 if (res == -1) { res = errno; } 409 410 /* EFAULT from the syscall means we underflowed. Crash here. */ 411 if (res == EFAULT) { 412 // <rdar://problem/17645082> Disable the trap-on-underflow, it doesn't co-exist 413 // with dispatch resetting override counts on threads. 414 //__builtin_trap(); 415 res = 0; 416 } 417 418 kr = mach_port_mod_refs(mach_task_self(), override->kthread, MACH_PORT_RIGHT_SEND, -1); 419 if (kr != KERN_SUCCESS) { 420 res = EINVAL; 421 } 422 423 if (override->malloced) { 424 free(override); 425 } else { 426 kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)override, round_page(sizeof(struct pthread_override_s))); 427 if (kr != KERN_SUCCESS) { 428 res = EINVAL; 429 } 430 } 431 432 return res; 433} 434 435int 436_pthread_qos_override_start_direct(mach_port_t thread, pthread_priority_t priority, void *resource) 437{ 438 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_START, thread, priority, (uintptr_t)resource); 439 if (res == -1) { res = errno; } 440 return res; 441} 442 443int 444_pthread_qos_override_end_direct(mach_port_t thread, void *resource) 445{ 446 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_END, thread, (uintptr_t)resource, 0); 447 if (res == -1) { res = errno; } 448 return res; 449} 450 451int 452_pthread_override_qos_class_start_direct(mach_port_t thread, pthread_priority_t priority) 453{ 454 // use pthread_self as the default per-thread memory allocation to track the override in the kernel 455 return _pthread_qos_override_start_direct(thread, priority, pthread_self()); 456} 457 458int 459_pthread_override_qos_class_end_direct(mach_port_t thread) 460{ 461 // use pthread_self as the default per-thread memory allocation to track the override in the kernel 462 return _pthread_qos_override_end_direct(thread, pthread_self()); 463} 464 465int 466_pthread_workqueue_override_start_direct(mach_port_t thread, pthread_priority_t priority) 467{ 468 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH, thread, priority, 0); 469 if (res == -1) { res = errno; } 470 return res; 471} 472 473int 474_pthread_workqueue_override_start_direct_check_owner(mach_port_t thread, pthread_priority_t priority, mach_port_t *ulock_addr) 475{ 476#if !TARGET_OS_IPHONE 477 static boolean_t kernel_supports_owner_check = TRUE; 478 if (!kernel_supports_owner_check) { 479 ulock_addr = NULL; 480 } 481#endif 482 483 for (;;) { 484 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH, thread, priority, ulock_addr); 485 if (res == -1) { res = errno; } 486#if !TARGET_OS_IPHONE 487 if (ulock_addr && res == EINVAL) { 488 if ((uintptr_t)ulock_addr % _Alignof(_Atomic uint32_t)) { 489 // do not mute bad ulock addresses related errors 490 return EINVAL; 491 } 492 // backward compatibility for the XBS chroot 493 // BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH used to return EINVAL if 494 // arg3 was non NULL. 495 kernel_supports_owner_check = FALSE; 496 ulock_addr = NULL; 497 continue; 498 } 499#endif 500 if (ulock_addr && res == EFAULT) { 501 // kernel wants us to redrive the call, so while we refault the 502 // memory, also revalidate the owner 503 uint32_t uval = *(uint32_t volatile *)ulock_addr; 504 if (ulock_owner_value_to_port_name(uval) != thread) { 505 return ESTALE; 506 } 507 continue; 508 } 509 510 return res; 511 } 512} 513 514int 515_pthread_workqueue_override_reset(void) 516{ 517 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_RESET, 0, 0, 0); 518 if (res == -1) { res = errno; } 519 return res; 520} 521 522int 523_pthread_workqueue_asynchronous_override_add(mach_port_t thread, pthread_priority_t priority, void *resource) 524{ 525 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_ADD, thread, priority, (uintptr_t)resource); 526 if (res == -1) { res = errno; } 527 return res; 528} 529 530int 531_pthread_workqueue_asynchronous_override_reset_self(void *resource) 532{ 533 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_RESET, 534 0 /* !reset_all */, 535 (uintptr_t)resource, 536 0); 537 if (res == -1) { res = errno; } 538 return res; 539} 540 541int 542_pthread_workqueue_asynchronous_override_reset_all_self(void) 543{ 544 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_RESET, 545 1 /* reset_all */, 546 0, 547 0); 548 if (res == -1) { res = errno; } 549 return res; 550} 551 552static inline uint16_t 553_pthread_workqueue_parallelism_for_priority(int qos, unsigned long flags) 554{ 555 int rc = __bsdthread_ctl(BSDTHREAD_CTL_QOS_MAX_PARALLELISM, qos, flags, 0); 556 if (os_unlikely(rc == -1)) { 557 rc = errno; 558 if (rc != EINVAL && rc != ENOTSUP) { 559 PTHREAD_INTERNAL_CRASH(rc, "qos_max_parallelism failed"); 560 } 561 if (flags & _PTHREAD_QOS_PARALLELISM_COUNT_LOGICAL) { 562 return *(uint8_t *)_COMM_PAGE_LOGICAL_CPUS; 563 } else if (flags & _PTHREAD_QOS_PARALLELISM_CLUSTER_SHARED_RSRC) { 564 if (rc == ENOTSUP) { 565 return 0; /* Not supported on this hardware */ 566 } else { 567 __builtin_trap(); 568 } 569 } else { 570 return *(uint8_t *)_COMM_PAGE_PHYSICAL_CPUS; 571 } 572 } 573 return (uint16_t)rc; 574} 575 576int 577pthread_qos_max_parallelism(qos_class_t qos, unsigned long flags) 578{ 579 thread_qos_t thread_qos; 580 if (qos == QOS_CLASS_UNSPECIFIED) { 581 qos = QOS_CLASS_DEFAULT; // <rdar://problem/35080198> 582 } 583 thread_qos = _pthread_qos_class_to_thread_qos(qos); 584 if (thread_qos == THREAD_QOS_UNSPECIFIED) { 585 errno = EINVAL; 586 return -1; 587 } 588 589 if (flags & ~(PTHREAD_MAX_PARALLELISM_PHYSICAL | PTHREAD_MAX_PARALLELISM_CLUSTER)) { 590 errno = EINVAL; 591 return -1; 592 } 593 594 unsigned long syscall_flags = _PTHREAD_QOS_PARALLELISM_COUNT_LOGICAL; 595 uint16_t *ptr = &_pthread_globals()->qmp_logical[thread_qos]; 596 597 if (flags & PTHREAD_MAX_PARALLELISM_PHYSICAL) { 598 syscall_flags = 0; 599 ptr = &_pthread_globals()->qmp_physical[thread_qos]; 600 } else if (flags & PTHREAD_MAX_PARALLELISM_CLUSTER) { 601 syscall_flags = _PTHREAD_QOS_PARALLELISM_CLUSTER_SHARED_RSRC; 602 ptr = &_pthread_globals()->cluster_physical[thread_qos]; 603 } 604 605 if (*ptr == QOS_PARALLELISM_NOT_QUERIED) { 606 *ptr = _pthread_workqueue_parallelism_for_priority(thread_qos, syscall_flags); 607 } 608 return *ptr; 609} 610 611int 612pthread_time_constraint_max_parallelism(unsigned long flags) 613{ 614 unsigned long syscall_flags = _PTHREAD_QOS_PARALLELISM_COUNT_LOGICAL; 615 uint16_t *ptr = &_pthread_globals()->qmp_logical[0]; 616 617 if (flags & PTHREAD_MAX_PARALLELISM_PHYSICAL) { 618 syscall_flags = 0; 619 ptr = &_pthread_globals()->qmp_physical[0]; 620 } else if (flags & PTHREAD_MAX_PARALLELISM_CLUSTER) { 621 errno = EINVAL; 622 return -1; 623 } 624 625 if (*ptr == QOS_PARALLELISM_NOT_QUERIED) { 626 *ptr = _pthread_workqueue_parallelism_for_priority(0, 627 syscall_flags | _PTHREAD_QOS_PARALLELISM_REALTIME); 628 } 629 return *ptr; 630} 631 632int 633posix_spawnattr_set_qos_class_np(posix_spawnattr_t * __restrict __attr, qos_class_t __qos_class) 634{ 635 switch (__qos_class) { 636 case QOS_CLASS_UTILITY: 637 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_UTILITY); 638 case QOS_CLASS_BACKGROUND: 639 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_BACKGROUND); 640 case QOS_CLASS_MAINTENANCE: 641 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_MAINTENANCE); 642 default: 643 return EINVAL; 644 } 645} 646 647int 648posix_spawnattr_get_qos_class_np(const posix_spawnattr_t *__restrict __attr, qos_class_t * __restrict __qos_class) 649{ 650 uint64_t clamp; 651 652 if (!__qos_class) { 653 return EINVAL; 654 } 655 656 int rv = posix_spawnattr_get_qos_clamp_np(__attr, &clamp); 657 if (rv != 0) { 658 return rv; 659 } 660 661 switch (clamp) { 662 case POSIX_SPAWN_PROC_CLAMP_UTILITY: 663 *__qos_class = QOS_CLASS_UTILITY; 664 break; 665 case POSIX_SPAWN_PROC_CLAMP_BACKGROUND: 666 *__qos_class = QOS_CLASS_BACKGROUND; 667 break; 668 case POSIX_SPAWN_PROC_CLAMP_MAINTENANCE: 669 *__qos_class = QOS_CLASS_MAINTENANCE; 670 break; 671 default: 672 *__qos_class = QOS_CLASS_UNSPECIFIED; 673 break; 674 } 675 676 return 0; 677}