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}