Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibCore/ArgsParser.h>
8#include <LibMain/Main.h>
9#include <errno.h>
10#include <pthread.h>
11#include <signal.h>
12#include <stdio.h>
13#include <string.h>
14#include <sys/mman.h>
15#include <unistd.h>
16
17static int mutex_test();
18static int detached_test();
19static int priority_test();
20static int stack_size_test();
21static int staying_alive_test();
22static int set_stack_test();
23static int kill_test();
24
25ErrorOr<int> serenity_main(Main::Arguments arguments)
26{
27 StringView test_name = "n"sv;
28
29 Core::ArgsParser args_parser;
30 args_parser.set_general_help(
31 "Exercise error-handling and edge-case paths of the execution environment "
32 "(i.e., Kernel or UE) by doing unusual thread-related things.");
33 args_parser.add_positional_argument(test_name, "Test to run (m = mutex, d = detached, p = priority, s = stack size, t = simple thread test, x = set stack, k = kill, nothing = join race)", "test-name", Core::ArgsParser::Required::No);
34 args_parser.parse(arguments);
35
36 if (test_name[0] == 'm')
37 return mutex_test();
38 if (test_name[0] == 'd')
39 return detached_test();
40 if (test_name[0] == 'p')
41 return priority_test();
42 if (test_name[0] == 's')
43 return stack_size_test();
44 if (test_name[0] == 't')
45 return staying_alive_test();
46 if (test_name[0] == 'x')
47 return set_stack_test();
48 if (test_name[0] == 'k')
49 return kill_test();
50 if (test_name[0] != 'n') {
51 args_parser.print_usage(stdout, arguments.strings[0]);
52 return 1;
53 }
54
55 outln("Hello from the first thread!");
56 pthread_t thread_id;
57 int rc = pthread_create(
58 &thread_id, nullptr, [](void*) -> void* {
59 outln("Hi there, from the second thread!");
60 pthread_exit((void*)0xDEADBEEF);
61 },
62 nullptr);
63 if (rc < 0) {
64 perror("pthread_create");
65 return 1;
66 }
67 void* retval;
68 rc = pthread_join(thread_id, &retval);
69 if (rc < 0) {
70 perror("pthread_join");
71 return 1;
72 }
73 outln("Okay, joined and got retval={}", retval);
74 return 0;
75}
76
77static pthread_mutex_t mutex;
78
79int mutex_test()
80{
81 int rc = pthread_mutex_init(&mutex, nullptr);
82 if (rc < 0) {
83 perror("pthread_mutex_init");
84 return 1;
85 }
86 pthread_t thread_id;
87 rc = pthread_create(
88 &thread_id, nullptr, [](void*) -> void* {
89 outln("I'm the secondary thread :^)");
90 for (;;) {
91 pthread_mutex_lock(&mutex);
92 outln("Second thread stole mutex");
93 sleep(1);
94 outln("Second thread giving back mutex");
95 pthread_mutex_unlock(&mutex);
96 sleep(1);
97 }
98 },
99 nullptr);
100 if (rc < 0) {
101 perror("pthread_create");
102 return 1;
103 }
104 for (;;) {
105 pthread_mutex_lock(&mutex);
106 outln("Obnoxious spam!");
107 pthread_mutex_unlock(&mutex);
108 usleep(10000);
109 }
110}
111
112int detached_test()
113{
114 pthread_attr_t attributes;
115 int rc = pthread_attr_init(&attributes);
116 if (rc != 0) {
117 outln("pthread_attr_init: {}", strerror(rc));
118 return 1;
119 }
120
121 int detach_state = 99; // clearly invalid
122 rc = pthread_attr_getdetachstate(&attributes, &detach_state);
123 if (rc != 0) {
124 outln("pthread_attr_getdetachstate: {}", strerror(rc));
125 return 2;
126 }
127 outln("Default detach state: {}", detach_state == PTHREAD_CREATE_JOINABLE ? "joinable" : "detached");
128
129 detach_state = PTHREAD_CREATE_DETACHED;
130 rc = pthread_attr_setdetachstate(&attributes, detach_state);
131 if (rc != 0) {
132 outln("pthread_attr_setdetachstate: {}", strerror(rc));
133 return 3;
134 }
135 outln("Set detach state on new thread to detached");
136
137 pthread_t thread_id;
138 rc = pthread_create(
139 &thread_id, &attributes, [](void*) -> void* {
140 outln("I'm the secondary thread :^)");
141 sleep(1);
142 pthread_exit((void*)0xDEADBEEF);
143 },
144 nullptr);
145 if (rc != 0) {
146 outln("pthread_create: {}", strerror(rc));
147 return 4;
148 }
149
150 void* ret_val;
151 rc = pthread_join(thread_id, &ret_val);
152 if (rc != 0 && rc != EINVAL) {
153 outln("pthread_join: {}", strerror(rc));
154 return 5;
155 }
156 if (rc != EINVAL) {
157 outln("Expected EINVAL! Thread was joinable?");
158 return 6;
159 }
160
161 sleep(2);
162 outln("Thread was created detached. I sure hope it exited on its own.");
163
164 rc = pthread_attr_destroy(&attributes);
165 if (rc != 0) {
166 outln("pthread_attr_destroy: {}", strerror(rc));
167 return 7;
168 }
169
170 return 0;
171}
172
173int priority_test()
174{
175 pthread_attr_t attributes;
176 int rc = pthread_attr_init(&attributes);
177 if (rc != 0) {
178 outln("pthread_attr_init: {}", strerror(rc));
179 return 1;
180 }
181
182 struct sched_param sched_params;
183 rc = pthread_attr_getschedparam(&attributes, &sched_params);
184 if (rc != 0) {
185 outln("pthread_attr_getschedparam: {}", strerror(rc));
186 return 2;
187 }
188 outln("Default priority: {}", sched_params.sched_priority);
189
190 sched_params.sched_priority = 3;
191 rc = pthread_attr_setschedparam(&attributes, &sched_params);
192 if (rc != 0) {
193 outln("pthread_attr_setschedparam: {}", strerror(rc));
194 return 3;
195 }
196 outln("Set thread priority to 3");
197
198 pthread_t thread_id;
199 rc = pthread_create(
200 &thread_id, &attributes, [](void*) -> void* {
201 outln("I'm the secondary thread :^)");
202 sleep(1);
203 pthread_exit((void*)0xDEADBEEF);
204 },
205 nullptr);
206 if (rc < 0) {
207 perror("pthread_create");
208 return 4;
209 }
210
211 rc = pthread_join(thread_id, nullptr);
212 if (rc < 0) {
213 perror("pthread_join");
214 return 5;
215 }
216
217 rc = pthread_attr_destroy(&attributes);
218 if (rc != 0) {
219 outln("pthread_attr_destroy: {}", strerror(rc));
220 return 6;
221 }
222
223 return 0;
224}
225
226int stack_size_test()
227{
228 pthread_attr_t attributes;
229 int rc = pthread_attr_init(&attributes);
230 if (rc != 0) {
231 outln("pthread_attr_init: {}", strerror(rc));
232 return 1;
233 }
234
235 size_t stack_size;
236 rc = pthread_attr_getstacksize(&attributes, &stack_size);
237 if (rc != 0) {
238 outln("pthread_attr_getstacksize: {}", strerror(rc));
239 return 2;
240 }
241 outln("Default stack size: {}", stack_size);
242
243 stack_size = 8 * 1024 * 1024;
244 rc = pthread_attr_setstacksize(&attributes, stack_size);
245 if (rc != 0) {
246 outln("pthread_attr_setstacksize: {}", strerror(rc));
247 return 3;
248 }
249 outln("Set thread stack size to 8 MiB");
250
251 pthread_t thread_id;
252 rc = pthread_create(
253 &thread_id, &attributes, [](void*) -> void* {
254 outln("I'm the secondary thread :^)");
255 sleep(1);
256 pthread_exit((void*)0xDEADBEEF);
257 },
258 nullptr);
259 if (rc < 0) {
260 perror("pthread_create");
261 return 4;
262 }
263
264 rc = pthread_join(thread_id, nullptr);
265 if (rc < 0) {
266 perror("pthread_join");
267 return 5;
268 }
269
270 rc = pthread_attr_destroy(&attributes);
271 if (rc != 0) {
272 outln("pthread_attr_destroy: {}", strerror(rc));
273 return 6;
274 }
275
276 return 0;
277}
278
279int staying_alive_test()
280{
281 pthread_t thread_id;
282 int rc = pthread_create(
283 &thread_id, nullptr, [](void*) -> void* {
284 outln("I'm the secondary thread :^)");
285 sleep(20);
286 outln("Secondary thread is still alive");
287 sleep(3520);
288 outln("Secondary thread exiting");
289 pthread_exit((void*)0xDEADBEEF);
290 },
291 nullptr);
292 if (rc < 0) {
293 perror("pthread_create");
294 return 1;
295 }
296
297 sleep(1);
298 outln("I'm the main thread :^)");
299 sleep(3600);
300
301 outln("Main thread exiting");
302 return 0;
303}
304
305int set_stack_test()
306{
307 pthread_attr_t attributes;
308 int rc = pthread_attr_init(&attributes);
309 if (rc < 0) {
310 outln("pthread_attr_init: {}", strerror(rc));
311 return 1;
312 }
313
314 size_t stack_size = 8 * 1024 * 1024;
315 void* stack_addr = mmap_with_name(nullptr, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 0, 0, "Cool stack");
316
317 if (!stack_addr) {
318 perror("mmap_with_name");
319 return -1;
320 }
321
322 rc = pthread_attr_setstack(&attributes, stack_addr, stack_size);
323 if (rc != 0) {
324 outln("pthread_attr_setstack: {}", strerror(rc));
325 return 2;
326 }
327 outln("Set thread stack to {:p}, size {}", stack_addr, stack_size);
328
329 size_t stack_size_verify;
330 void* stack_addr_verify;
331
332 rc = pthread_attr_getstack(&attributes, &stack_addr_verify, &stack_size_verify);
333 if (rc != 0) {
334 outln("pthread_attr_getstack: {}", strerror(rc));
335 return 3;
336 }
337
338 if (stack_addr != stack_addr_verify || stack_size != stack_size_verify) {
339 outln("Stack address and size don't match! addr: {:p} {:p}, size: {} {}", stack_addr, stack_addr_verify, stack_size, stack_size_verify);
340 return 4;
341 }
342
343 pthread_t thread_id;
344 rc = pthread_create(
345 &thread_id, &attributes, [](void*) -> void* {
346 outln("I'm the secondary thread :^)");
347 sleep(1);
348 pthread_exit((void*)0xDEADBEEF);
349 },
350 nullptr);
351 if (rc < 0) {
352 perror("pthread_create");
353 return 5;
354 }
355
356 rc = pthread_join(thread_id, nullptr);
357 if (rc < 0) {
358 perror("pthread_join");
359 return 6;
360 }
361
362 rc = pthread_attr_destroy(&attributes);
363 if (rc != 0) {
364 outln("pthread_attr_destroy: {}", strerror(rc));
365 return 7;
366 }
367
368 return 0;
369}
370
371int kill_test()
372{
373 pthread_t thread_id;
374 int rc = pthread_create(
375 &thread_id, nullptr, [](void*) -> void* {
376 outln("I'm the secondary thread :^)");
377 sleep(100);
378 outln("Secondary thread is still alive :^(");
379 pthread_exit((void*)0xDEADBEEF);
380 },
381 nullptr);
382 if (rc < 0) {
383 perror("pthread_create");
384 return 1;
385 }
386
387 int result = 0;
388
389 sleep(1);
390 outln("I'm the main thread :^)");
391 if (pthread_kill(thread_id, 0) != 0) {
392 perror("pthread_kill");
393 result = 1;
394 }
395
396 if (pthread_kill(thread_id, SIGKILL) != 0) {
397 perror("pthread_kill(SIGKILL)");
398 result = 1;
399 }
400
401 outln("Main thread exiting");
402 return result;
403}