xref: /xnu-8019.80.24/tests/restart.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1 #include <mach/task.h>
2 #include <mach/mach.h>
3 #include <kern/restartable.h>
4 #include <stdbool.h>
5 #include <darwintest.h>
6 #include <pthread.h>
7 #include <unistd.h>
8 #include <signal.h>
9 
10 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
11 
12 extern task_restartable_range_t range;
13 extern void restartable_function(int *);
14 static int step = 0;
15 
16 #if defined(__x86_64__)
17 __asm__("    .align 4\n"
18 		"    .text\n"
19 		"    .private_extern _restartable_function\n"
20 		"_restartable_function:\n"
21 		// this should use $arg1 but I don't know intel calling conventions
22 		// so the argument to restartable_function() is actually ignored
23 		// as we know what it is anyway, and Intel PC-relative addressing,
24 		// unlike ARM, is pretty readable
25 		"    incl _step(%rip)\n"
26 		"1:\n"
27 		"    pause\n"
28 		"    jmp 1b\n"
29 		"LExit_restartable_function:\n"
30 		"    ret\n");
31 #elif defined(__arm64__)
32 __asm__("    .align 4\n"
33 		"    .text\n"
34 		"    .private_extern _restartable_function\n"
35 		"_restartable_function:\n"
36 		"    ldr    x11, [x0]\n"
37 		"    add    x11, x11, #1\n"
38 		"    str    x11, [x0]\n"
39 		"1:\n"
40 		"    b 1b\n"
41 		"LExit_restartable_function:\n"
42 		"    ret\n");
43 #elif defined(__arm__)
44 __asm__("    .align 4\n"
45 		"    .text\n"
46 		"    .thumb\n"
47 		"    .private_extern _restartable_function\n"
48 		"    .thumb_func\n"
49 		"_restartable_function:\n"
50 		"0:\n"
51 		"    ldr    r12, [r0]\n"
52 		"    add    r12, r12, #1\n"
53 		"    str    r12, [r0]\n"
54 		"1:\n"
55 		"    b 1b\n"
56 		"LExit_restartable_function:\n"
57 		"    bx lr\n");
58 #elif defined(__i386__)
59 #define SKIP_TEST 1
60 #else
61 #error Architecture unsupported
62 #endif
63 
64 #ifndef SKIP_TEST
65 __asm__("    .align 4\n"
66 		"    .data\n"
67 		"    .private_extern _range\n"
68 		"_range:\n"
69 #if __LP64__
70 		"    .quad _restartable_function\n"
71 #else
72 		"    .long _restartable_function\n"
73 		"    .long 0\n"
74 #endif
75 		"    .short LExit_restartable_function - _restartable_function\n"
76 		"    .short LExit_restartable_function - _restartable_function\n"
77 		"    .long 0\n");
78 #endif
79 
80 static void
noop_signal(int signo __unused)81 noop_signal(int signo __unused)
82 {
83 }
84 
85 static void *
task_restartable_ranges_thread(void * _ctx)86 task_restartable_ranges_thread(void *_ctx)
87 {
88 	int *stepp = _ctx;
89 	restartable_function(stepp); // increments step
90 	T_PASS("was successfully restarted\n");
91 	(*stepp)++;
92 	return NULL;
93 }
94 
95 static void
wait_for_step(int which)96 wait_for_step(int which)
97 {
98 	for (int i = 0; step != which && i < 10; i++) {
99 		usleep(100000);
100 	}
101 }
102 
103 T_DECL(task_restartable_ranges, "test task_restartable_ranges")
104 {
105 #ifdef SKIP_TEST
106 	T_SKIP("Not supported");
107 #else
108 	kern_return_t kr;
109 	pthread_t th;
110 	int rc;
111 
112 	signal(SIGUSR1, noop_signal);
113 
114 	kr = task_restartable_ranges_register(mach_task_self(), &range, 1);
115 	T_ASSERT_MACH_SUCCESS(kr, "task_restartable_ranges_register");
116 
117 	{
118 		rc = pthread_create(&th, NULL, &task_restartable_ranges_thread, &step);
119 		T_ASSERT_POSIX_SUCCESS(rc, "pthread_create");
120 
121 		wait_for_step(1);
122 		T_ASSERT_EQ(step, 1, "The thread started (sync)");
123 
124 		kr = task_restartable_ranges_synchronize(mach_task_self());
125 		T_ASSERT_MACH_SUCCESS(kr, "task_restartable_ranges_synchronize");
126 
127 		T_LOG("wait for the function to be restarted (sync)");
128 		wait_for_step(2);
129 		T_ASSERT_EQ(step, 2, "The thread exited (sync)");
130 		pthread_join(th, NULL);
131 	}
132 
133 	{
134 		rc = pthread_create(&th, NULL, &task_restartable_ranges_thread, &step);
135 		T_ASSERT_POSIX_SUCCESS(rc, "pthread_create");
136 
137 		wait_for_step(3);
138 		T_ASSERT_EQ(step, 3, "The thread started (signal)");
139 
140 		rc = pthread_kill(th, SIGUSR1);
141 		T_ASSERT_POSIX_SUCCESS(rc, "pthread_kill");
142 
143 		T_LOG("wait for the function to be restarted (signal)");
144 		wait_for_step(4);
145 		T_ASSERT_EQ(step, 4, "The thread exited (signal)");
146 		pthread_join(th, NULL);
147 	}
148 #endif
149 }
150