xref: /xnu-10002.61.3/tests/ulock.c (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
1 #include <darwintest.h>
2 
3 #include <stdatomic.h>
4 
5 #include <unistd.h>
6 #include <pthread.h>
7 #include <sys/ulock.h>
8 
9 #include <os/tsd.h>
10 
11 #ifndef __TSD_MACH_THREAD_SELF
12 #define __TSD_MACH_THREAD_SELF 3
13 #endif
14 
15 #pragma clang diagnostic push
16 #pragma clang diagnostic ignored "-Wbad-function-cast"
17 __inline static mach_port_name_t
_os_get_self(void)18 _os_get_self(void)
19 {
20 	mach_port_name_t self = (mach_port_name_t)_os_tsd_get_direct(__TSD_MACH_THREAD_SELF);
21 	return self;
22 }
23 #pragma clang diagnostic pop
24 
25 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
26 
27 #pragma mark ulock_non_owner_wake
28 
29 static _Atomic uint32_t test_ulock;
30 
31 static void *
test_waiter(void * arg __unused)32 test_waiter(void *arg __unused)
33 {
34 	for (;;) {
35 		uint32_t test_ulock_owner = atomic_load_explicit(&test_ulock,
36 		    memory_order_relaxed);
37 		int rc = __ulock_wait(UL_UNFAIR_LOCK | ULF_NO_ERRNO, &test_ulock,
38 		    test_ulock_owner, 0);
39 		if (rc == -EINTR || rc == -EFAULT) {
40 			continue;
41 		}
42 		T_ASSERT_GE(rc, 0, "__ulock_wait");
43 		break;
44 	}
45 
46 	T_PASS("Waiter woke");
47 	T_END;
48 
49 	return NULL;
50 }
51 
52 static void *
test_waker(void * arg __unused)53 test_waker(void *arg __unused)
54 {
55 	for (;;) {
56 		int rc = __ulock_wake(UL_UNFAIR_LOCK | ULF_NO_ERRNO | ULF_WAKE_ALLOW_NON_OWNER,
57 		    &test_ulock, 0);
58 		if (rc == -EINTR) {
59 			continue;
60 		}
61 		T_ASSERT_EQ(rc, 0, "__ulock_wake");
62 		break;
63 	}
64 	return NULL;
65 }
66 
67 T_DECL(ulock_non_owner_wake, "ulock_wake respects non-owner wakes",
68     T_META_CHECK_LEAKS(false))
69 {
70 	pthread_t waiter, waker;
71 
72 	atomic_store_explicit(&test_ulock, _os_get_self() & ~0x3u, memory_order_relaxed);
73 
74 	T_ASSERT_POSIX_ZERO(pthread_create(&waiter, NULL, test_waiter, NULL), "create waiter");
75 
76 	// wait for the waiter to reach the kernel
77 	for (;;) {
78 		int kernel_ulocks = __ulock_wake(UL_DEBUG_HASH_DUMP_PID, NULL, 0);
79 		T_QUIET; T_ASSERT_NE(kernel_ulocks, -1, "UL_DEBUG_HASH_DUMP_PID");
80 
81 		if (kernel_ulocks == 1) {
82 			T_LOG("waiter is now waiting");
83 			break;
84 		}
85 		usleep(100);
86 	}
87 
88 	T_ASSERT_POSIX_ZERO(pthread_create(&waker, NULL, test_waker, NULL), "create waker");
89 
90 	// won't ever actually join
91 	pthread_join(waiter, NULL);
92 }
93