]> git.saurik.com Git - apple/libpthread.git/blame - src/qos.c
libpthread-330.250.2.tar.gz
[apple/libpthread.git] / src / qos.c
CommitLineData
f1a1da6c
A
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 <_simple.h>
27#include <mach/mach_vm.h>
28#include <unistd.h>
29#include <spawn.h>
30#include <spawn_private.h>
31#include <sys/spawn_internal.h>
2546420a 32#include <sys/ulock.h>
f1a1da6c
A
33
34// TODO: remove me when internal.h can include *_private.h itself
35#include "workqueue_private.h"
36#include "qos_private.h"
37
f1a1da6c
A
38#define PTHREAD_OVERRIDE_SIGNATURE (0x6f766572)
39#define PTHREAD_OVERRIDE_SIG_DEAD (0x7265766f)
40
41struct pthread_override_s
42{
43 uint32_t sig;
f1a1da6c 44 mach_port_t kthread;
964d3577 45 pthread_t pthread;
f1a1da6c
A
46 pthread_priority_t priority;
47 bool malloced;
48};
49
214d78a2
A
50thread_qos_t
51_pthread_qos_class_to_thread_qos(qos_class_t qos)
f1a1da6c 52{
214d78a2
A
53 switch (qos) {
54 case QOS_CLASS_USER_INTERACTIVE: return THREAD_QOS_USER_INTERACTIVE;
55 case QOS_CLASS_USER_INITIATED: return THREAD_QOS_USER_INITIATED;
56 case QOS_CLASS_DEFAULT: return THREAD_QOS_LEGACY;
57 case QOS_CLASS_UTILITY: return THREAD_QOS_UTILITY;
58 case QOS_CLASS_BACKGROUND: return THREAD_QOS_BACKGROUND;
59 case QOS_CLASS_MAINTENANCE: return THREAD_QOS_MAINTENANCE;
60 default: return THREAD_QOS_UNSPECIFIED;
61 }
f1a1da6c
A
62}
63
214d78a2
A
64static inline qos_class_t
65_pthread_qos_class_from_thread_qos(thread_qos_t tqos)
f1a1da6c 66{
214d78a2
A
67 static const qos_class_t thread_qos_to_qos_class[THREAD_QOS_LAST] = {
68 [THREAD_QOS_UNSPECIFIED] = QOS_CLASS_UNSPECIFIED,
69 [THREAD_QOS_MAINTENANCE] = QOS_CLASS_MAINTENANCE,
70 [THREAD_QOS_BACKGROUND] = QOS_CLASS_BACKGROUND,
71 [THREAD_QOS_UTILITY] = QOS_CLASS_UTILITY,
72 [THREAD_QOS_LEGACY] = QOS_CLASS_DEFAULT,
73 [THREAD_QOS_USER_INITIATED] = QOS_CLASS_USER_INITIATED,
74 [THREAD_QOS_USER_INTERACTIVE] = QOS_CLASS_USER_INTERACTIVE,
75 };
76 if (os_unlikely(tqos >= THREAD_QOS_LAST)) return QOS_CLASS_UNSPECIFIED;
77 return thread_qos_to_qos_class[tqos];
78}
f1a1da6c 79
214d78a2
A
80static inline thread_qos_t
81_pthread_validate_qos_class_and_relpri(qos_class_t qc, int relpri)
82{
83 if (relpri > 0 || relpri < QOS_MIN_RELATIVE_PRIORITY) {
84 return THREAD_QOS_UNSPECIFIED;
f1a1da6c 85 }
214d78a2
A
86 return _pthread_qos_class_to_thread_qos(qc);
87}
f1a1da6c 88
214d78a2
A
89static inline void
90_pthread_priority_split(pthread_priority_t pp, qos_class_t *qc, int *relpri)
91{
92 thread_qos_t qos = _pthread_priority_thread_qos(pp);
93 if (qc) *qc = _pthread_qos_class_from_thread_qos(qos);
94 if (relpri) *relpri = _pthread_priority_relpri(pp);
95}
f1a1da6c 96
214d78a2
A
97void
98_pthread_set_main_qos(pthread_priority_t qos)
99{
100 _main_qos = (uint32_t)qos;
f1a1da6c
A
101}
102
103int
214d78a2 104pthread_attr_set_qos_class_np(pthread_attr_t *attr, qos_class_t qc, int relpri)
f1a1da6c 105{
214d78a2
A
106 thread_qos_t qos = _pthread_validate_qos_class_and_relpri(qc, relpri);
107 if (attr->sig != _PTHREAD_ATTR_SIG || attr->schedset) {
108 return EINVAL;
f1a1da6c
A
109 }
110
214d78a2
A
111 attr->qosclass = _pthread_priority_make_from_thread_qos(qos, relpri, 0);
112 attr->qosset = 1;
113 attr->schedset = 0;
114 return 0;
115}
f1a1da6c 116
214d78a2
A
117int
118pthread_attr_get_qos_class_np(pthread_attr_t *attr, qos_class_t *qc, int *relpri)
119{
120 if (attr->sig != _PTHREAD_ATTR_SIG) {
121 return EINVAL;
f1a1da6c
A
122 }
123
214d78a2
A
124 _pthread_priority_split(attr->qosset ? attr->qosclass : 0, qc, relpri);
125 return 0;
f1a1da6c
A
126}
127
128int
214d78a2 129pthread_set_qos_class_self_np(qos_class_t qc, int relpri)
f1a1da6c 130{
214d78a2
A
131 thread_qos_t qos = _pthread_validate_qos_class_and_relpri(qc, relpri);
132 if (!qos) {
f1a1da6c
A
133 return EINVAL;
134 }
135
214d78a2
A
136 pthread_priority_t pp = _pthread_priority_make_from_thread_qos(qos, relpri, 0);
137 return _pthread_set_properties_self(_PTHREAD_SET_SELF_QOS_FLAG, pp, 0);
f1a1da6c
A
138}
139
140int
214d78a2 141pthread_set_qos_class_np(pthread_t thread, qos_class_t qc, int relpri)
f1a1da6c 142{
214d78a2 143 if (thread != pthread_self()) {
f1a1da6c
A
144 /* The kext now enforces this anyway, if we check here too, it allows us to call
145 * _pthread_set_properties_self later if we can.
146 */
147 return EPERM;
148 }
214d78a2 149 return pthread_set_qos_class_self_np(qc, relpri);
f1a1da6c
A
150}
151
152int
214d78a2 153pthread_get_qos_class_np(pthread_t thread, qos_class_t *qc, int *relpri)
f1a1da6c 154{
214d78a2
A
155 pthread_priority_t pp = thread->tsd[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS];
156 _pthread_priority_split(pp, qc, relpri);
f1a1da6c
A
157 return 0;
158}
159
160qos_class_t
161qos_class_self(void)
162{
214d78a2
A
163 pthread_priority_t pp;
164 pp = _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS);
165 return _pthread_qos_class_from_thread_qos(_pthread_priority_thread_qos(pp));
f1a1da6c
A
166}
167
168qos_class_t
169qos_class_main(void)
170{
214d78a2
A
171 pthread_priority_t pp = _main_qos;
172 return _pthread_qos_class_from_thread_qos(_pthread_priority_thread_qos(pp));
f1a1da6c
A
173}
174
175pthread_priority_t
214d78a2 176_pthread_qos_class_encode(qos_class_t qc, int relpri, unsigned long flags)
f1a1da6c 177{
214d78a2
A
178 thread_qos_t qos = _pthread_qos_class_to_thread_qos(qc);
179 return _pthread_priority_make_from_thread_qos(qos, relpri, flags);
f1a1da6c
A
180}
181
182qos_class_t
214d78a2 183_pthread_qos_class_decode(pthread_priority_t pp, int *relpri, unsigned long *flags)
f1a1da6c 184{
214d78a2
A
185 qos_class_t qc;
186 _pthread_priority_split(pp, &qc, relpri);
187 if (flags) *flags = (pp & _PTHREAD_PRIORITY_FLAGS_MASK);
188 return qc;
f1a1da6c
A
189}
190
a0619f9c
A
191// Encode a legacy workqueue API priority into a pthread_priority_t. This API
192// is deprecated and can be removed when the simulator no longer uses it.
f1a1da6c
A
193pthread_priority_t
194_pthread_qos_class_encode_workqueue(int queue_priority, unsigned long flags)
195{
214d78a2 196 thread_qos_t qos;
f1a1da6c 197 switch (queue_priority) {
214d78a2
A
198 case WORKQ_HIGH_PRIOQUEUE: qos = THREAD_QOS_USER_INTERACTIVE; break;
199 case WORKQ_DEFAULT_PRIOQUEUE: qos = THREAD_QOS_LEGACY; break;
a0619f9c 200 case WORKQ_NON_INTERACTIVE_PRIOQUEUE:
214d78a2
A
201 case WORKQ_LOW_PRIOQUEUE: qos = THREAD_QOS_UTILITY; break;
202 case WORKQ_BG_PRIOQUEUE: qos = THREAD_QOS_BACKGROUND; break;
a0619f9c
A
203 default:
204 __pthread_abort();
f1a1da6c 205 }
214d78a2 206 return _pthread_priority_make_from_thread_qos(qos, 0, flags);
f1a1da6c
A
207}
208
214d78a2
A
209#define _PTHREAD_SET_SELF_OUTSIDE_QOS_SKIP \
210 (_PTHREAD_SET_SELF_QOS_FLAG | _PTHREAD_SET_SELF_FIXEDPRIORITY_FLAG | \
211 _PTHREAD_SET_SELF_TIMESHARE_FLAG)
212
f1a1da6c 213int
214d78a2
A
214_pthread_set_properties_self(_pthread_set_flags_t flags,
215 pthread_priority_t priority, mach_port_t voucher)
f1a1da6c 216{
214d78a2
A
217 pthread_t self = pthread_self();
218 _pthread_set_flags_t kflags = flags;
219 int rv = 0;
220
221 if (self->wqoutsideqos && (flags & _PTHREAD_SET_SELF_OUTSIDE_QOS_SKIP)) {
222 // A number of properties cannot be altered if we are a workloop
223 // thread that has outside of QoS properties applied to it.
224 kflags &= ~_PTHREAD_SET_SELF_OUTSIDE_QOS_SKIP;
225 if (kflags == 0) goto skip;
f1a1da6c
A
226 }
227
214d78a2 228 rv = __bsdthread_ctl(BSDTHREAD_CTL_SET_SELF, priority, voucher, kflags);
f1a1da6c 229
214d78a2
A
230skip:
231 // Set QoS TSD if we succeeded, or only failed the voucher portion of the
232 // call. Additionally, if we skipped setting QoS because of outside-of-QoS
233 // attributes then we still want to set the TSD in userspace.
f1a1da6c
A
234 if ((flags & _PTHREAD_SET_SELF_QOS_FLAG) != 0) {
235 if (rv == 0 || errno == ENOENT) {
214d78a2
A
236 _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS,
237 priority);
f1a1da6c
A
238 }
239 }
240
241 if (rv) {
242 rv = errno;
243 }
244 return rv;
245}
246
247int
248pthread_set_fixedpriority_self(void)
249{
214d78a2 250 return _pthread_set_properties_self(_PTHREAD_SET_SELF_FIXEDPRIORITY_FLAG, 0, 0);
f1a1da6c
A
251}
252
964d3577
A
253int
254pthread_set_timeshare_self(void)
255{
214d78a2 256 return _pthread_set_properties_self(_PTHREAD_SET_SELF_TIMESHARE_FLAG, 0, 0);
964d3577
A
257}
258
f1a1da6c 259pthread_override_t
214d78a2 260pthread_override_qos_class_start_np(pthread_t thread, qos_class_t qc, int relpri)
f1a1da6c
A
261{
262 pthread_override_t rv;
263 kern_return_t kr;
214d78a2 264 thread_qos_t qos;
f1a1da6c
A
265 int res = 0;
266
267 /* For now, we don't have access to malloc. So we'll have to vm_allocate this, which means the tiny struct is going
268 * to use an entire page.
269 */
270 bool did_malloc = true;
271
214d78a2
A
272 qos = _pthread_validate_qos_class_and_relpri(qc, relpri);
273 if (qos == THREAD_QOS_UNSPECIFIED) {
274 return (_Nonnull pthread_override_t)NULL;
275 }
276
f1a1da6c
A
277 mach_vm_address_t vm_addr = malloc(sizeof(struct pthread_override_s));
278 if (!vm_addr) {
279 vm_addr = vm_page_size;
280 did_malloc = false;
281
214d78a2
A
282 kr = mach_vm_allocate(mach_task_self(), &vm_addr,
283 round_page(sizeof(struct pthread_override_s)),
284 VM_MAKE_TAG(VM_MEMORY_LIBDISPATCH) | VM_FLAGS_ANYWHERE);
f1a1da6c
A
285 if (kr != KERN_SUCCESS) {
286 errno = ENOMEM;
214d78a2 287 return (_Nonnull pthread_override_t)NULL;
f1a1da6c
A
288 }
289 }
290
291 rv = (pthread_override_t)vm_addr;
292 rv->sig = PTHREAD_OVERRIDE_SIGNATURE;
214d78a2
A
293 rv->pthread = thread;
294 rv->kthread = pthread_mach_thread_np(thread);
295 rv->priority = _pthread_priority_make_from_thread_qos(qos, relpri, 0);
f1a1da6c
A
296 rv->malloced = did_malloc;
297
298 /* To ensure that the kernel port that we keep stays valid, we retain it here. */
299 kr = mach_port_mod_refs(mach_task_self(), rv->kthread, MACH_PORT_RIGHT_SEND, 1);
300 if (kr != KERN_SUCCESS) {
301 res = EINVAL;
302 }
303
304 if (res == 0) {
215aeb03 305 res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_START, rv->kthread, rv->priority, (uintptr_t)rv);
f1a1da6c
A
306
307 if (res != 0) {
308 mach_port_mod_refs(mach_task_self(), rv->kthread, MACH_PORT_RIGHT_SEND, -1);
309 }
310 }
311
312 if (res != 0) {
313 if (did_malloc) {
314 free(rv);
315 } else {
316 mach_vm_deallocate(mach_task_self(), vm_addr, round_page(sizeof(struct pthread_override_s)));
317 }
318 rv = NULL;
319 }
214d78a2 320 return (_Nonnull pthread_override_t)rv;
f1a1da6c
A
321}
322
323int
324pthread_override_qos_class_end_np(pthread_override_t override)
325{
326 kern_return_t kr;
327 int res = 0;
328
329 /* Double-free is a fault. Swap the signature and check the old one. */
a0619f9c 330 if (_pthread_atomic_xchg_uint32_relaxed(&override->sig, PTHREAD_OVERRIDE_SIG_DEAD) != PTHREAD_OVERRIDE_SIGNATURE) {
f1a1da6c
A
331 __builtin_trap();
332 }
333
f1a1da6c 334 /* Always consumes (and deallocates) the pthread_override_t object given. */
215aeb03 335 res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_END, override->kthread, (uintptr_t)override, 0);
f1a1da6c
A
336 if (res == -1) { res = errno; }
337
338 /* EFAULT from the syscall means we underflowed. Crash here. */
339 if (res == EFAULT) {
340 // <rdar://problem/17645082> Disable the trap-on-underflow, it doesn't co-exist
341 // with dispatch resetting override counts on threads.
342 //__builtin_trap();
343 res = 0;
344 }
345
346 kr = mach_port_mod_refs(mach_task_self(), override->kthread, MACH_PORT_RIGHT_SEND, -1);
347 if (kr != KERN_SUCCESS) {
348 res = EINVAL;
349 }
350
351 if (override->malloced) {
352 free(override);
353 } else {
354 kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)override, round_page(sizeof(struct pthread_override_s)));
355 if (kr != KERN_SUCCESS) {
356 res = EINVAL;
357 }
358 }
359
360 return res;
361}
362
363int
2546420a 364_pthread_qos_override_start_direct(mach_port_t thread, pthread_priority_t priority, void *resource)
f1a1da6c 365{
2546420a 366 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_START, thread, priority, (uintptr_t)resource);
f1a1da6c
A
367 if (res == -1) { res = errno; }
368 return res;
369}
370
371int
2546420a 372_pthread_qos_override_end_direct(mach_port_t thread, void *resource)
f1a1da6c 373{
2546420a 374 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_END, thread, (uintptr_t)resource, 0);
f1a1da6c
A
375 if (res == -1) { res = errno; }
376 return res;
377}
378
2546420a
A
379int
380_pthread_override_qos_class_start_direct(mach_port_t thread, pthread_priority_t priority)
381{
382 // use pthread_self as the default per-thread memory allocation to track the override in the kernel
383 return _pthread_qos_override_start_direct(thread, priority, pthread_self());
384}
385
386int
387_pthread_override_qos_class_end_direct(mach_port_t thread)
388{
389 // use pthread_self as the default per-thread memory allocation to track the override in the kernel
390 return _pthread_qos_override_end_direct(thread, pthread_self());
391}
392
f1a1da6c
A
393int
394_pthread_workqueue_override_start_direct(mach_port_t thread, pthread_priority_t priority)
395{
396 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH, thread, priority, 0);
397 if (res == -1) { res = errno; }
398 return res;
399}
400
2546420a
A
401int
402_pthread_workqueue_override_start_direct_check_owner(mach_port_t thread, pthread_priority_t priority, mach_port_t *ulock_addr)
403{
404#if !TARGET_OS_IPHONE
405 static boolean_t kernel_supports_owner_check = TRUE;
406 if (!kernel_supports_owner_check) {
407 ulock_addr = NULL;
408 }
409#endif
410
411 for (;;) {
412 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH, thread, priority, ulock_addr);
413 if (res == -1) { res = errno; }
414#if !TARGET_OS_IPHONE
415 if (ulock_addr && res == EINVAL) {
416 if ((uintptr_t)ulock_addr % _Alignof(_Atomic uint32_t)) {
417 // do not mute bad ulock addresses related errors
418 return EINVAL;
419 }
420 // backward compatibility for the XBS chroot
421 // BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH used to return EINVAL if
422 // arg3 was non NULL.
423 kernel_supports_owner_check = FALSE;
424 ulock_addr = NULL;
425 continue;
426 }
427#endif
428 if (ulock_addr && res == EFAULT) {
429 // kernel wants us to redrive the call, so while we refault the
430 // memory, also revalidate the owner
a0619f9c 431 uint32_t uval = *(uint32_t volatile *)ulock_addr;
2546420a
A
432 if (ulock_owner_value_to_port_name(uval) != thread) {
433 return ESTALE;
434 }
435 continue;
436 }
437
438 return res;
439 }
440}
441
f1a1da6c
A
442int
443_pthread_workqueue_override_reset(void)
444{
445 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_RESET, 0, 0, 0);
446 if (res == -1) { res = errno; }
447 return res;
448}
449
215aeb03
A
450int
451_pthread_workqueue_asynchronous_override_add(mach_port_t thread, pthread_priority_t priority, void *resource)
452{
453 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_ADD, thread, priority, (uintptr_t)resource);
454 if (res == -1) { res = errno; }
455 return res;
456}
457
458int
459_pthread_workqueue_asynchronous_override_reset_self(void *resource)
460{
461 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_RESET,
462 0 /* !reset_all */,
463 (uintptr_t)resource,
464 0);
465 if (res == -1) { res = errno; }
466 return res;
467}
468
469int
470_pthread_workqueue_asynchronous_override_reset_all_self(void)
471{
472 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_RESET,
473 1 /* reset_all */,
474 0,
475 0);
476 if (res == -1) { res = errno; }
477 return res;
478}
479
a0619f9c
A
480static inline uint16_t
481_pthread_workqueue_parallelism_for_priority(int qos, unsigned long flags)
482{
483 int rc = __bsdthread_ctl(BSDTHREAD_CTL_QOS_MAX_PARALLELISM, qos, flags, 0);
484 if (os_unlikely(rc == -1)) {
485 rc = errno;
486 if (rc != EINVAL) {
487 PTHREAD_INTERNAL_CRASH(rc, "qos_max_parallelism failed");
488 }
489 if (flags & _PTHREAD_QOS_PARALLELISM_COUNT_LOGICAL) {
490 return *(uint8_t *)_COMM_PAGE_LOGICAL_CPUS;
491 } else {
492 return *(uint8_t *)_COMM_PAGE_PHYSICAL_CPUS;
493 }
494 }
495 return (uint16_t)rc;
496}
497
498int
499pthread_qos_max_parallelism(qos_class_t qos, unsigned long flags)
500{
214d78a2
A
501 thread_qos_t thread_qos;
502 if (qos == QOS_CLASS_UNSPECIFIED) {
503 qos = QOS_CLASS_DEFAULT; // <rdar://problem/35080198>
504 }
505 thread_qos = _pthread_qos_class_to_thread_qos(qos);
a0619f9c
A
506 if (thread_qos == THREAD_QOS_UNSPECIFIED) {
507 errno = EINVAL;
508 return -1;
509 }
510
511 unsigned long syscall_flags = _PTHREAD_QOS_PARALLELISM_COUNT_LOGICAL;
512 uint16_t *ptr = &_pthread_globals()->qmp_logical[thread_qos];
513
514 if (flags & PTHREAD_MAX_PARALLELISM_PHYSICAL) {
515 syscall_flags = 0;
516 ptr = &_pthread_globals()->qmp_physical[thread_qos];
517 }
518 if (*ptr == 0) {
519 *ptr = _pthread_workqueue_parallelism_for_priority(thread_qos, syscall_flags);
520 }
521 return *ptr;
522}
523
524int
525pthread_time_constraint_max_parallelism(unsigned long flags)
526{
527 unsigned long syscall_flags = _PTHREAD_QOS_PARALLELISM_COUNT_LOGICAL;
528 uint16_t *ptr = &_pthread_globals()->qmp_logical[0];
529
530 if (flags & PTHREAD_MAX_PARALLELISM_PHYSICAL) {
531 syscall_flags = 0;
532 ptr = &_pthread_globals()->qmp_physical[0];
533 }
534 if (*ptr == 0) {
535 *ptr = _pthread_workqueue_parallelism_for_priority(0,
536 syscall_flags | _PTHREAD_QOS_PARALLELISM_REALTIME);
537 }
538 return *ptr;
539}
540
f1a1da6c
A
541int
542posix_spawnattr_set_qos_class_np(posix_spawnattr_t * __restrict __attr, qos_class_t __qos_class)
543{
544 switch (__qos_class) {
545 case QOS_CLASS_UTILITY:
546 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_UTILITY);
547 case QOS_CLASS_BACKGROUND:
548 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_BACKGROUND);
549 case QOS_CLASS_MAINTENANCE:
550 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_MAINTENANCE);
551 default:
552 return EINVAL;
553 }
554}
555
556int
557posix_spawnattr_get_qos_class_np(const posix_spawnattr_t *__restrict __attr, qos_class_t * __restrict __qos_class)
558{
559 uint64_t clamp;
560
561 if (!__qos_class) {
562 return EINVAL;
563 }
564
565 int rv = posix_spawnattr_get_qos_clamp_np(__attr, &clamp);
566 if (rv != 0) {
567 return rv;
568 }
569
570 switch (clamp) {
571 case POSIX_SPAWN_PROC_CLAMP_UTILITY:
572 *__qos_class = QOS_CLASS_UTILITY;
573 break;
574 case POSIX_SPAWN_PROC_CLAMP_BACKGROUND:
575 *__qos_class = QOS_CLASS_BACKGROUND;
576 break;
577 case POSIX_SPAWN_PROC_CLAMP_MAINTENANCE:
578 *__qos_class = QOS_CLASS_MAINTENANCE;
579 break;
580 default:
581 *__qos_class = QOS_CLASS_UNSPECIFIED;
582 break;
583 }
584
585 return 0;
586}