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