2 * prioritize process launch: Tests prioritized process launch across posix spawn and exec.
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 <spawn_private.h>
33 T_GLOBAL_META(T_META_NAMESPACE("xnu.prioritize_process_launch"),
34 T_META_RUN_CONCURRENTLY(true));
36 #define HELPER_TIMEOUT_SECS (3000)
37 #define MACH_RCV_OPTIONS (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY | \
38 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) | \
39 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0))
42 thread_create_at_qos(qos_class_t qos
, void * (*function
)(void *), void *arg
);
43 static mach_port_t sr_port
;
46 #pragma mark Mach receive
49 create_pthpriority_voucher(mach_msg_priority_t qos
)
51 char voucher_buf
[sizeof(mach_voucher_attr_recipe_data_t
) + sizeof(ipc_pthread_priority_value_t
)];
53 mach_voucher_t voucher
= MACH_PORT_NULL
;
55 ipc_pthread_priority_value_t ipc_pthread_priority_value
=
56 (ipc_pthread_priority_value_t
)qos
;
58 mach_voucher_attr_raw_recipe_array_t recipes
;
59 mach_voucher_attr_raw_recipe_size_t recipe_size
= 0;
60 mach_voucher_attr_recipe_t recipe
=
61 (mach_voucher_attr_recipe_t
)&voucher_buf
[recipe_size
];
63 recipe
->key
= MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
;
64 recipe
->command
= MACH_VOUCHER_ATTR_PTHPRIORITY_CREATE
;
65 recipe
->previous_voucher
= MACH_VOUCHER_NULL
;
66 memcpy((char *)&recipe
->content
[0], &ipc_pthread_priority_value
, sizeof(ipc_pthread_priority_value
));
67 recipe
->content_size
= sizeof(ipc_pthread_priority_value_t
);
68 recipe_size
+= sizeof(mach_voucher_attr_recipe_data_t
) + recipe
->content_size
;
70 recipes
= (mach_voucher_attr_raw_recipe_array_t
)&voucher_buf
[0];
72 ret
= host_create_mach_voucher(mach_host_self(),
77 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "client host_create_mach_voucher");
83 mach_port_t send_port
,
84 mach_port_t reply_port
,
86 mach_msg_priority_t qos
,
87 mach_msg_option_t options
,
90 kern_return_t ret
= 0;
93 mach_msg_header_t header
;
95 mach_msg_port_descriptor_t port_descriptor
;
98 .msgh_remote_port
= send_port
,
99 .msgh_local_port
= reply_port
,
100 .msgh_bits
= MACH_MSGH_BITS_SET(send_disposition
,
101 reply_port
? MACH_MSG_TYPE_MAKE_SEND_ONCE
: 0,
102 MACH_MSG_TYPE_MOVE_SEND
,
103 MACH_MSGH_BITS_COMPLEX
),
105 .msgh_size
= sizeof(send_msg
),
108 .msgh_descriptor_count
= 1,
112 .disposition
= MACH_MSG_TYPE_MOVE_RECEIVE
,
113 .type
= MACH_MSG_PORT_DESCRIPTOR
,
117 if (options
& MACH_SEND_SYNC_USE_THRPRI
) {
118 send_msg
.header
.msgh_voucher_port
= create_pthpriority_voucher(qos
);
121 if (msg_port
== MACH_PORT_NULL
) {
122 send_msg
.body
.msgh_descriptor_count
= 0;
125 ret
= mach_msg(&(send_msg
.header
),
129 ((reply_port
? MACH_SEND_SYNC_OVERRIDE
: 0) | options
),
130 send_msg
.header
.msgh_size
,
136 T_QUIET
; T_ASSERT_MACH_SUCCESS(ret
, "client mach_msg");
141 mach_port_t rcv_port
,
142 mach_port_t notify_port
)
144 kern_return_t ret
= 0;
147 mach_msg_header_t header
;
148 mach_msg_body_t body
;
149 mach_msg_port_descriptor_t port_descriptor
;
150 mach_msg_trailer_t trailer
;
154 .msgh_remote_port
= MACH_PORT_NULL
,
155 .msgh_local_port
= rcv_port
,
156 .msgh_size
= sizeof(rcv_msg
),
160 T_LOG("Client: Starting sync receive\n");
162 ret
= mach_msg(&(rcv_msg
.header
),
166 rcv_msg
.header
.msgh_size
,
173 get_pri(thread_t thread_port
)
177 thread_extended_info_data_t extended_info
;
178 mach_msg_type_number_t count
= THREAD_EXTENDED_INFO_COUNT
;
179 kr
= thread_info(thread_port
, THREAD_EXTENDED_INFO
,
180 (thread_info_t
)&extended_info
, &count
);
182 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "thread_info");
184 return extended_info
.pth_curpri
;
188 set_thread_name(const char *fn_name
)
192 thread_t thread_port
= pthread_mach_thread_np(pthread_self());
194 int pri
= get_pri(thread_port
);
196 snprintf(name
, sizeof(name
), "%s at pri %2d", fn_name
, pri
);
197 pthread_setname_np(name
);
201 thread_wait_to_block(mach_port_t thread_port
)
203 thread_extended_info_data_t extended_info
;
207 mach_msg_type_number_t count
= THREAD_EXTENDED_INFO_COUNT
;
208 kr
= thread_info(thread_port
, THREAD_EXTENDED_INFO
,
209 (thread_info_t
)&extended_info
, &count
);
211 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "thread_info");
213 if (extended_info
.pth_run_state
== TH_STATE_WAITING
) {
214 T_LOG("Target thread blocked\n");
217 thread_switch(thread_port
, SWITCH_OPTION_DEPRESS
, 0);
222 thread_sync_rcv(void *arg
)
224 mach_port_t port
= (mach_port_t
)arg
;
225 mach_port_t special_reply_port
;
227 set_thread_name(__FUNCTION__
);
228 special_reply_port
= thread_get_special_reply_port();
229 T_QUIET
; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port
), "get_thread_special_reply_port");
231 sr_port
= special_reply_port
;
232 /* Do a sync rcv on special reply port and push on given arg port */
233 receive(special_reply_port
, port
);
238 thread_create_at_qos(qos_class_t qos
, void * (*function
)(void *), void *arg
)
240 qos_class_t qos_thread
;
245 ret
= setpriority(PRIO_DARWIN_ROLE
, 0, PRIO_DARWIN_ROLE_UI_FOCAL
);
247 T_LOG("set priority failed\n");
250 pthread_attr_init(&attr
);
251 pthread_attr_set_qos_class_np(&attr
, qos
, 0);
252 pthread_create(&pthread
, &attr
, function
, arg
);
254 T_LOG("pthread created\n");
255 pthread_get_qos_class_np(pthread
, &qos_thread
, NULL
);
260 get_sync_push_port_at_qos(qos_class_t qos
)
267 /* Create a rcv right to have a sync ipc push from a thread */
268 kr
= mach_port_allocate(mach_task_self(),
269 MACH_PORT_RIGHT_RECEIVE
,
272 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "sync push port mach_port_allocate");
274 kr
= mach_port_insert_right(mach_task_self(),
277 MACH_MSG_TYPE_MAKE_SEND
);
279 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "sync push port mach_port_insert_right");
281 /* Create a thread at given qos and start a sync push on given port */
282 pthread
= thread_create_at_qos(qos
, thread_sync_rcv
, (void *)(uintptr_t)port
);
283 thread
= pthread_mach_thread_np(pthread
);
284 thread_wait_to_block(thread
);
290 create_port_and_copyin_a_port(mach_port_t port
)
292 mach_port_t new_port
;
295 /* Create a rcv right */
296 kr
= mach_port_allocate(mach_task_self(),
297 MACH_PORT_RIGHT_RECEIVE
,
300 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "copyin mach_port_allocate");
302 kr
= mach_port_insert_right(mach_task_self(),
305 MACH_MSG_TYPE_MAKE_SEND
);
307 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "copyin mach_port_insert_right");
309 send(new_port
, MACH_PORT_NULL
, port
, 0, 0, MACH_MSG_TYPE_COPY_SEND
);
314 posix_spawn_child_with_watch_ports(
317 mach_port_t
*port_array
,
321 char *new_argv
[] = { binary
, arg
, NULL
};
323 posix_spawnattr_t attr
;
325 ret
= posix_spawnattr_init(&attr
);
326 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "posix_spawnattr_init");
328 ret
= posix_spawnattr_set_importancewatch_port_np(&attr
, arrayCnt
, port_array
);
329 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "posix_spawnattr_set_importancewatch_port_np");
331 ret
= posix_spawn(&child_pid
, binary
, NULL
, &attr
, new_argv
, NULL
);
332 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "posix_spawn");
334 ret
= posix_spawnattr_destroy(&attr
);
335 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "posix_spawnattr_destroy");
341 worker_cb(pthread_priority_t __unused priority
)
343 T_FAIL("a worker thread was created");
347 event_cb(void ** __unused events
, int * __unused nevents
)
349 T_FAIL("a kevent routine was called instead of workloop");
353 workloop_cb_test_intransit(uint64_t *workloop_id __unused
, void **eventslist
, int *events
)
359 struct kevent_qos_s
*kev
= *eventslist
;
360 mach_msg_header_t
*hdr
= (mach_msg_header_t
*)kev
->ext
[0];
361 port
= hdr
->msgh_local_port
;
363 T_LOG("Workloop handler workloop_cb_test_intransit called. ");
364 T_LOG("The total events returned is %d", *events
);
366 priority
= get_pri(mach_thread_self());
367 T_EXPECT_EQ(priority
, 47, "Priority of servicer is %d", priority
);
369 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port
, 1);
371 /* Make sure our priority has dropped */
372 priority
= get_pri(mach_thread_self());
373 T_EXPECT_EQ(priority
, 31, "Priority of servicer is %d", priority
);
377 /*enqueue the port to sever the temp onwer boost */
378 create_port_and_copyin_a_port(port
);
380 waitpid(pid
, &stat
, 0);
384 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
385 T_EXPECT_EQ(WEXITSTATUS(stat
), 31, "Temp owner boost did not work correctly with knotes");
390 workloop_cb_test_knote_kill(uint64_t *workloop_id __unused
, void **eventslist
, int *events
)
396 struct kevent_qos_s
*kev
= *eventslist
;
397 mach_msg_header_t
*hdr
= (mach_msg_header_t
*)kev
->ext
[0];
398 port
= hdr
->msgh_local_port
;
400 T_LOG("Workloop handler workloop_cb_test_knote_kill called. ");
401 T_LOG("The total events returned is %d", *events
);
403 priority
= get_pri(mach_thread_self());
404 T_EXPECT_EQ(priority
, 47, "Priority of servicer is %d", priority
);
406 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "EXIT", &port
, 1);
410 /* Make sure our priority is boosted again */
411 priority
= get_pri(mach_thread_self());
412 T_EXPECT_EQ(priority
, 47, "Priority of servicer is %d", priority
);
414 waitpid(pid
, &stat
, 0);
418 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
419 T_EXPECT_EQ(WEXITSTATUS(stat
), 47, "Temp owner boost did not work correctly with knotes");
424 workloop_cb_test_sync_bootstrap(uint64_t *workloop_id __unused
, void **eventslist
, int *events
)
426 static pid_t pid
= 0;
429 static mach_port_t port
= MACH_PORT_NULL
;
430 struct kevent_qos_s
*kev
= *eventslist
;
431 mach_msg_header_t
*hdr
= (mach_msg_header_t
*)kev
->ext
[0];
433 T_LOG("Workloop handler workloop_cb_test_knote_kill called. ");
434 T_LOG("The total events returned is %d", *events
);
436 /* Check if called for peek */
438 priority
= get_pri(mach_thread_self());
439 T_EXPECT_EQ(priority
, 47, "Priority of servicer is %d", priority
);
441 port
= (mach_port_t
)kev
->ident
;
442 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "MSGSYNC", &port
, 1);
444 /* Wait till the priority of servicer is 47 */
445 T_LOG("Waiting for the servicer to be boosted");
448 priority
= get_pri(mach_thread_self());
449 } while (priority
!= 47);
451 T_EXPECT_EQ(priority
, 47, "Priority of servicer is %d", priority
);
453 /* Get the reply port and send the receive right in it */
454 mach_port_t reply_port
= hdr
->msgh_remote_port
;
455 T_LOG("The rcv right to send is %d", port
);
456 send(reply_port
, MACH_PORT_NULL
, port
, 0, 0, MACH_MSG_TYPE_MOVE_SEND_ONCE
);
458 waitpid(pid
, &stat
, 0);
460 /* The handler priority should not be boosted anymore */
461 priority
= get_pri(mach_thread_self());
462 T_EXPECT_EQ(priority
, 31, "Priority of servicer is %d", priority
);
464 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
465 T_EXPECT_EQ(WEXITSTATUS(stat
), 31, "Temp owner boost did not work correctly with knotes");
472 register_workloop_for_port(
474 pthread_workqueue_function_workloop_t func
,
475 unsigned int options
)
479 /* register workloop handler with pthread */
481 T_QUIET
; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
483 (pthread_workqueue_function_workloop_t
)func
, 0, 0), NULL
);
486 /* attach port to workloop */
487 struct kevent_qos_s kev
[] = {{
489 .filter
= EVFILT_MACHPORT
,
490 .flags
= EV_ADD
| EV_UDATA_SPECIFIC
| EV_DISPATCH
| EV_VANISHED
,
493 .qos
= (int32_t)_pthread_qos_class_encode(QOS_CLASS_DEFAULT
, 0, 0)
496 struct kevent_qos_s kev_err
[] = {{ 0 }};
498 /* Setup workloop for mach msg rcv */
499 r
= kevent_id(25, kev
, 1, kev_err
, 1, NULL
,
500 NULL
, KEVENT_FLAG_WORKLOOP
| KEVENT_FLAG_ERROR_EVENTS
);
502 T_QUIET
; T_ASSERT_POSIX_SUCCESS(r
, "kevent_id");
503 T_QUIET
; T_ASSERT_EQ(r
, 0, "no errors returned from kevent_id");
507 * Test 1: Test turnstile boosting for temp owner ports for posix_spawn.
509 * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
510 * test that spawned binary has the temp owner push of the port.
512 T_DECL(posix_spawn_basic_priority
, "Basic posix spawn temp owner priority test", T_META_ASROOT(YES
))
518 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
519 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "EXIT", &port
, 1);
521 waitpid(pid
, &stat
, 0);
523 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
524 T_EXPECT_EQ(WEXITSTATUS(stat
), 47, "spawn did not properly boost main thread");
529 * Test 2: Test turnstile boosting for temp owner ports for posix_spawn and exec.
531 * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
532 * test that spawned binary has the temp owner push of the port. The spawned binary will exec
533 * and verify that it still has the push.
535 T_DECL(posix_spawn_exec_basic_priority
, "Basic posix spawn/exec temp owner priority test", T_META_ASROOT(YES
))
541 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
542 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "EXEC", &port
, 1);
544 waitpid(pid
, &stat
, 0);
546 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
547 T_EXPECT_EQ(WEXITSTATUS(stat
), 47, "spawn/exec did not properly boost main thread");
552 * Test 3: Test turnstile boosting for temp owner ports for posix_spawn and set exec.
554 * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
555 * test that spawned binary has the temp owner push of the port. The spawned binary will
556 * posix_spawn set exec and verify that it still has the push.
558 T_DECL(posix_spawn_set_exec_basic_priority
, "Basic posix spawn set exec temp owner priority test", T_META_ASROOT(YES
))
564 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
565 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "SETEXEC", &port
, 1);
567 waitpid(pid
, &stat
, 0);
569 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
570 T_EXPECT_EQ(WEXITSTATUS(stat
), 47, "spawn set exec did not properly boost main thread");
575 * Test 4: Test turnstile boosting for temp owner ports for posix_spawn and set exec.
577 * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
578 * test that spawned binary has the temp owner push of the port. The spawned binary already
579 * having the temp owner push will try to do set exec with watchports which should fail.
581 T_DECL(posix_spawn_set_exec_with_more_ports
, "posix spawn set exec with more watch ports", T_META_ASROOT(YES
))
587 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
588 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "SETEXEC_PORTS", &port
, 1);
590 waitpid(pid
, &stat
, 0);
592 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
593 T_EXPECT_EQ(WEXITSTATUS(stat
), EINVAL
, "spawn set exec did not error out when watchports were passed to already boosted process");
598 * Test 5: Test turnstile boosting for temp owner ports for multiple posix_spawns.
600 * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port, then
601 * pass the same port as a watchport to another posix_spawn and verify that the boost was
602 * transferred to the new process.
604 T_DECL(posix_spawn_multiple
, "multiple posix_spawn with same watchport", T_META_ASROOT(YES
))
610 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
611 pid1
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port
, 1);
613 /* Let the child 1 execute a little, the sleep here is optional */
616 pid2
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "EXIT", &port
, 1);
618 waitpid(pid2
, &stat2
, 0);
619 waitpid(pid1
, &stat1
, 0);
621 T_QUIET
; T_LOG("The return stat for child 1 is is %d", WEXITSTATUS(stat1
));
622 T_QUIET
; T_LOG("The return stat for child 2 is is %d", WEXITSTATUS(stat2
));
623 T_EXPECT_EQ(WEXITSTATUS(stat2
), 47, "spawn of multiple processes with same watchport did not transfer the boost correctly");
624 T_EXPECT_EQ(WEXITSTATUS(stat1
), 31, "spawn of multiple processes with same watchport did not transfer the boost correctly");
629 * Test 6: Test turnstile boosting for temp owner ports for posix_spawn for dead port.
631 * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
632 * test that spawned binary has the temp owner push of the port. Destroy the port and verify
633 * the temp owner push has gone away.
635 T_DECL(posix_spawn_dead_reply_port
, "posix spawn with reply port destory", T_META_ASROOT(YES
))
642 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
643 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port
, 1);
645 /* Let the child execute a little, the sleep here is optional */
648 /* Destory the special reply port */
649 kr
= mach_port_mod_refs(mach_task_self(), sr_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
650 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "posix_spaw_dead_port mach_port_mod_refs");
652 waitpid(pid
, &stat
, 0);
654 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
655 T_EXPECT_EQ(WEXITSTATUS(stat
), 31, "Temp owner boost was not removed on port death");
660 * Test 7: Test turnstile boosting for temp owner ports for posix_spawn for dead port.
662 * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
663 * test that spawned binary has the temp owner push of the port. Destroy the port and verify
664 * the temp owner push has gone.
666 T_DECL(posix_spawn_dead_port
, "posix spawn with port destory", T_META_ASROOT(YES
))
673 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
674 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port
, 1);
676 /* Destory the port */
677 kr
= mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_RECEIVE
, -1);
678 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "posix_spaw_dead_port mach_port_mod_refs");
680 waitpid(pid
, &stat
, 0);
682 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
683 T_EXPECT_EQ(WEXITSTATUS(stat
), 31, "Temp owner boost was not removed on port death");
688 * Test 8: Test turnstile boosting for temp owner ports for posix_spawn when port is copied in.
690 * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
691 * test that spawned binary has the temp owner push of the port. Copyin the port and verify
692 * the temp owner push has gone.
694 T_DECL(posix_spawn_copyin_port
, "posix spawn with copyin port", T_META_ASROOT(YES
))
700 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
701 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port
, 1);
703 /* Let the child execute a little, the sleep here is optional */
706 /* Copyin the port in another port */
707 create_port_and_copyin_a_port(port
);
709 waitpid(pid
, &stat
, 0);
711 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
712 T_EXPECT_EQ(WEXITSTATUS(stat
), 31, "Temp owner boost was not removed on port copyin");
717 * Test 9: Test turnstile boosting for temp owner ports for posix_spawn with multiple ports.
719 * Create multiple ports with sync IPC push and then pass the port to posix_spawn as watch ports and
720 * test that spawned binary has the temp owner push of the ports. Copyin ports one by one and verify
723 T_DECL(posix_spawn_multiple_port
, "posix spawn with multiple ports", T_META_ASROOT(YES
))
729 port
[0] = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
730 port
[1] = get_sync_push_port_at_qos(QOS_CLASS_USER_INITIATED
);
731 pid
= posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "MULTIWAIT", port
, 2);
733 /* Let the child execute a little, the sleep here is optional */
736 /* Copyin the port in another port */
737 create_port_and_copyin_a_port(port
[0]);
739 /* Let the child execute a little, the sleep here is optional */
742 /* Copyin the port in another port */
743 create_port_and_copyin_a_port(port
[1]);
745 waitpid(pid
, &stat
, 0);
747 T_QUIET
; T_LOG("The return stat is %d", WEXITSTATUS(stat
));
748 T_EXPECT_EQ(WEXITSTATUS(stat
), 31, "Temp owner boost did not work correctly with multiple ports");
753 * Test 10: Test turnstile boosting for temp owner ports for posix_spawn when port attached to a knote.
755 * Create a port with sync IPC push attach a workloop knote to it, send a message on the port, then in the
756 * servicer pass the port to posix_spawn as a watch port and test that spawned binary has the temp owner
757 * push of the port and the servicer looses the boost.
759 T_DECL(posix_spawn_knote
, "posix spawn with temp owner port attached to knote", T_META_ASROOT(YES
))
763 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
765 /* attach port to a workloop */
766 register_workloop_for_port(port
, workloop_cb_test_intransit
, MACH_RCV_OPTIONS
);
768 /* send a message on port to activate workloop handler */
769 send(port
, MACH_PORT_NULL
, MACH_PORT_NULL
, QOS_CLASS_DEFAULT
, 0, MACH_MSG_TYPE_COPY_SEND
);
774 * Test 11: Test turnstile boosting for temp owner ports for posix_spawn when port attached to a knote.
776 * Create a port with sync IPC push attach a workloop knote to it, send a message on the port, then in the
777 * servicer pass the port to posix_spawn as a watch port and test that spawned binary has the temp owner
778 * push of the port and the servicer looses the boost, verify that once the spawned binary dies, the servicer
781 T_DECL(posix_spawn_knote_ret
, "posix spawn with temp owner port attached to knote with spawned binary dead", T_META_ASROOT(YES
))
785 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
787 register_workloop_for_port(port
, workloop_cb_test_knote_kill
, MACH_RCV_OPTIONS
);
789 /* send a message on port to activate workloop handler */
790 send(port
, MACH_PORT_NULL
, MACH_PORT_NULL
, QOS_CLASS_DEFAULT
, 0, MACH_MSG_TYPE_COPY_SEND
);
795 * Test 12: Test turnstile boosting for temp owner ports and mach msg option for sync bootstrap_checkin.
797 * Create a port with sync IPC push attach a workloop knote to it, send a message on the port, then in the
798 * servicer pass the port to posix_spawn as a watch port and test that spawned binary has the temp owner
799 * push of the port and the servicer looses the boost, the spawn binary then does a sync bootstrap_checkin
800 * with test binary to get the receive right and verify that is still has the boost.
802 T_DECL(mach_msg_sync_boostrap_checkin
, "test mach msg option for sync bootstrap_checkin", T_META_ASROOT(YES
))
805 mach_port_t sync_port
;
808 port
= get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE
);
810 register_workloop_for_port(port
, workloop_cb_test_sync_bootstrap
, MACH_RCV_SYNC_PEEK
);
812 /* Create a mach port for spawned binary to do bootstrap checkin */
813 kr
= mach_port_allocate(mach_task_self(),
814 MACH_PORT_RIGHT_RECEIVE
,
817 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "mach_msg_sync_boostrap_checkin mach_port_allocate");
819 kr
= mach_port_insert_right(mach_task_self(),
822 MACH_MSG_TYPE_MAKE_SEND
);
824 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "mach_msg_sync_boostrap_checkin mach_port_insert_right");
826 kr
= mach_port_mod_refs(mach_task_self(), sync_port
, MACH_PORT_RIGHT_SEND
, 1);
827 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "mach_msg_sync_boostrap_checkin mach_port_mod_refs");
829 register_workloop_for_port(sync_port
, NULL
, MACH_RCV_OPTIONS
);
831 /* Stash the port in task to make sure child also gets it */
832 kr
= mach_ports_register(mach_task_self(), &sync_port
, 1);
833 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "mach_msg_sync_boostrap_checkin mach_ports_register");
835 /* send a message on port to activate workloop handler */
836 send(port
, MACH_PORT_NULL
, MACH_PORT_NULL
, QOS_CLASS_DEFAULT
, 0, MACH_MSG_TYPE_COPY_SEND
);