Serenity Operating System
at master 403 lines 10 kB view raw
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}