1*e3723e1fSApple OSS Distributions #include <darwintest.h>
2*e3723e1fSApple OSS Distributions
3*e3723e1fSApple OSS Distributions #include <stdatomic.h>
4*e3723e1fSApple OSS Distributions
5*e3723e1fSApple OSS Distributions #include <unistd.h>
6*e3723e1fSApple OSS Distributions #include <pthread.h>
7*e3723e1fSApple OSS Distributions #include <sys/ulock.h>
8*e3723e1fSApple OSS Distributions
9*e3723e1fSApple OSS Distributions #include <os/tsd.h>
10*e3723e1fSApple OSS Distributions
11*e3723e1fSApple OSS Distributions #ifndef __TSD_MACH_THREAD_SELF
12*e3723e1fSApple OSS Distributions #define __TSD_MACH_THREAD_SELF 3
13*e3723e1fSApple OSS Distributions #endif
14*e3723e1fSApple OSS Distributions
15*e3723e1fSApple OSS Distributions #pragma clang diagnostic push
16*e3723e1fSApple OSS Distributions #pragma clang diagnostic ignored "-Wbad-function-cast"
17*e3723e1fSApple OSS Distributions __inline static mach_port_name_t
_os_get_self(void)18*e3723e1fSApple OSS Distributions _os_get_self(void)
19*e3723e1fSApple OSS Distributions {
20*e3723e1fSApple OSS Distributions mach_port_name_t self = (mach_port_name_t)_os_tsd_get_direct(__TSD_MACH_THREAD_SELF);
21*e3723e1fSApple OSS Distributions return self;
22*e3723e1fSApple OSS Distributions }
23*e3723e1fSApple OSS Distributions #pragma clang diagnostic pop
24*e3723e1fSApple OSS Distributions
25*e3723e1fSApple OSS Distributions T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
26*e3723e1fSApple OSS Distributions
27*e3723e1fSApple OSS Distributions #pragma mark ulock_non_owner_wake
28*e3723e1fSApple OSS Distributions
29*e3723e1fSApple OSS Distributions static _Atomic uint32_t test_ulock;
30*e3723e1fSApple OSS Distributions
31*e3723e1fSApple OSS Distributions static void *
test_waiter(void * arg __unused)32*e3723e1fSApple OSS Distributions test_waiter(void *arg __unused)
33*e3723e1fSApple OSS Distributions {
34*e3723e1fSApple OSS Distributions for (;;) {
35*e3723e1fSApple OSS Distributions uint32_t test_ulock_owner = atomic_load_explicit(&test_ulock,
36*e3723e1fSApple OSS Distributions memory_order_relaxed);
37*e3723e1fSApple OSS Distributions int rc = __ulock_wait(UL_UNFAIR_LOCK | ULF_NO_ERRNO, &test_ulock,
38*e3723e1fSApple OSS Distributions test_ulock_owner, 0);
39*e3723e1fSApple OSS Distributions if (rc == -EINTR || rc == -EFAULT) {
40*e3723e1fSApple OSS Distributions continue;
41*e3723e1fSApple OSS Distributions }
42*e3723e1fSApple OSS Distributions T_ASSERT_GE(rc, 0, "__ulock_wait");
43*e3723e1fSApple OSS Distributions break;
44*e3723e1fSApple OSS Distributions }
45*e3723e1fSApple OSS Distributions
46*e3723e1fSApple OSS Distributions T_PASS("Waiter woke");
47*e3723e1fSApple OSS Distributions T_END;
48*e3723e1fSApple OSS Distributions
49*e3723e1fSApple OSS Distributions return NULL;
50*e3723e1fSApple OSS Distributions }
51*e3723e1fSApple OSS Distributions
52*e3723e1fSApple OSS Distributions static mach_timebase_info_data_t timebase_info;
53*e3723e1fSApple OSS Distributions
54*e3723e1fSApple OSS Distributions static uint64_t
nanos_to_abs(uint64_t nanos)55*e3723e1fSApple OSS Distributions nanos_to_abs(uint64_t nanos)
56*e3723e1fSApple OSS Distributions {
57*e3723e1fSApple OSS Distributions return nanos * timebase_info.denom / timebase_info.numer;
58*e3723e1fSApple OSS Distributions }
59*e3723e1fSApple OSS Distributions
60*e3723e1fSApple OSS Distributions static void *
test_waiter_with_timeout(void * arg)61*e3723e1fSApple OSS Distributions test_waiter_with_timeout(void *arg)
62*e3723e1fSApple OSS Distributions {
63*e3723e1fSApple OSS Distributions uint64_t deadline = (uint64_t) arg;
64*e3723e1fSApple OSS Distributions
65*e3723e1fSApple OSS Distributions for (;;) {
66*e3723e1fSApple OSS Distributions uint32_t test_ulock_owner = atomic_load_explicit(&test_ulock,
67*e3723e1fSApple OSS Distributions memory_order_relaxed);
68*e3723e1fSApple OSS Distributions int rc = __ulock_wait2(UL_UNFAIR_LOCK | ULF_NO_ERRNO | ULF_DEADLINE, &test_ulock,
69*e3723e1fSApple OSS Distributions test_ulock_owner, deadline, 0);
70*e3723e1fSApple OSS Distributions if (rc == -EINTR || rc == -EFAULT) {
71*e3723e1fSApple OSS Distributions continue;
72*e3723e1fSApple OSS Distributions }
73*e3723e1fSApple OSS Distributions T_ASSERT_EQ(rc, -ETIMEDOUT, "__ulock_wait");
74*e3723e1fSApple OSS Distributions break;
75*e3723e1fSApple OSS Distributions }
76*e3723e1fSApple OSS Distributions
77*e3723e1fSApple OSS Distributions T_ASSERT_GE(mach_absolute_time(), deadline, "Current time is past the deadline specified");
78*e3723e1fSApple OSS Distributions
79*e3723e1fSApple OSS Distributions T_PASS("Waiter woke");
80*e3723e1fSApple OSS Distributions
81*e3723e1fSApple OSS Distributions return NULL;
82*e3723e1fSApple OSS Distributions }
83*e3723e1fSApple OSS Distributions
84*e3723e1fSApple OSS Distributions static void *
test_waker(void * arg __unused)85*e3723e1fSApple OSS Distributions test_waker(void *arg __unused)
86*e3723e1fSApple OSS Distributions {
87*e3723e1fSApple OSS Distributions for (;;) {
88*e3723e1fSApple OSS Distributions int rc = __ulock_wake(UL_UNFAIR_LOCK | ULF_NO_ERRNO | ULF_WAKE_ALLOW_NON_OWNER,
89*e3723e1fSApple OSS Distributions &test_ulock, 0);
90*e3723e1fSApple OSS Distributions if (rc == -EINTR) {
91*e3723e1fSApple OSS Distributions continue;
92*e3723e1fSApple OSS Distributions }
93*e3723e1fSApple OSS Distributions T_ASSERT_EQ(rc, 0, "__ulock_wake");
94*e3723e1fSApple OSS Distributions break;
95*e3723e1fSApple OSS Distributions }
96*e3723e1fSApple OSS Distributions return NULL;
97*e3723e1fSApple OSS Distributions }
98*e3723e1fSApple OSS Distributions
99*e3723e1fSApple OSS Distributions T_DECL(ulock_non_owner_wake, "ulock_wake respects non-owner wakes",
100*e3723e1fSApple OSS Distributions T_META_CHECK_LEAKS(false), T_META_TAG_VM_PREFERRED)
101*e3723e1fSApple OSS Distributions {
102*e3723e1fSApple OSS Distributions pthread_t waiter, waker;
103*e3723e1fSApple OSS Distributions
104*e3723e1fSApple OSS Distributions atomic_store_explicit(&test_ulock, _os_get_self() & ~0x3u, memory_order_relaxed);
105*e3723e1fSApple OSS Distributions
106*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_ZERO(pthread_create(&waiter, NULL, test_waiter, NULL), "create waiter");
107*e3723e1fSApple OSS Distributions
108*e3723e1fSApple OSS Distributions // wait for the waiter to reach the kernel
109*e3723e1fSApple OSS Distributions for (;;) {
110*e3723e1fSApple OSS Distributions int kernel_ulocks = __ulock_wake(UL_DEBUG_HASH_DUMP_PID, NULL, 0);
111*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_NE(kernel_ulocks, -1, "UL_DEBUG_HASH_DUMP_PID");
112*e3723e1fSApple OSS Distributions
113*e3723e1fSApple OSS Distributions if (kernel_ulocks == 1) {
114*e3723e1fSApple OSS Distributions T_LOG("waiter is now waiting");
115*e3723e1fSApple OSS Distributions break;
116*e3723e1fSApple OSS Distributions }
117*e3723e1fSApple OSS Distributions usleep(100);
118*e3723e1fSApple OSS Distributions }
119*e3723e1fSApple OSS Distributions
120*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_ZERO(pthread_create(&waker, NULL, test_waker, NULL), "create waker");
121*e3723e1fSApple OSS Distributions
122*e3723e1fSApple OSS Distributions // won't ever actually join
123*e3723e1fSApple OSS Distributions pthread_join(waiter, NULL);
124*e3723e1fSApple OSS Distributions }
125*e3723e1fSApple OSS Distributions
126*e3723e1fSApple OSS Distributions T_DECL(ulock_wait_deadline, "ulock_wait2 with deadline", T_META_CHECK_LEAKS(false), T_META_TAG_VM_PREFERRED)
127*e3723e1fSApple OSS Distributions {
128*e3723e1fSApple OSS Distributions kern_return_t kr = mach_timebase_info(&timebase_info);
129*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_timebase_info");
130*e3723e1fSApple OSS Distributions
131*e3723e1fSApple OSS Distributions // Take the lock as self
132*e3723e1fSApple OSS Distributions atomic_store_explicit(&test_ulock, _os_get_self() & ~0x3u, memory_order_relaxed);
133*e3723e1fSApple OSS Distributions
134*e3723e1fSApple OSS Distributions pthread_t waiter;
135*e3723e1fSApple OSS Distributions
136*e3723e1fSApple OSS Distributions // Deadline in the past
137*e3723e1fSApple OSS Distributions uint64_t deadline = mach_absolute_time() - nanos_to_abs(3 * NSEC_PER_SEC);
138*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_ZERO(pthread_create(&waiter, NULL, test_waiter_with_timeout, (void *) deadline), "create waiter");
139*e3723e1fSApple OSS Distributions
140*e3723e1fSApple OSS Distributions pthread_join(waiter, NULL);
141*e3723e1fSApple OSS Distributions
142*e3723e1fSApple OSS Distributions // Deadline in the future
143*e3723e1fSApple OSS Distributions deadline = mach_absolute_time() + nanos_to_abs(3 * NSEC_PER_SEC);
144*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_ZERO(pthread_create(&waiter, NULL, test_waiter_with_timeout, (void *) deadline), "create waiter");
145*e3723e1fSApple OSS Distributions
146*e3723e1fSApple OSS Distributions pthread_join(waiter, NULL);
147*e3723e1fSApple OSS Distributions T_END;
148*e3723e1fSApple OSS Distributions }
149