2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
27 #include <mach/mach_vm.h>
30 #include <spawn_private.h>
31 #include <sys/spawn_internal.h>
33 // TODO: remove me when internal.h can include *_private.h itself
34 #include "workqueue_private.h"
35 #include "qos_private.h"
37 static pthread_priority_t _main_qos
= QOS_CLASS_UNSPECIFIED
;
39 #define PTHREAD_OVERRIDE_SIGNATURE (0x6f766572)
40 #define PTHREAD_OVERRIDE_SIG_DEAD (0x7265766f)
42 struct pthread_override_s
47 pthread_priority_t priority
;
52 _pthread_set_main_qos(pthread_priority_t qos
)
58 pthread_attr_set_qos_class_np(pthread_attr_t
*__attr
,
59 qos_class_t __qos_class
,
60 int __relative_priority
)
62 if (!(__pthread_supported_features
& PTHREAD_FEATURE_BSDTHREADCTL
)) {
66 if (__relative_priority
> 0 || __relative_priority
< QOS_MIN_RELATIVE_PRIORITY
) {
71 if (__attr
->sig
== _PTHREAD_ATTR_SIG
) {
72 if (!__attr
->schedset
) {
73 __attr
->qosclass
= _pthread_priority_make_newest(__qos_class
, __relative_priority
, 0);
83 pthread_attr_get_qos_class_np(pthread_attr_t
* __restrict __attr
,
84 qos_class_t
* __restrict __qos_class
,
85 int * __restrict __relative_priority
)
87 if (!(__pthread_supported_features
& PTHREAD_FEATURE_BSDTHREADCTL
)) {
92 if (__attr
->sig
== _PTHREAD_ATTR_SIG
) {
94 qos_class_t qos
; int relpri
;
95 _pthread_priority_split_newest(__attr
->qosclass
, qos
, relpri
);
97 if (__qos_class
) { *__qos_class
= qos
; }
98 if (__relative_priority
) { *__relative_priority
= relpri
; }
100 if (__qos_class
) { *__qos_class
= 0; }
101 if (__relative_priority
) { *__relative_priority
= 0; }
110 pthread_set_qos_class_self_np(qos_class_t __qos_class
,
111 int __relative_priority
)
113 if (!(__pthread_supported_features
& PTHREAD_FEATURE_BSDTHREADCTL
)) {
117 if (__relative_priority
> 0 || __relative_priority
< QOS_MIN_RELATIVE_PRIORITY
) {
121 pthread_priority_t priority
= _pthread_priority_make_newest(__qos_class
, __relative_priority
, 0);
123 if (__pthread_supported_features
& PTHREAD_FEATURE_SETSELF
) {
124 return _pthread_set_properties_self(_PTHREAD_SET_SELF_QOS_FLAG
, priority
, 0);
126 /* We set the thread QoS class in the TSD and then call into the kernel to
127 * read the value out of it and set the QoS class.
129 _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
, priority
);
131 mach_port_t kport
= pthread_mach_thread_np(pthread_self());
132 int res
= __bsdthread_ctl(BSDTHREAD_CTL_SET_QOS
, kport
, &pthread_self()->tsd
[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
], 0);
143 pthread_set_qos_class_np(pthread_t __pthread
,
144 qos_class_t __qos_class
,
145 int __relative_priority
)
147 if (!(__pthread_supported_features
& PTHREAD_FEATURE_BSDTHREADCTL
)) {
151 if (__relative_priority
> 0 || __relative_priority
< QOS_MIN_RELATIVE_PRIORITY
) {
155 if (__pthread
!= pthread_self()) {
156 /* The kext now enforces this anyway, if we check here too, it allows us to call
157 * _pthread_set_properties_self later if we can.
162 pthread_priority_t priority
= _pthread_priority_make_newest(__qos_class
, __relative_priority
, 0);
164 if (__pthread_supported_features
& PTHREAD_FEATURE_SETSELF
) {
165 /* If we have _pthread_set_properties_self, then we can easily set this using that. */
166 return _pthread_set_properties_self(_PTHREAD_SET_SELF_QOS_FLAG
, priority
, 0);
168 /* We set the thread QoS class in the TSD and then call into the kernel to
169 * read the value out of it and set the QoS class.
171 if (__pthread
== pthread_self()) {
172 _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
, priority
);
174 __pthread
->tsd
[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
] = priority
;
177 mach_port_t kport
= pthread_mach_thread_np(__pthread
);
178 int res
= __bsdthread_ctl(BSDTHREAD_CTL_SET_QOS
, kport
, &__pthread
->tsd
[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
], 0);
189 pthread_get_qos_class_np(pthread_t __pthread
,
190 qos_class_t
* __restrict __qos_class
,
191 int * __restrict __relative_priority
)
193 if (!(__pthread_supported_features
& PTHREAD_FEATURE_BSDTHREADCTL
)) {
197 pthread_priority_t priority
;
199 if (__pthread
== pthread_self()) {
200 priority
= _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
);
202 priority
= __pthread
->tsd
[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
];
205 qos_class_t qos
; int relpri
;
206 _pthread_priority_split_newest(priority
, qos
, relpri
);
208 if (__qos_class
) { *__qos_class
= qos
; }
209 if (__relative_priority
) { *__relative_priority
= relpri
; }
217 if (!(__pthread_supported_features
& PTHREAD_FEATURE_BSDTHREADCTL
)) {
218 return QOS_CLASS_UNSPECIFIED
;
221 pthread_priority_t p
= _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
);
222 qos_class_t c
= _pthread_priority_get_qos_newest(p
);
230 return _pthread_priority_get_qos_newest(_main_qos
);
234 _pthread_qos_class_encode(qos_class_t qos_class
, int relative_priority
, unsigned long flags
)
236 if ((__pthread_supported_features
& PTHREAD_FEATURE_QOS_MAINTENANCE
) == 0) {
237 return _pthread_priority_make_version2(qos_class
, relative_priority
, flags
);
239 return _pthread_priority_make_newest(qos_class
, relative_priority
, flags
);
244 _pthread_qos_class_decode(pthread_priority_t priority
, int *relative_priority
, unsigned long *flags
)
246 qos_class_t qos
; int relpri
;
248 if ((__pthread_supported_features
& PTHREAD_FEATURE_QOS_MAINTENANCE
) == 0) {
249 _pthread_priority_split_version2(priority
, qos
, relpri
);
251 _pthread_priority_split_newest(priority
, qos
, relpri
);
254 if (relative_priority
) { *relative_priority
= relpri
; }
255 if (flags
) { *flags
= _pthread_priority_get_flags(priority
); }
260 _pthread_qos_class_encode_workqueue(int queue_priority
, unsigned long flags
)
262 if ((__pthread_supported_features
& PTHREAD_FEATURE_QOS_DEFAULT
) == 0) {
263 switch (queue_priority
) {
264 case WORKQ_HIGH_PRIOQUEUE
:
265 return _pthread_priority_make_version1(QOS_CLASS_USER_INTERACTIVE
, 0, flags
);
266 case WORKQ_DEFAULT_PRIOQUEUE
:
267 return _pthread_priority_make_version1(QOS_CLASS_USER_INITIATED
, 0, flags
);
268 case WORKQ_LOW_PRIOQUEUE
:
269 case WORKQ_NON_INTERACTIVE_PRIOQUEUE
:
270 return _pthread_priority_make_version1(QOS_CLASS_UTILITY
, 0, flags
);
271 case WORKQ_BG_PRIOQUEUE
:
272 return _pthread_priority_make_version1(QOS_CLASS_BACKGROUND
, 0, flags
);
278 if ((__pthread_supported_features
& PTHREAD_FEATURE_QOS_MAINTENANCE
) == 0) {
279 switch (queue_priority
) {
280 case WORKQ_HIGH_PRIOQUEUE
:
281 return _pthread_priority_make_version2(QOS_CLASS_USER_INITIATED
, 0, flags
);
282 case WORKQ_DEFAULT_PRIOQUEUE
:
283 return _pthread_priority_make_version2(QOS_CLASS_DEFAULT
, 0, flags
);
284 case WORKQ_LOW_PRIOQUEUE
:
285 case WORKQ_NON_INTERACTIVE_PRIOQUEUE
:
286 return _pthread_priority_make_version2(QOS_CLASS_UTILITY
, 0, flags
);
287 case WORKQ_BG_PRIOQUEUE
:
288 return _pthread_priority_make_version2(QOS_CLASS_BACKGROUND
, 0, flags
);
289 /* Legacy dispatch does not use QOS_CLASS_MAINTENANCE, so no need to handle it here */
295 switch (queue_priority
) {
296 case WORKQ_HIGH_PRIOQUEUE
:
297 return _pthread_priority_make_newest(QOS_CLASS_USER_INITIATED
, 0, flags
);
298 case WORKQ_DEFAULT_PRIOQUEUE
:
299 return _pthread_priority_make_newest(QOS_CLASS_DEFAULT
, 0, flags
);
300 case WORKQ_LOW_PRIOQUEUE
:
301 case WORKQ_NON_INTERACTIVE_PRIOQUEUE
:
302 return _pthread_priority_make_newest(QOS_CLASS_UTILITY
, 0, flags
);
303 case WORKQ_BG_PRIOQUEUE
:
304 return _pthread_priority_make_newest(QOS_CLASS_BACKGROUND
, 0, flags
);
305 /* Legacy dispatch does not use QOS_CLASS_MAINTENANCE, so no need to handle it here */
312 _pthread_set_properties_self(_pthread_set_flags_t flags
, pthread_priority_t priority
, mach_port_t voucher
)
314 if (!(__pthread_supported_features
& PTHREAD_FEATURE_SETSELF
)) {
318 int rv
= __bsdthread_ctl(BSDTHREAD_CTL_SET_SELF
, priority
, voucher
, flags
);
320 /* Set QoS TSD if we succeeded or only failed the voucher half. */
321 if ((flags
& _PTHREAD_SET_SELF_QOS_FLAG
) != 0) {
322 if (rv
== 0 || errno
== ENOENT
) {
323 _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS
, priority
);
334 pthread_set_fixedpriority_self(void)
336 if (!(__pthread_supported_features
& PTHREAD_FEATURE_BSDTHREADCTL
)) {
340 if (__pthread_supported_features
& PTHREAD_FEATURE_SETSELF
) {
341 return _pthread_set_properties_self(_PTHREAD_SET_SELF_FIXEDPRIORITY_FLAG
, 0, 0);
349 pthread_override_qos_class_start_np(pthread_t __pthread
, qos_class_t __qos_class
, int __relative_priority
)
351 pthread_override_t rv
;
355 /* For now, we don't have access to malloc. So we'll have to vm_allocate this, which means the tiny struct is going
356 * to use an entire page.
358 bool did_malloc
= true;
360 mach_vm_address_t vm_addr
= malloc(sizeof(struct pthread_override_s
));
362 vm_addr
= vm_page_size
;
365 kr
= mach_vm_allocate(mach_task_self(), &vm_addr
, round_page(sizeof(struct pthread_override_s
)), VM_MAKE_TAG(VM_MEMORY_LIBDISPATCH
) | VM_FLAGS_ANYWHERE
);
366 if (kr
!= KERN_SUCCESS
) {
372 rv
= (pthread_override_t
)vm_addr
;
373 rv
->sig
= PTHREAD_OVERRIDE_SIGNATURE
;
374 rv
->pthread
= __pthread
;
375 rv
->kthread
= pthread_mach_thread_np(__pthread
);
376 rv
->priority
= _pthread_priority_make_newest(__qos_class
, __relative_priority
, 0);
377 rv
->malloced
= did_malloc
;
379 /* To ensure that the kernel port that we keep stays valid, we retain it here. */
380 kr
= mach_port_mod_refs(mach_task_self(), rv
->kthread
, MACH_PORT_RIGHT_SEND
, 1);
381 if (kr
!= KERN_SUCCESS
) {
386 res
= __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_START
, rv
->kthread
, rv
->priority
, 0);
389 mach_port_mod_refs(mach_task_self(), rv
->kthread
, MACH_PORT_RIGHT_SEND
, -1);
397 mach_vm_deallocate(mach_task_self(), vm_addr
, round_page(sizeof(struct pthread_override_s
)));
405 pthread_override_qos_class_end_np(pthread_override_t override
)
410 /* Double-free is a fault. Swap the signature and check the old one. */
411 if (__sync_swap(&override
->sig
, PTHREAD_OVERRIDE_SIG_DEAD
) != PTHREAD_OVERRIDE_SIGNATURE
) {
415 override
->sig
= PTHREAD_OVERRIDE_SIG_DEAD
;
417 /* Always consumes (and deallocates) the pthread_override_t object given. */
418 res
= __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_END
, override
->kthread
, 0, 0);
419 if (res
== -1) { res
= errno
; }
421 /* EFAULT from the syscall means we underflowed. Crash here. */
423 // <rdar://problem/17645082> Disable the trap-on-underflow, it doesn't co-exist
424 // with dispatch resetting override counts on threads.
429 kr
= mach_port_mod_refs(mach_task_self(), override
->kthread
, MACH_PORT_RIGHT_SEND
, -1);
430 if (kr
!= KERN_SUCCESS
) {
434 if (override
->malloced
) {
437 kr
= mach_vm_deallocate(mach_task_self(), (mach_vm_address_t
)override
, round_page(sizeof(struct pthread_override_s
)));
438 if (kr
!= KERN_SUCCESS
) {
447 _pthread_override_qos_class_start_direct(mach_port_t thread
, pthread_priority_t priority
)
449 int res
= __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_START
, thread
, priority
, 0);
450 if (res
== -1) { res
= errno
; }
455 _pthread_override_qos_class_end_direct(mach_port_t thread
)
457 int res
= __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_END
, thread
, 0, 0);
458 if (res
== -1) { res
= errno
; }
463 _pthread_workqueue_override_start_direct(mach_port_t thread
, pthread_priority_t priority
)
465 int res
= __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH
, thread
, priority
, 0);
466 if (res
== -1) { res
= errno
; }
471 _pthread_workqueue_override_reset(void)
473 int res
= __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_RESET
, 0, 0, 0);
474 if (res
== -1) { res
= errno
; }
479 posix_spawnattr_set_qos_class_np(posix_spawnattr_t
* __restrict __attr
, qos_class_t __qos_class
)
481 switch (__qos_class
) {
482 case QOS_CLASS_UTILITY
:
483 return posix_spawnattr_set_qos_clamp_np(__attr
, POSIX_SPAWN_PROC_CLAMP_UTILITY
);
484 case QOS_CLASS_BACKGROUND
:
485 return posix_spawnattr_set_qos_clamp_np(__attr
, POSIX_SPAWN_PROC_CLAMP_BACKGROUND
);
486 case QOS_CLASS_MAINTENANCE
:
487 return posix_spawnattr_set_qos_clamp_np(__attr
, POSIX_SPAWN_PROC_CLAMP_MAINTENANCE
);
494 posix_spawnattr_get_qos_class_np(const posix_spawnattr_t
*__restrict __attr
, qos_class_t
* __restrict __qos_class
)
502 int rv
= posix_spawnattr_get_qos_clamp_np(__attr
, &clamp
);
508 case POSIX_SPAWN_PROC_CLAMP_UTILITY
:
509 *__qos_class
= QOS_CLASS_UTILITY
;
511 case POSIX_SPAWN_PROC_CLAMP_BACKGROUND
:
512 *__qos_class
= QOS_CLASS_BACKGROUND
;
514 case POSIX_SPAWN_PROC_CLAMP_MAINTENANCE
:
515 *__qos_class
= QOS_CLASS_MAINTENANCE
;
518 *__qos_class
= QOS_CLASS_UNSPECIFIED
;