2 * turnstile_multihop: Tests turnstile and multi hop priority propagation.
9 #include <darwintest.h>
10 #include <darwintest_multiprocess.h>
12 #include <dispatch/dispatch.h>
15 #include <mach/mach.h>
16 #include <mach/message.h>
17 #include <mach/mach_voucher.h>
18 #include <pthread/workqueue_private.h>
19 #include <voucher/ipc_pthread_priority_types.h>
20 #include <servers/bootstrap.h>
22 #include <sys/event.h>
24 #include <crt_externs.h>
26 #include <sys/types.h>
27 #include <sys/sysctl.h>
28 #include <libkern/OSAtomic.h>
31 #include "turnstile_multihop_helper.h"
33 T_GLOBAL_META(T_META_NAMESPACE("xnu.turnstile_multihop"));
35 #define HELPER_TIMEOUT_SECS (3000)
37 static boolean_t spin_for_ever
= false;
40 thread_create_at_qos(qos_class_t qos
, void * (*function
)(void *));
42 nanoseconds_to_absolutetime(uint64_t nanoseconds
);
44 sched_create_load_at_qos(qos_class_t qos
, void **load_token
);
46 sched_terminate_load(void *load_token
) __unused
;
47 static void do_work(int num
);
49 dispatch_sync_cancel(mach_port_t owner_thread
, qos_class_t promote_qos
);
51 static void *sched_load_thread(void *);
53 struct load_token_context
{
54 volatile int threads_should_exit
;
60 static struct mach_timebase_info sched_mti
;
61 static pthread_once_t sched_mti_once_control
= PTHREAD_ONCE_INIT
;
63 static void sched_mti_init(void)
65 mach_timebase_info(&sched_mti
);
68 nanoseconds_to_absolutetime(uint64_t nanoseconds
)
70 pthread_once(&sched_mti_once_control
, sched_mti_init
);
72 return (uint64_t)(nanoseconds
* (((double)sched_mti
.denom
) / ((double)sched_mti
.numer
)));
76 sched_create_load_at_qos(qos_class_t qos
, void **load_token
)
78 struct load_token_context
*context
= NULL
;
81 size_t ncpu_size
= sizeof(ncpu
);
86 ret
= sysctlbyname("hw.ncpu", &ncpu
, &ncpu_size
, NULL
, 0);
88 T_LOG("sysctlbyname(hw.ncpu)");
92 T_QUIET
; T_LOG("%s: Detected %d CPUs\n", __FUNCTION__
, ncpu
);
95 T_QUIET
; T_LOG("%s: Will create %d threads\n", __FUNCTION__
, nthreads
);
97 ret
= pthread_attr_init(&attr
);
98 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_attr_init");
100 if (&pthread_attr_set_qos_class_np
) {
101 ret
= pthread_attr_set_qos_class_np(&attr
, qos
, 0);
102 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_attr_set_qos_class_np");
105 context
= calloc(1, sizeof(*context
));
106 if (context
== NULL
) { T_QUIET
; T_LOG("calloc returned error"); return ENOMEM
; }
108 context
->threads_should_exit
= 0;
109 context
->thread_count
= nthreads
;
111 context
->threads
= calloc((unsigned int)nthreads
, sizeof(pthread_t
));
115 for (i
=0; i
< nthreads
; i
++) {
116 ret
= pthread_create(&context
->threads
[i
], &attr
, sched_load_thread
, context
);
117 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_create");
118 T_QUIET
; T_LOG("%s: Created thread %d (%p)\n", __FUNCTION__
, i
, (void *)context
->threads
[i
]);
121 ret
= pthread_attr_destroy(&attr
);
122 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_attr_destroy");
124 *load_token
= context
;
130 sched_load_thread(void *arg
)
132 struct load_token_context
*context
= (struct load_token_context
*)arg
;
134 T_QUIET
; T_LOG("%s: Thread started %p\n", __FUNCTION__
, (void *)pthread_self());
136 while (!context
->threads_should_exit
) {
137 uint64_t start
= mach_absolute_time();
138 uint64_t end
= start
+ nanoseconds_to_absolutetime(900ULL * NSEC_PER_MSEC
);
140 while ((mach_absolute_time() < end
) && !context
->threads_should_exit
);
143 T_QUIET
; T_LOG("%s: Thread terminating %p\n", __FUNCTION__
, (void *)pthread_self());
149 sched_terminate_load(void *load_token
)
153 struct load_token_context
*context
= (struct load_token_context
*)load_token
;
155 context
->threads_should_exit
= 1;
158 for (i
=0; i
< context
->thread_count
; i
++) {
159 T_QUIET
; T_LOG("%s: Joining thread %d (%p)\n", __FUNCTION__
, i
, (void *)context
->threads
[i
]);
160 ret
= pthread_join(context
->threads
[i
], NULL
);
161 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_join");
164 free(context
->threads
);
171 // Find the first num primes, simply as a means of doing work
172 static void do_work(int num
)
174 volatile int i
= 3, count
, c
;
176 for(count
= 2; count
<= num
; ) {
177 for(c
= 2; c
<= i
; c
++) {
189 #pragma mark pthread callbacks
192 worker_cb(pthread_priority_t __unused priority
)
194 T_FAIL("a worker thread was created");
198 event_cb(void ** __unused events
, int * __unused nevents
)
200 T_FAIL("a kevent routine was called instead of workloop");
204 get_user_promotion_basepri(void)
206 mach_msg_type_number_t count
= THREAD_POLICY_STATE_COUNT
;
207 struct thread_policy_state thread_policy
;
208 boolean_t get_default
= FALSE
;
209 mach_port_t thread_port
= pthread_mach_thread_np(pthread_self());
211 kern_return_t kr
= thread_policy_get(thread_port
, THREAD_POLICY_STATE
,
212 (thread_policy_t
)&thread_policy
, &count
, &get_default
);
213 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "thread_policy_get");
214 return thread_policy
.thps_user_promotion_basepri
;
217 static int messages_received
= 0;
219 * Basic WL handler callback, it checks the
220 * effective Qos of the servicer thread.
223 workloop_cb_test_intransit(uint64_t *workloop_id __unused
, void **eventslist __unused
, int *events
)
226 T_LOG("Workloop handler workloop_cb_test_intransit called. Received message no %d",
230 /* Skip the test if we can't check Qos */
231 if (geteuid() != 0) {
232 T_SKIP("kevent_qos test requires root privileges to run.");
235 if (messages_received
== 1) {
238 T_LOG("Do some CPU work.");
241 /* Check if the override now is IN + 60 boost */
242 T_EXPECT_EFFECTIVE_QOS_EQ(QOS_CLASS_USER_INITIATED
,
243 "dispatch_source event handler QoS should be QOS_CLASS_USER_INITIATED");
244 T_EXPECT_EQ(get_user_promotion_basepri(), 60u,
245 "dispatch_source event handler should be overridden at 60");
247 /* Enable the knote to get 2nd message */
248 struct kevent_qos_s
*kev
= *eventslist
;
249 kev
->flags
= EV_ADD
| EV_ENABLE
| EV_UDATA_SPECIFIC
| EV_DISPATCH
| EV_VANISHED
;
250 kev
->fflags
= (MACH_RCV_MSG
| MACH_RCV_LARGE
| MACH_RCV_LARGE_IDENTITY
|
251 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX
) |
252 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
) |
262 run_client_server(const char *server_name
, const char *client_name
)
264 dt_helper_t helpers
[] = {
265 dt_launchd_helper_domain("com.apple.xnu.test.turnstile_multihop.plist",
266 server_name
, NULL
, LAUNCH_SYSTEM_DOMAIN
),
267 dt_fork_helper(client_name
)
269 dt_run_helpers(helpers
, 2, HELPER_TIMEOUT_SECS
);
272 #pragma mark Mach receive
274 #define TURNSTILE_MULTIHOP_SERVICE_NAME "com.apple.xnu.test.turnstile_multihop"
277 get_server_port(void)
280 kern_return_t kr
= bootstrap_check_in(bootstrap_port
,
281 TURNSTILE_MULTIHOP_SERVICE_NAME
, &port
);
282 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "server bootstrap_check_in");
286 static mach_voucher_t
287 create_pthpriority_voucher(mach_msg_priority_t qos
)
289 char voucher_buf
[sizeof(mach_voucher_attr_recipe_data_t
) + sizeof(ipc_pthread_priority_value_t
)];
291 mach_voucher_t voucher
= MACH_PORT_NULL
;
293 ipc_pthread_priority_value_t ipc_pthread_priority_value
=
294 (ipc_pthread_priority_value_t
)qos
;
296 mach_voucher_attr_raw_recipe_array_t recipes
;
297 mach_voucher_attr_raw_recipe_size_t recipe_size
= 0;
298 mach_voucher_attr_recipe_t recipe
=
299 (mach_voucher_attr_recipe_t
)&voucher_buf
[recipe_size
];
301 recipe
->key
= MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
;
302 recipe
->command
= MACH_VOUCHER_ATTR_PTHPRIORITY_CREATE
;
303 recipe
->previous_voucher
= MACH_VOUCHER_NULL
;
304 memcpy((char *)&recipe
->content
[0], &ipc_pthread_priority_value
, sizeof(ipc_pthread_priority_value
));
305 recipe
->content_size
= sizeof(ipc_pthread_priority_value_t
);
306 recipe_size
+= sizeof(mach_voucher_attr_recipe_data_t
) + recipe
->content_size
;
308 recipes
= (mach_voucher_attr_raw_recipe_array_t
)&voucher_buf
[0];
310 ret
= host_create_mach_voucher(mach_host_self(),
315 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "client host_create_mach_voucher");
321 mach_port_t send_port
,
322 mach_port_t reply_port
,
323 mach_port_t msg_port
,
324 mach_msg_priority_t qos
,
325 mach_msg_option_t options
)
327 kern_return_t ret
= 0;
330 mach_msg_header_t header
;
331 mach_msg_body_t body
;
332 mach_msg_port_descriptor_t port_descriptor
;
335 .msgh_remote_port
= send_port
,
336 .msgh_local_port
= reply_port
,
337 .msgh_bits
= MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND
,
338 reply_port
? MACH_MSG_TYPE_MAKE_SEND_ONCE
: 0,
339 MACH_MSG_TYPE_MOVE_SEND
,
340 MACH_MSGH_BITS_COMPLEX
),
342 .msgh_size
= sizeof(send_msg
),
345 .msgh_descriptor_count
= 1,
349 .disposition
= MACH_MSG_TYPE_MOVE_RECEIVE
,
350 .type
= MACH_MSG_PORT_DESCRIPTOR
,
354 if (options
& MACH_SEND_SYNC_USE_THRPRI
) {
355 send_msg
.header
.msgh_voucher_port
= create_pthpriority_voucher(qos
);
358 if (msg_port
== MACH_PORT_NULL
) {
359 send_msg
.body
.msgh_descriptor_count
= 0;
362 ret
= mach_msg(&(send_msg
.header
),
366 ((reply_port
? MACH_SEND_SYNC_OVERRIDE
: 0) | options
),
367 send_msg
.header
.msgh_size
,
373 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "client mach_msg");
378 mach_port_t rcv_port
,
379 mach_port_t notify_port
)
381 kern_return_t ret
= 0;
384 mach_msg_header_t header
;
385 mach_msg_body_t body
;
386 mach_msg_port_descriptor_t port_descriptor
;
390 .msgh_remote_port
= MACH_PORT_NULL
,
391 .msgh_local_port
= rcv_port
,
392 .msgh_size
= sizeof(rcv_msg
),
396 T_LOG("Client: Starting sync receive\n");
398 ret
= mach_msg(&(rcv_msg
.header
),
402 rcv_msg
.header
.msgh_size
,
408 static lock_t lock_DEF
;
409 static lock_t lock_IN
;
410 static lock_t lock_UI
;
412 static mach_port_t main_thread_port
;
413 static mach_port_t def_thread_port
;
414 static mach_port_t in_thread_port
;
415 static mach_port_t ui_thread_port
;
416 static mach_port_t sixty_thread_port
;
418 static uint64_t dispatch_sync_owner
;
420 static int get_pri(thread_t thread_port
) {
423 thread_extended_info_data_t extended_info
;
424 mach_msg_type_number_t count
= THREAD_EXTENDED_INFO_COUNT
;
425 kr
= thread_info(thread_port
, THREAD_EXTENDED_INFO
,
426 (thread_info_t
)&extended_info
, &count
);
428 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "thread_info");
430 return extended_info
.pth_curpri
;
434 set_thread_name(const char *fn_name
)
438 thread_t thread_port
= pthread_mach_thread_np(pthread_self());
440 int pri
= get_pri(thread_port
);
442 snprintf(name
, sizeof(name
), "%s at pri %2d", fn_name
, pri
);
443 pthread_setname_np(name
);
447 thread_wait_to_block(mach_port_t thread_port
)
449 thread_extended_info_data_t extended_info
;
453 mach_msg_type_number_t count
= THREAD_EXTENDED_INFO_COUNT
;
454 kr
= thread_info(thread_port
, THREAD_EXTENDED_INFO
,
455 (thread_info_t
)&extended_info
, &count
);
457 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "thread_info");
459 if (extended_info
.pth_run_state
== TH_STATE_WAITING
) {
460 T_LOG("Target thread blocked\n");
463 thread_switch(thread_port
, SWITCH_OPTION_DEPRESS
, 0);
468 thread_wait_to_boost(mach_port_t thread_port
, mach_port_t yield_thread
, int priority
)
470 thread_extended_info_data_t extended_info
;
474 mach_msg_type_number_t count
= THREAD_EXTENDED_INFO_COUNT
;
475 kr
= thread_info(thread_port
, THREAD_EXTENDED_INFO
,
476 (thread_info_t
)&extended_info
, &count
);
478 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "thread_info");
480 if (extended_info
.pth_priority
>= priority
) {
481 T_LOG("Target thread boosted\n");
484 thread_switch(yield_thread
, SWITCH_OPTION_DEPRESS
, 0);
489 dispatch_sync_wait(mach_port_t owner_thread
, qos_class_t promote_qos
)
491 struct kevent_qos_s kev_err
[] = {{ 0 }};
497 action
= EV_ADD
| EV_DISABLE
;
498 fflags
= NOTE_WL_SYNC_WAIT
| NOTE_WL_DISCOVER_OWNER
;
500 dispatch_sync_owner
= owner_thread
;
502 struct kevent_qos_s kev
[] = {{
503 .ident
= mach_thread_self(),
504 .filter
= EVFILT_WORKLOOP
,
507 .udata
= (uintptr_t) &def_thread_port
,
508 .qos
= (int32_t)_pthread_qos_class_encode(promote_qos
, 0, 0),
509 .ext
[EV_EXTIDX_WL_MASK
] = mask
,
510 .ext
[EV_EXTIDX_WL_VALUE
] = dispatch_sync_owner
,
511 .ext
[EV_EXTIDX_WL_ADDR
] = (uint64_t)&dispatch_sync_owner
,
514 /* Setup workloop to fake dispatch sync wait on a workloop */
515 r
= kevent_id(30, kev
, 1, kev_err
, 1, NULL
,
516 NULL
, KEVENT_FLAG_WORKLOOP
| KEVENT_FLAG_ERROR_EVENTS
);
517 T_QUIET
; T_LOG("dispatch_sync_wait returned\n");
521 dispatch_sync_cancel(mach_port_t owner_thread
, qos_class_t promote_qos
)
523 struct kevent_qos_s kev_err
[] = {{ 0 }};
529 action
= EV_DELETE
| EV_ENABLE
;
530 fflags
= NOTE_WL_SYNC_WAKE
| NOTE_WL_END_OWNERSHIP
;
532 dispatch_sync_owner
= owner_thread
;
534 struct kevent_qos_s kev
[] = {{
535 .ident
= def_thread_port
,
536 .filter
= EVFILT_WORKLOOP
,
539 .udata
= (uintptr_t) &def_thread_port
,
540 .qos
= (int32_t)_pthread_qos_class_encode(promote_qos
, 0, 0),
541 .ext
[EV_EXTIDX_WL_MASK
] = mask
,
542 .ext
[EV_EXTIDX_WL_VALUE
] = dispatch_sync_owner
,
543 .ext
[EV_EXTIDX_WL_ADDR
] = (uint64_t)&dispatch_sync_owner
,
546 /* Setup workloop to fake dispatch sync wake on a workloop */
547 r
= kevent_id(30, kev
, 1, kev_err
, 1, NULL
,
548 NULL
, KEVENT_FLAG_WORKLOOP
| KEVENT_FLAG_ERROR_EVENTS
);
549 T_QUIET
; T_LOG("dispatch_sync_cancel returned\n");
554 thread_at_sixty(void *arg __unused
)
557 struct sched_param param
;
560 uint64_t before_lock_time
, after_lock_time
;
562 sixty_thread_port
= mach_thread_self();
564 set_thread_name(__FUNCTION__
);
566 /* Change our priority to 60 */
567 ret
= pthread_getschedparam(pthread_self(), &policy
, ¶m
);
568 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_getschedparam");
570 param
.sched_priority
= 60;
572 ret
= pthread_setschedparam(pthread_self(), policy
, ¶m
);
573 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_setschedparam");
575 ret
= pthread_getschedparam(pthread_self(), &policy
, ¶m
);
576 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "pthread_getschedparam");
578 T_LOG("My priority is %d", param
.sched_priority
);
580 thread_wait_to_boost(in_thread_port
, ui_thread_port
, 46);
583 /* Schedule load at Default */
584 sched_create_load_at_qos(QOS_CLASS_DEFAULT
, &load_token
);
587 T_LOG("Thread at priority 60 trying to acquire UI lock");
589 before_lock_time
= mach_absolute_time();
590 ull_lock(&lock_UI
, 3, UL_UNFAIR_LOCK
, 0);
591 after_lock_time
= mach_absolute_time();
593 T_QUIET
; T_LOG("The time for priority 60 thread to acquire lock was %llu \n",
594 (after_lock_time
- before_lock_time
));
599 thread_at_ui(void *arg __unused
)
601 ui_thread_port
= mach_thread_self();
603 set_thread_name(__FUNCTION__
);
605 /* Grab the first ulock */
606 ull_lock(&lock_UI
, 2, UL_UNFAIR_LOCK
, 0);
608 thread_wait_to_boost(def_thread_port
, in_thread_port
, 37);
609 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE
, thread_at_sixty
);
611 T_LOG("Thread at UI priority trying to acquire IN lock");
612 ull_lock(&lock_IN
, 2, UL_UNFAIR_LOCK
, 0);
613 ull_unlock(&lock_UI
, 2, UL_UNFAIR_LOCK
, 0);
618 thread_at_in(void *arg __unused
)
620 in_thread_port
= mach_thread_self();
622 set_thread_name(__FUNCTION__
);
624 /* Grab the first ulock */
625 ull_lock(&lock_IN
, 2, UL_UNFAIR_LOCK
, 0);
627 T_LOG("Thread at IN priority got first lock ");
629 thread_wait_to_boost(main_thread_port
, def_thread_port
, 31);
631 /* Create a new thread at QOS_CLASS_USER_INTERACTIVE qos */
632 thread_create_at_qos(QOS_CLASS_USER_INTERACTIVE
, thread_at_ui
);
634 T_LOG("Thread at IN priority trying to acquire default lock");
635 ull_lock(&lock_DEF
, 1, UL_UNFAIR_LOCK
, 0);
636 ull_unlock(&lock_IN
, 2, UL_UNFAIR_LOCK
, 0);
641 thread_at_default(void *arg __unused
)
643 def_thread_port
= mach_thread_self();
645 set_thread_name(__FUNCTION__
);
647 /* Grab the first ulock */
648 ull_lock(&lock_DEF
, 1, UL_UNFAIR_LOCK
, 0);
650 T_LOG("Thread at DEFAULT priority got first lock ");
652 thread_wait_to_block(main_thread_port
);
654 /* Create a new thread at QOS_CLASS_USER_INITIATED qos */
655 thread_create_at_qos(QOS_CLASS_USER_INITIATED
, thread_at_in
);
657 T_LOG("Thread at Default priority trying to wait on dispatch sync for maintenance thread");
658 dispatch_sync_wait(main_thread_port
, QOS_CLASS_DEFAULT
);
659 ull_unlock(&lock_DEF
, 1, UL_UNFAIR_LOCK
, 0);
664 thread_at_maintenance(void *arg __unused
)
666 mach_port_t qos_send_port
;
667 mach_port_t special_reply_port
;
669 main_thread_port
= mach_thread_self();
671 set_thread_name(__FUNCTION__
);
673 kern_return_t kr
= bootstrap_look_up(bootstrap_port
,
674 TURNSTILE_MULTIHOP_SERVICE_NAME
, &qos_send_port
);
675 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "client bootstrap_look_up");
677 special_reply_port
= thread_get_special_reply_port();
678 T_QUIET
; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port
), "get_thread_special_reply_port");
680 /* Become the dispatch sync owner, dispatch_sync_owner will be set in dispatch_sync_wait function */
682 /* Send an async message */
683 send(qos_send_port
, MACH_PORT_NULL
, MACH_PORT_NULL
,
684 (uint32_t)_pthread_qos_class_encode(QOS_CLASS_MAINTENANCE
, 0, 0), 0);
686 /* Send a sync message */
687 send(qos_send_port
, special_reply_port
, MACH_PORT_NULL
,
688 (uint32_t)_pthread_qos_class_encode(QOS_CLASS_MAINTENANCE
, 0, 0), 0);
690 /* Create a new thread at QOS_CLASS_DEFAULT qos */
691 thread_create_at_qos(QOS_CLASS_DEFAULT
, thread_at_default
);
693 /* Block on Sync IPC */
694 receive(special_reply_port
, qos_send_port
);
696 dispatch_sync_cancel(def_thread_port
, QOS_CLASS_DEFAULT
);
700 T_HELPER_DECL(three_ulock_sync_ipc_hop
,
701 "Create chain of 4 threads with 3 ulocks and 1 sync IPC at different qos")
703 dt_stat_time_t roundtrip_stat
= dt_stat_time_create("multihop_lock_acquire");
705 T_STAT_MEASURE_LOOP(roundtrip_stat
) {
707 thread_create_at_qos(QOS_CLASS_MAINTENANCE
, thread_at_maintenance
);
714 dt_stat_finalize(roundtrip_stat
);
719 thread_create_at_qos(qos_class_t qos
, void * (*function
)(void *))
721 qos_class_t qos_thread
;
726 ret
= setpriority(PRIO_DARWIN_ROLE
, 0, PRIO_DARWIN_ROLE_UI_FOCAL
);
728 T_LOG("set priority failed\n");
731 pthread_attr_init(&attr
);
732 pthread_attr_set_qos_class_np(&attr
, qos
, 0);
733 pthread_create(&thread
, &attr
, function
, NULL
);
735 T_LOG("pthread created\n");
736 pthread_get_qos_class_np(thread
, &qos_thread
, NULL
);
739 #pragma mark Mach receive - kevent_qos
742 expect_kevent_id_recv(mach_port_t port
)
746 T_QUIET
; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
748 (pthread_workqueue_function_workloop_t
)workloop_cb_test_intransit
, 0, 0), NULL
);
750 struct kevent_qos_s kev
[] = {{
752 .filter
= EVFILT_MACHPORT
,
753 .flags
= EV_ADD
| EV_UDATA_SPECIFIC
| EV_DISPATCH
| EV_VANISHED
,
754 .fflags
= (MACH_RCV_MSG
| MACH_RCV_LARGE
| MACH_RCV_LARGE_IDENTITY
|
755 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX
) |
756 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
) |
759 .qos
= (int32_t)_pthread_qos_class_encode(QOS_CLASS_MAINTENANCE
, 0, 0)
762 struct kevent_qos_s kev_err
[] = {{ 0 }};
764 /* Setup workloop for mach msg rcv */
765 r
= kevent_id(25, kev
, 1, kev_err
, 1, NULL
,
766 NULL
, KEVENT_FLAG_WORKLOOP
| KEVENT_FLAG_ERROR_EVENTS
);
768 T_QUIET
; T_ASSERT_POSIX_SUCCESS(r
, "kevent_id");
769 T_QUIET
; T_ASSERT_EQ(r
, 0, "no errors returned from kevent_id");
772 T_HELPER_DECL(server_kevent_id
,
773 "Reply with the QoS that a dispatch source event handler ran with")
775 expect_kevent_id_recv(get_server_port());
777 T_ASSERT_FAIL("should receive a message");
780 #define TEST_MULTIHOP(server_name, client_name, name) \
781 T_DECL(server_kevent_id_##name, \
782 "Event delivery using a kevent_id", \
783 T_META_ASROOT(YES)) \
785 run_client_server(server_name, client_name); \
788 #define TEST_MULTIHOP_SPIN(server_name, client_name, name) \
789 T_DECL(server_kevent_id_##name, \
790 "Event delivery using a kevent_id", \
791 T_META_ASROOT(YES), T_META_ENABLED(FALSE)) \
793 spin_for_ever = true; \
794 run_client_server(server_name, client_name); \
795 spin_for_ever = false; \
799 * Test 1: Test multihop priority boosting with ulocks, dispatch sync and sync IPC.
801 * Create thread's at different Qos and acquire a ulock and block on next ulock/dispatch sync
802 * creating a sync chain. The last hop the chain is blocked on Sync IPC.
804 TEST_MULTIHOP("server_kevent_id", "three_ulock_sync_ipc_hop", three_ulock_sync_ipc_hop
)
807 * Test 2: Test multihop priority boosting with ulocks, dispatch sync and sync IPC.
809 * Create thread's at different Qos and acquire a ulock and block on next ulock/dispatch sync
810 * creating a sync chain. The last hop the chain is blocked on Sync IPC.
811 * Before the last priority 60 thread blocks on ulock, it also starts spinforeverd at priority 31.
813 TEST_MULTIHOP_SPIN("server_kevent_id", "three_ulock_sync_ipc_hop", three_ulock_sync_ipc_hop_spin
)