This is xnu-11215.1.10. See this file in:
/*
* mach eventlink: Tests mach eventlink kernel synchronization primitive.
*/
#include <darwintest.h>
#include <darwintest_multiprocess.h>
#include <pthread.h>
#include <launch.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <mach/mach_voucher.h>
#include <pthread/workqueue_private.h>
#include <voucher/ipc_pthread_priority_types.h>
#include <servers/bootstrap.h>
#include <stdlib.h>
#include <sys/event.h>
#include <unistd.h>
#include <crt_externs.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <libkern/OSAtomic.h>
#include <sys/wait.h>
#include <spawn.h>
#include <spawn_private.h>
#include <mach/mach_eventlink.h>
#include <os/atomic_private.h>
T_GLOBAL_META(T_META_NAMESPACE("xnu.mach_eventlink"),
T_META_RUN_CONCURRENTLY(true));
static int g_loop_iterations = 100000;
static kern_return_t
test_eventlink_create(mach_port_t *port_pair)
{
kern_return_t kr;
kr = mach_eventlink_create(mach_task_self(), MELC_OPTION_NO_COPYIN, port_pair);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_create");
return kr;
}
static pthread_t
thread_create_for_test(void * (*function)(void *), void *arg)
{
pthread_t pthread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&pthread, &attr, function, arg);
T_LOG("pthread created\n");
return pthread;
}
static void *
while1loop(__unused void *arg)
{
while (1) {
;
}
return NULL;
}
static void *
test_eventlink_wait_with_timeout(void *arg)
{
kern_return_t kr;
mach_port_t eventlink_port = (mach_port_t) (uintptr_t)arg;
mach_port_t self = mach_thread_self();
uint64_t ticks = mach_absolute_time();
uint64_t count = 1;
/* Associate thread with eventlink port */
kr = mach_eventlink_associate(eventlink_port, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate");
/* Wait on the eventlink with timeout */
kr = mach_eventlink_wait_until(eventlink_port, &count, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, ticks + 5000);
T_EXPECT_MACH_ERROR(kr, KERN_OPERATION_TIMED_OUT, "mach_eventlink_wait_until returned expected error");
T_EXPECT_EQ(count, (uint64_t)0, "mach_eventlink_wait_until returned correct count value");
return NULL;
}
static void *
test_eventlink_wait_no_wait(void *arg)
{
kern_return_t kr;
mach_port_t eventlink_port = (mach_port_t) (uintptr_t)arg;
mach_port_t self = mach_thread_self();
uint64_t count = 1;
/* Associate thread with eventlink port */
kr = mach_eventlink_associate(eventlink_port, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate");
/* Wait on the eventlink */
kr = mach_eventlink_wait_until(eventlink_port, &count, MELSW_OPTION_NO_WAIT,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_EXPECT_MACH_ERROR(kr, KERN_OPERATION_TIMED_OUT, "mach_eventlink_wait_until returned expected error");
T_EXPECT_EQ(count, (uint64_t)0, "mach_eventlink_wait_until returned correct count value");
return NULL;
}
static void *
test_eventlink_wait_destroy(void *arg)
{
kern_return_t kr;
mach_port_t eventlink_port = (mach_port_t) (uintptr_t)arg;
mach_port_t self = mach_thread_self();
uint64_t count = 1;
/* Associate thread with eventlink port */
kr = mach_eventlink_associate(eventlink_port, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate");
/* Wait on the eventlink */
kr = mach_eventlink_wait_until(eventlink_port, &count, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_EXPECT_MACH_ERROR(kr, KERN_TERMINATED, "mach_eventlink_wait_until returned expected error");
return NULL;
}
static void *
test_eventlink_wait_for_signal(void *arg)
{
kern_return_t kr;
mach_port_t eventlink_port = (mach_port_t) (uintptr_t)arg;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
/* Associate thread with eventlink port */
kr = mach_eventlink_associate(eventlink_port, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate");
/* Wait on the eventlink */
kr = mach_eventlink_wait_until(eventlink_port, &count, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_wait_until returned correct count value");
return NULL;
}
static void *
test_eventlink_wait_then_signal(void *arg)
{
kern_return_t kr;
mach_port_t eventlink_port = (mach_port_t) (uintptr_t)arg;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
/* Associate thread with eventlink port */
kr = mach_eventlink_associate(eventlink_port, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate");
/* Wait on the eventlink */
kr = mach_eventlink_wait_until(eventlink_port, &count, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_wait_until returned correct count value");
/* Signal the eventlink to wakeup other side */
kr = mach_eventlink_signal(eventlink_port, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal");
return NULL;
}
static void *
test_eventlink_wait_then_wait_signal_with_no_wait(void *arg)
{
kern_return_t kr;
mach_port_t eventlink_port = (mach_port_t) (uintptr_t)arg;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
/* Associate thread with eventlink port */
kr = mach_eventlink_associate(eventlink_port, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate");
/* Wait on the eventlink */
kr = mach_eventlink_wait_until(eventlink_port, &count, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_wait_until returned correct count value");
/* Signal wait the eventlink */
kr = mach_eventlink_signal_wait_until(eventlink_port, &count, 0, MELSW_OPTION_NO_WAIT,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_EXPECT_MACH_ERROR(kr, KERN_OPERATION_TIMED_OUT, "mach_eventlink_wait_until returned expected error");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_wait_until returned correct count value");
return NULL;
}
static void *
test_eventlink_wait_then_wait_signal_with_prepost(void *arg)
{
kern_return_t kr;
mach_port_t eventlink_port = (mach_port_t) (uintptr_t)arg;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
/* Associate thread with eventlink port */
kr = mach_eventlink_associate(eventlink_port, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate");
/* Wait on the eventlink */
kr = mach_eventlink_wait_until(eventlink_port, &count, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_wait_until returned correct count value");
/* Signal wait the eventlink with stale counter value */
count = 0;
kr = mach_eventlink_signal_wait_until(eventlink_port, &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_wait_until returned correct count value");
return NULL;
}
static void *
test_eventlink_wait_then_signal_loop(void *arg)
{
kern_return_t kr;
mach_port_t eventlink_port = (mach_port_t) (uintptr_t)arg;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
int i;
/* Associate thread with eventlink port */
kr = mach_eventlink_associate(eventlink_port, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate");
/* Wait on the eventlink */
kr = mach_eventlink_wait_until(eventlink_port, &count, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_wait_until returned correct count value");
for (i = 1; i < g_loop_iterations; i++) {
/* Signal wait the eventlink */
kr = mach_eventlink_signal_wait_until(eventlink_port, &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal_wait_until");
T_QUIET; T_EXPECT_EQ(count, (uint64_t)(i + 1), "mach_eventlink_wait_until returned correct count value");
}
/* Signal the eventlink to wakeup other side */
kr = mach_eventlink_signal(eventlink_port, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal");
return NULL;
}
/*
* Test 1: Create ipc eventlink kernel object.
*
* Calls eventlink creates which returns a pair of eventlink port objects.
*/
T_DECL(test_eventlink_create, "eventlink create test", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 2: Create ipc eventlink kernel object and call eventlink destroy
*
* Calls eventlink creates which returns a pair of eventlink port objects.
* Calls eventlink destroy on eventlink port pair.
*/
T_DECL(test_eventlink_destroy, "eventlink destroy test", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
kr = mach_eventlink_destroy(port_pair[0]);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_destroy");
kr = mach_eventlink_destroy(port_pair[1]);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_destroy");
}
/*
* Test 3: Associate threads to eventlink object.
*
* Create eventlink object pair and associate threads to each side and then
* disassociate threads and check for error conditions.
*/
T_DECL(test_eventlink_associate, "eventlink associate test", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
mach_port_t self = mach_thread_self();
mach_port_t other_thread = MACH_PORT_NULL;
pthread_t pthread;
/* eventlink associate to NULL eventlink object */
kr = mach_eventlink_associate(MACH_PORT_NULL, self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_EXPECT_MACH_ERROR(kr, MACH_SEND_INVALID_DEST, "mach_eventlink_associate with null eventlink returned expected error");
/* eventlink disassociate to NULL eventlink object */
kr = mach_eventlink_disassociate(MACH_PORT_NULL, MELD_OPTION_NONE);
T_EXPECT_MACH_ERROR(kr, MACH_SEND_INVALID_DEST, "mach_eventlink_disassociate with null eventlink returned expected error");
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(while1loop, NULL);
other_thread = pthread_mach_thread_np(pthread);
for (int i = 0; i < 3; i++) {
/* Associate thread to eventlink objects */
kr = mach_eventlink_associate(port_pair[0], self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate for object 1");
kr = mach_eventlink_associate(port_pair[1], other_thread, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate for object 2");
/* Try to associate again with diff threads, expect failure */
kr = mach_eventlink_associate(port_pair[0], other_thread, 0, 0, 0, 0, MELA_OPTION_NONE);
T_EXPECT_MACH_ERROR(kr, KERN_NAME_EXISTS, "mach_eventlink_associate for associated "
"objects returned expected error");
kr = mach_eventlink_associate(port_pair[1], self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_EXPECT_MACH_ERROR(kr, KERN_NAME_EXISTS, "mach_eventlink_associate for associated "
"objects return expected error");
/* Try to disassociate the threads */
kr = mach_eventlink_disassociate(port_pair[0], MELD_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_disassociate for object 1");
kr = mach_eventlink_disassociate(port_pair[1], MELD_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_disassociate for object 2");
/* Try to disassociate the threads again, expect failure */
kr = mach_eventlink_disassociate(port_pair[0], MELD_OPTION_NONE);
T_EXPECT_MACH_ERROR(kr, KERN_INVALID_ARGUMENT, "mach_eventlink_disassociate for "
"disassociated objects returned expected error");
kr = mach_eventlink_disassociate(port_pair[1], MELD_OPTION_NONE);
T_EXPECT_MACH_ERROR(kr, KERN_INVALID_ARGUMENT, "mach_eventlink_disassociate for "
"disassociated objects returned expected error");
}
kr = mach_eventlink_destroy(port_pair[0]);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_destroy");
/* Try disassociate on other end of destoryed eventlink pair */
kr = mach_eventlink_disassociate(port_pair[1], MELD_OPTION_NONE);
T_EXPECT_MACH_ERROR(kr, KERN_TERMINATED, "mach_eventlink_disassociate for "
"terminated object returned expected error");
kr = mach_eventlink_destroy(port_pair[1]);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_destroy");
}
/*
* Test 4: Test eventlink wait with timeout.
*
* Create an eventlink object, associate threads and test eventlink wait with timeout.
*/
T_DECL(test_eventlink_wait_timeout, "eventlink wait timeout test", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_with_timeout, (void *)(uintptr_t)port_pair[0]);
sleep(10);
/* destroy the eventlink object, the wake status of thread will check if the test passsed or failed */
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
pthread_join(pthread, NULL);
}
/*
* Test 5: Test eventlink wait with no wait.
*
* Create an eventlink object, associate threads and test eventlink wait with no wait flag.
*/
T_DECL(test_eventlink_wait_no_wait, "eventlink wait no wait test", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_no_wait, (void *)(uintptr_t)port_pair[0]);
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 6: Test eventlink wait and destroy.
*
* Create an eventlink object, associate threads and destroy the port.
*/
T_DECL(test_eventlink_wait_and_destroy, "eventlink wait and destroy", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_destroy, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Increase the send right count for port before destroy to make sure no sender does not fire on destroy */
kr = mach_port_mod_refs(mach_task_self(), port_pair[0], MACH_PORT_RIGHT_SEND, 2);
T_ASSERT_MACH_SUCCESS(kr, "mach_port_mod_refs");
/* Destroy the port for thread to wakeup */
kr = mach_eventlink_destroy(port_pair[0]);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_destroy");
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 7: Test eventlink wait and destroy remote side.
*
* Create an eventlink object, associate threads, wait and destroy the remote eventlink port.
*/
T_DECL(test_eventlink_wait_and_destroy_remote, "eventlink wait and remote destroy", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_destroy, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Increase the send right count for port before destroy to make sure no sender does not fire on destroy */
kr = mach_port_mod_refs(mach_task_self(), port_pair[1], MACH_PORT_RIGHT_SEND, 2);
T_ASSERT_MACH_SUCCESS(kr, "mach_port_mod_refs");
/* Destroy the port for thread to wakeup */
kr = mach_eventlink_destroy(port_pair[1]);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_destroy");
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
}
/*
* Test 8: Test eventlink wait and deallocate port.
*
* Create an eventlink object, associate threads, wait and deallocate the eventlink port.
*/
T_DECL(test_eventlink_wait_and_deallocate, "eventlink wait and deallocate", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_destroy, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Destroy the port for thread to wakeup */
mach_port_deallocate(mach_task_self(), port_pair[0]);
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 9: Test eventlink wait and disassociate.
*
* Create an eventlink object, associate threads, wait and disassociate thread from the eventlink port.
*/
T_DECL(test_eventlink_wait_and_disassociate, "eventlink wait and disassociate", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_destroy, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Disassociate thread from eventlink for thread to wakeup */
kr = mach_eventlink_disassociate(port_pair[0], MELD_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_disassociate");
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[1]);
mach_port_deallocate(mach_task_self(), port_pair[0]);
}
/*
* Test 10: Test eventlink wait and signal.
*
* Create an eventlink object, associate threads and test wait signal.
*/
T_DECL(test_eventlink_wait_and_signal, "eventlink wait and signal", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
mach_port_t self = mach_thread_self();
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_for_signal, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Associate thread and signal the eventlink */
kr = mach_eventlink_associate(port_pair[1], self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate for object 2");
kr = mach_eventlink_signal(port_pair[1], 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal for object 2");
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 11: Test eventlink wait_signal.
*
* Create an eventlink object, associate threads and test wait_signal.
*/
T_DECL(test_eventlink_wait_signal, "eventlink wait_signal", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_then_signal, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Associate thread and wait_signal the eventlink */
kr = mach_eventlink_associate(port_pair[1], self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate for object 2");
/* Wait on the eventlink with timeout */
kr = mach_eventlink_signal_wait_until(port_pair[1], &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_signal_wait_until returned correct count value");
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 12: Test eventlink wait_signal with no wait.
*
* Create an eventlink object, associate threads and test wait_signal with no wait.
*/
T_DECL(test_eventlink_wait_signal_no_wait, "eventlink wait_signal with no wait", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_then_wait_signal_with_no_wait, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Associate thread and wait_signal the eventlink */
kr = mach_eventlink_associate(port_pair[1], self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate for object 2");
/* Wait on the eventlink with timeout */
kr = mach_eventlink_signal_wait_until(port_pair[1], &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_signal_wait_until returned correct count value");
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 13: Test eventlink wait_signal with prepost.
*
* Create an eventlink object, associate threads and test wait_signal with prepost.
*/
T_DECL(test_eventlink_wait_signal_prepost, "eventlink wait_signal with prepost", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_then_wait_signal_with_prepost, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Associate thread and wait_signal the eventlink */
kr = mach_eventlink_associate(port_pair[1], self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate for object 2");
/* Wait on the eventlink with timeout */
kr = mach_eventlink_signal_wait_until(port_pair[1], &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_signal_wait_until returned correct count value");
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 14: Test eventlink wait_signal with associate on wait option.
*
* Create an eventlink object, set associate on wait on one side and test wait_signal.
*/
T_DECL(test_eventlink_wait_signal_associate_on_wait, "eventlink wait_signal associate on wait", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
uint64_t count = 0;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_then_signal, (void *)(uintptr_t)port_pair[0]);
sleep(5);
/* Set associate on wait and wait_signal the eventlink */
kr = mach_eventlink_associate(port_pair[1], MACH_PORT_NULL, 0, 0, 0, 0, MELA_OPTION_ASSOCIATE_ON_WAIT);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate with associate on wait for object 2");
/* Wait on the eventlink with timeout */
kr = mach_eventlink_signal_wait_until(port_pair[1], &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_signal_wait_until");
T_EXPECT_EQ(count, (uint64_t)1, "mach_eventlink_signal_wait_until returned correct count value");
/* Remove associate on wait option */
kr = mach_eventlink_disassociate(port_pair[1], MELD_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_disassociate");
/* Wait on the eventlink with timeout */
kr = mach_eventlink_signal_wait_until(port_pair[1], &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_EXPECT_MACH_ERROR(kr, KERN_INVALID_ARGUMENT, "mach_eventlink_wait_until returned expected error");
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
/*
* Test 15: Test eventlink wait_signal_loop.
*
* Create an eventlink object, associate threads and test wait_signal in a loop.
*/
T_DECL(test_eventlink_wait_signal_loop, "eventlink wait_signal in loop", T_META_ASROOT(YES), T_META_TAG_VM_PREFERRED)
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
int i;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_then_signal_loop, (void *)(uintptr_t)port_pair[0]);
/* Associate thread and wait_signal the eventlink */
kr = mach_eventlink_associate(port_pair[1], self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate for object 2");
for (i = 0; i < g_loop_iterations; i++) {
/* Wait on the eventlink with timeout */
kr = mach_eventlink_signal_wait_until(port_pair[1], &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "main thread: mach_eventlink_signal_wait_until");
T_QUIET; T_EXPECT_EQ(count, (uint64_t)(i + 1), "main thread: mach_eventlink_signal_wait_until returned correct count value");
}
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}
static uint64_t
nanos_to_abs(uint64_t nanos)
{
static mach_timebase_info_data_t timebase_info;
mach_timebase_info(&timebase_info);
return nanos * timebase_info.denom / timebase_info.numer;
}
static const uint64_t DEFAULT_INTERVAL_NS = 15000000; // 15 ms
static void
set_realtime(pthread_t thread, uint64_t interval_nanos)
{
kern_return_t kr;
thread_time_constraint_policy_data_t pol;
mach_port_t target_thread = pthread_mach_thread_np(thread);
T_QUIET; T_ASSERT_GT(target_thread, 0, "pthread_mach_thread_np");
/* 1s 100ms 10ms */
pol.period = (uint32_t)nanos_to_abs(interval_nanos);
pol.constraint = (uint32_t)nanos_to_abs(interval_nanos);
pol.computation = (uint32_t)nanos_to_abs(interval_nanos - 1000000); // 1 ms of leeway
pol.preemptible = 0; /* Ignored by OS */
kr = thread_policy_set(target_thread, THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &pol,
THREAD_TIME_CONSTRAINT_POLICY_COUNT);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_policy_set(THREAD_TIME_CONSTRAINT_POLICY)");
}
static _Atomic bool suspend_resume_thread_stop = false;
static void *
test_suspend_resume_thread(void *arg)
{
uint64_t count = 0;
mach_port_t suspend_resume_other_thread_port = (mach_port_t) (uintptr_t)arg;
kern_return_t kr1 = KERN_SUCCESS, kr2 = KERN_SUCCESS;
while (!os_atomic_load(&suspend_resume_thread_stop, relaxed) && kr1 == KERN_SUCCESS && kr2 == KERN_SUCCESS) {
kr1 = thread_suspend(suspend_resume_other_thread_port);
kr2 = thread_resume(suspend_resume_other_thread_port);
count++;
}
T_ASSERT_MACH_SUCCESS(kr1, "thread_suspend #%lld", count);
T_ASSERT_MACH_SUCCESS(kr2, "thread_resume #%lld", count);
return NULL;
}
/*
* Test 16: Test suspension of a thread in the middle of a wait-signal operation
* rdar://120761588 rdar://123887338
*/
T_DECL(test_eventlink_wait_signal_suspend_loop, "eventlink wait_signal + thread_suspend/resume in loop", T_META_ASROOT(YES))
{
kern_return_t kr;
mach_port_t port_pair[2];
pthread_t pthread, suspend_thread;
mach_port_t self = mach_thread_self();
uint64_t count = 0;
int i;
/* Create an eventlink and associate threads to it */
kr = test_eventlink_create(port_pair);
if (kr != KERN_SUCCESS) {
return;
}
pthread = thread_create_for_test(test_eventlink_wait_then_signal_loop, (void *)(uintptr_t)port_pair[0]);
mach_port_t suspend_resume_other_thread_port = pthread_mach_thread_np(pthread);
/*
* Threads must be RT to get direct handoff mode.
*/
set_realtime(pthread_self(), DEFAULT_INTERVAL_NS);
set_realtime(pthread, DEFAULT_INTERVAL_NS);
suspend_thread = thread_create_for_test(test_suspend_resume_thread, (void *)(uintptr_t)suspend_resume_other_thread_port);
/* Associate thread and wait_signal the eventlink */
kr = mach_eventlink_associate(port_pair[1], self, 0, 0, 0, 0, MELA_OPTION_NONE);
T_ASSERT_MACH_SUCCESS(kr, "mach_eventlink_associate for object 2");
for (i = 0; i < g_loop_iterations; i++) {
/* Wait on the eventlink with timeout */
kr = mach_eventlink_signal_wait_until(port_pair[1], &count, 0, MELSW_OPTION_NONE,
KERN_CLOCK_MACH_ABSOLUTE_TIME, 0);
T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "main thread: mach_eventlink_signal_wait_until");
T_QUIET; T_EXPECT_EQ(count, (uint64_t)(i + 1), "main thread: mach_eventlink_signal_wait_until returned correct count value");
}
os_atomic_store(&suspend_resume_thread_stop, true, relaxed);
pthread_join(suspend_thread, NULL);
pthread_join(pthread, NULL);
mach_port_deallocate(mach_task_self(), port_pair[0]);
mach_port_deallocate(mach_task_self(), port_pair[1]);
}