+ sleep(16);
+ return;
+}
+
+/*
+ * Test 5: test if a chain of user-space turnstile primitives interleaved by kernel primitives works correctly.
+ */
+static void
+test5(void)
+{
+ pthread_t threads[5] = {};
+ struct thread_data data[5] = {};
+
+ T_LOG("Test 5: test if a chain of user-space turnstile primitives interleaved by kernel primitives works correctly");
+
+ /*
+ * Chain: t4->ud->t3->kh->t2->uh->t1->kd->t0
+ * ud and uh (user space turnstiles) will push base pri and sched pri
+ * kd and kh (kernel space turnstiles) will push sched pri
+ * uh is the breaking point of the chain for sched pri
+ */
+
+ /* Create a thread at priority 4 and take SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT lock */
+ data[0].pri_to_set = 4;
+ data[0].lock1 = SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT; /* this should be not locked */
+ data[0].lock2 = NULL;
+ data[0].sleep = 10; /* long sleep, nothing is blocking this thread */
+ data[0].sched_pri_to_check = 41;
+ data[0].base_pri_to_check = 4;
+ pthread_create(&threads[0], NULL, chain_locking, (void *)&data[0]);
+ sleep(2); /* give the thread time to acquire the lock */
+
+ /* Create a thread at priority 31 and take SYSCTL_TURNSTILE_TEST_USER_HASHTABLE lock followed by SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT */
+ data[1].pri_to_set = 31;
+ data[1].lock1 = SYSCTL_TURNSTILE_TEST_USER_HASHTABLE; /* this should be not locked */
+ data[1].lock2 = SYSCTL_TURNSTILE_TEST_KERNEL_DEFAULT; /* this should be locked */
+ data[1].sleep = 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
+ data[1].sched_pri_to_check = 41;
+ data[1].base_pri_to_check = 41;
+ pthread_create(&threads[1], NULL, chain_locking, (void *)&data[1]);
+ sleep(2); /* give the thread time to acquire the lock */
+
+ /* Create a thread at priority 41 and take SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE lock followed by SYSCTL_TURNSTILE_TEST_USER_HASHTABLE */
+ data[2].pri_to_set = 41;
+ data[2].lock1 = SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE; /* this should be not locked */
+ data[2].lock2 = SYSCTL_TURNSTILE_TEST_USER_HASHTABLE; /* this should be locked */
+ data[2].sleep = 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
+ data[2].sched_pri_to_check = 60;
+ data[2].base_pri_to_check = 41;
+ pthread_create(&threads[2], NULL, chain_locking, (void *)&data[2]);
+ sleep(2); /* give the thread time to acquire the lock */
+
+ /* Create a thread at priority 47 and take SYSCTL_TURNSTILE_TEST_USER_DEFAULT lock followed by SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE */
+ data[3].pri_to_set = 47;
+ data[3].lock1 = SYSCTL_TURNSTILE_TEST_USER_DEFAULT; /* this should be not locked */
+ data[3].lock2 = SYSCTL_TURNSTILE_TEST_KERNEL_HASHTABLE; /* this should be locked */
+ data[3].sleep = 0; /* no need to sleep, everything should be pushing by the time it acquires the lock */
+ data[3].sched_pri_to_check = 60;
+ data[3].base_pri_to_check = 60;
+ pthread_create(&threads[3], NULL, chain_locking, (void *)&data[3]);
+ sleep(2); /* give the thread time to acquire the lock */
+
+ /* Create a thread at priority 60 and take SYSCTL_TURNSTILE_TEST_USER_DEFAULT */
+ data[4].pri_to_set = 60;
+ data[4].lock1 = SYSCTL_TURNSTILE_TEST_USER_DEFAULT; /* this should be locked */
+ data[4].lock2 = NULL;
+ data[4].sleep = 0; /* no need to sleep, nothing should be pushing by the time it acquires the lock */
+ data[4].sched_pri_to_check = 60; /* this is its own priority */
+ data[4].base_pri_to_check = 60;
+ pthread_create(&threads[4], NULL, chain_locking, (void *)&data[4]);
+
+ sleep(16);
+ return;
+}
+
+T_DECL(turnstile_test, "Turnstile test", T_META_ASROOT(YES))
+{
+ test1(SYSCTL_TURNSTILE_TEST_USER_DEFAULT);
+ test2(SYSCTL_TURNSTILE_TEST_USER_DEFAULT);
+ test3(SYSCTL_TURNSTILE_TEST_USER_DEFAULT);
+
+ test1(SYSCTL_TURNSTILE_TEST_USER_HASHTABLE);
+ test2(SYSCTL_TURNSTILE_TEST_USER_HASHTABLE);
+ test3(SYSCTL_TURNSTILE_TEST_USER_HASHTABLE);
+
+ /*
+ * rdar://problem/46302128
+ * These tests are using a sysctl to lock a dummy kernel resource that uses turnstile.
+ * However a thread holding a kernel push from turnstile should never return in
+ * userspace, and rdar://problem/24194397 adds an assert for it.
+ */
+ //test4();
+ //test5();