Changeset 29b8138 in mainline for uspace/app/rcutest/rcutest.c
- Timestamp:
- 2012-11-23T19:58:34Z (12 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- a879d73
- Parents:
- a2f42e5
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/app/rcutest/rcutest.c
ra2f42e5 r29b8138 42 42 #include <thread.h> 43 43 #include <assert.h> 44 44 #include <async.h> 45 45 #include <fibril.h> 46 #include <compiler/barrier.h> 46 47 47 48 #include <rcu.h> 48 49 49 50 50 static void invoke_rcu(void) 51 { 52 rcu_read_lock(); 51 #define USECS_PER_SEC (1000 * 1000) 52 #define USECS_PER_MS 1000 53 54 55 typedef struct test_desc { 56 /* Aggregate test that runs other tests already in the table test_desc. */ 57 bool aggregate; 58 enum { 59 T_OTHER, 60 T_SANITY, 61 T_STRESS 62 } type; 63 bool (*func)(void); 64 const char *name; 65 const char *desc; 66 } test_desc_t; 67 68 69 static bool run_all_tests(void); 70 static bool run_sanity_tests(void); 71 static bool run_stress_tests(void); 72 73 static bool wait_for_one_reader(void); 74 static bool basic_sanity_check(void); 75 static bool dont_wait_for_new_reader(void); 76 77 78 static test_desc_t test_desc[] = { 79 { 80 .aggregate = true, 81 .type = T_OTHER, 82 .func = run_all_tests, 83 .name = "*", 84 .desc = "Runs all tests.", 85 }, 86 { 87 .aggregate = true, 88 .type = T_SANITY, 89 .func = run_sanity_tests, 90 .name = "sanity-tests", 91 .desc = "Runs all RCU sanity tests.", 92 }, 93 { 94 .aggregate = true, 95 .type = T_STRESS, 96 .func = run_stress_tests, 97 .name = "stress-tests", 98 .desc = "Runs all RCU stress tests.", 99 }, 100 101 { 102 .aggregate = false, 103 .type = T_SANITY, 104 .func = basic_sanity_check, 105 .name = "basic-sanity", 106 .desc = "Locks/unlocks and syncs in a single fibril, no contention.", 107 }, 108 { 109 .aggregate = false, 110 .type = T_SANITY, 111 .func = wait_for_one_reader, 112 .name = "wait-for-one", 113 .desc = "Syncs with one 2 secs sleeping reader.", 114 }, 115 { 116 .aggregate = false, 117 .type = T_SANITY, 118 .func = dont_wait_for_new_reader, 119 .name = "ignore-new-r", 120 .desc = "Syncs with preexisting reader but ignores new reader.", 121 }, 122 { 123 .aggregate = false, 124 .type = T_OTHER, 125 .func = NULL, 126 .name = "(null)", 127 .desc = "", 128 }, 129 }; 130 131 static const size_t test_desc_cnt = sizeof(test_desc) / sizeof(test_desc[0]); 132 133 /*--------------------------------------------------------------------*/ 134 135 typedef int (*fibril_func_t)(void *); 136 137 static bool create_fibril(int (*func)(void*), void *arg) 138 { 139 fid_t fid = fibril_create(func, arg); 140 141 if (0 == fid) { 142 printf("Failed to create a fibril!\n"); 143 return false; 144 } 145 146 fibril_add_ready(fid); 147 return true; 148 } 149 150 /*--------------------------------------------------------------------*/ 151 152 static bool run_tests(bool (*include_filter)(test_desc_t *)) 153 { 154 size_t failed_cnt = 0; 155 size_t ok_cnt = 0; 156 157 for (size_t i = 0; i < test_desc_cnt; ++i) { 158 test_desc_t *t = &test_desc[i]; 159 160 if (t->func && !t->aggregate && include_filter(t)) { 161 printf("Running \'%s\'...\n", t->name); 162 bool ok = test_desc[i].func(); 163 164 if (ok) { 165 ++ok_cnt; 166 printf("Passed: \'%s\'\n", t->name); 167 } else { 168 ++failed_cnt; 169 printf("FAILED: \'%s\'\n", t->name); 170 } 171 } 172 } 173 174 printf("\n"); 175 176 printf("%zu tests passed\n", ok_cnt); 177 178 if (failed_cnt) { 179 printf("%zu tests failed\n", failed_cnt); 180 } 181 182 return 0 == failed_cnt; 183 } 184 185 /*--------------------------------------------------------------------*/ 186 187 static bool all_tests_include_filter(test_desc_t *desc) 188 { 189 return true; 190 } 191 192 /* Runs all available tests tests one-by-one. */ 193 static bool run_all_tests(void) 194 { 195 printf("Running all tests...\n"); 196 return run_tests(all_tests_include_filter); 197 } 198 199 /*--------------------------------------------------------------------*/ 200 201 static bool stress_tests_include_filter(test_desc_t *desc) 202 { 203 return desc->type == T_STRESS; 204 } 205 206 /* Runs all available stress tests one-by-one. */ 207 static bool run_stress_tests(void) 208 { 209 printf("Running stress tests...\n"); 210 return run_tests(stress_tests_include_filter); 211 } 212 213 /*--------------------------------------------------------------------*/ 214 215 static bool sanity_tests_include_filter(test_desc_t *desc) 216 { 217 return desc->type == T_SANITY; 218 } 219 220 /* Runs all available sanity tests one-by-one. */ 221 static bool run_sanity_tests(void) 222 { 223 printf("Running sanity tests...\n"); 224 return run_tests(sanity_tests_include_filter); 225 } 226 227 /*--------------------------------------------------------------------*/ 228 229 /* Locks/unlocks rcu and synchronizes without contention in a single fibril. */ 230 static bool basic_sanity_check(void) 231 { 53 232 rcu_read_lock(); 54 233 /* nop */ 55 234 rcu_read_unlock(); 235 236 rcu_read_lock(); 237 /* nop */ 56 238 rcu_read_unlock(); 57 239 58 240 rcu_synchronize(); 59 60 printf("Woohoo, did not crash after invoking rcu functions.\n"); 61 } 241 242 /* Nested lock with yield(). */ 243 rcu_read_lock(); 244 fibril_yield(); 245 rcu_read_lock(); 246 fibril_yield(); 247 rcu_read_unlock(); 248 fibril_yield(); 249 rcu_read_unlock(); 250 251 fibril_yield(); 252 rcu_synchronize(); 253 rcu_synchronize(); 254 255 rcu_read_lock(); 256 /* nop */ 257 if (!rcu_read_locked()) 258 return false; 259 260 rcu_read_unlock(); 261 262 return !rcu_read_locked(); 263 } 264 265 typedef struct one_reader_info { 266 bool entered_cs; 267 bool exited_cs; 268 size_t done_sleeps_cnt; 269 bool synching; 270 bool synched; 271 } one_reader_info_t; 272 273 274 static int sleeping_reader(one_reader_info_t *arg) 275 { 276 rcu_register_fibril(); 277 278 printf("lock{"); 279 rcu_read_lock(); 280 arg->entered_cs = true; 281 282 printf("r-sleep{"); 283 /* 2 sec */ 284 async_usleep(2 * USECS_PER_SEC); 285 ++arg->done_sleeps_cnt; 286 printf("}"); 287 288 arg->exited_cs = true; 289 rcu_read_unlock(); 290 printf("}"); 291 292 rcu_deregister_fibril(); 293 return 0; 294 } 295 296 static bool wait_for_one_reader(void) 297 { 298 one_reader_info_t info = { 0 }; 299 300 if (!create_fibril((fibril_func_t) sleeping_reader, &info)) 301 return false; 302 303 /* 1 sec, waits for the reader to enter its critical section and sleep. */ 304 async_usleep(1 * USECS_PER_SEC); 305 306 if (!info.entered_cs || info.exited_cs) { 307 printf("Error: reader is unexpectedly outside of critical section.\n"); 308 return false; 309 } 310 311 info.synching = true; 312 printf("sync["); 313 rcu_synchronize(); 314 printf("]\n"); 315 info.synched = true; 316 317 /* Load info.exited_cs */ 318 memory_barrier(); 319 320 if (!info.exited_cs) { 321 printf("Error: rcu_sync() returned before the reader exited its CS.\n"); 322 /* 323 * Sleep some more so we don't free info on stack while the reader 324 * is using it. 325 */ 326 /* 1.5 sec */ 327 async_usleep(1500 * 1000); 328 return false; 329 } else { 330 return true; 331 } 332 } 333 334 /*--------------------------------------------------------------------*/ 335 336 #define WAIT_STEP_US 1000 * USECS_PER_MS 337 338 typedef struct two_reader_info { 339 bool new_entered_cs; 340 bool new_exited_cs; 341 bool old_entered_cs; 342 bool old_exited_cs; 343 bool synching; 344 bool synched; 345 size_t failed; 346 } two_reader_info_t; 347 348 349 static int preexisting_reader(two_reader_info_t *arg) 350 { 351 rcu_register_fibril(); 352 353 printf("old-lock{"); 354 rcu_read_lock(); 355 arg->old_entered_cs = true; 356 357 printf("wait-for-sync{"); 358 /* Wait for rcu_sync() to start waiting for us. */ 359 while (!arg->synching) { 360 async_usleep(WAIT_STEP_US); 361 } 362 printf(" }"); 363 364 /* A new reader starts while rcu_sync() is in progress. */ 365 366 printf("wait-for-new-R{"); 367 /* Wait for the new reader to enter its reader section. */ 368 while (!arg->new_entered_cs) { 369 async_usleep(WAIT_STEP_US); 370 } 371 printf(" }"); 372 373 arg->old_exited_cs = true; 374 375 assert(!arg->new_exited_cs); 376 377 if (arg->synched) { 378 arg->failed = 1; 379 printf("Error: rcu_sync() did not wait for preexisting reader.\n"); 380 } 381 382 rcu_read_unlock(); 383 printf(" }"); 384 385 rcu_deregister_fibril(); 386 return 0; 387 } 388 389 static int new_reader(two_reader_info_t *arg) 390 { 391 rcu_register_fibril(); 392 393 /* Wait until rcu_sync() starts. */ 394 while (!arg->synching) { 395 async_usleep(WAIT_STEP_US); 396 } 397 398 /* 399 * synching is set when rcu_sync() is about to be entered so wait 400 * some more to make sure it really does start executing. 401 */ 402 async_usleep(WAIT_STEP_US); 403 404 printf("new-lock("); 405 rcu_read_lock(); 406 arg->new_entered_cs = true; 407 408 /* Wait for rcu_sync() exit, ie stop waiting for the preexisting reader. */ 409 while (!arg->synched) { 410 async_usleep(WAIT_STEP_US); 411 } 412 413 arg->new_exited_cs = true; 414 /* Write new_exited_cs before exiting reader section. */ 415 memory_barrier(); 416 417 /* 418 * Preexisting reader should have exited by now, so rcu_synchronize() 419 * must have returned. 420 */ 421 if (!arg->old_exited_cs) { 422 arg->failed = 1; 423 printf("Error: preexisting reader should have exited by now!\n"); 424 } 425 426 rcu_read_unlock(); 427 printf(")"); 428 429 rcu_deregister_fibril(); 430 return 0; 431 } 432 433 static bool dont_wait_for_new_reader(void) 434 { 435 two_reader_info_t info = { 0 }; 436 437 if (!create_fibril((fibril_func_t) preexisting_reader, &info)) 438 return false; 439 440 if (!create_fibril((fibril_func_t) new_reader, &info)) 441 return false; 442 443 /* Waits for the preexisting_reader to enter its CS.*/ 444 while (!info.old_entered_cs) { 445 async_usleep(WAIT_STEP_US); 446 } 447 448 assert(!info.old_exited_cs); 449 assert(!info.new_entered_cs); 450 assert(!info.new_exited_cs); 451 452 printf("sync["); 453 info.synching = true; 454 rcu_synchronize(); 455 printf(" ]"); 456 457 /* Load info.exited_cs */ 458 memory_barrier(); 459 460 if (!info.old_exited_cs) { 461 printf("Error: rcu_sync() returned before preexisting reader exited.\n"); 462 info.failed = 1; 463 } 464 465 bool new_outside_cs = !info.new_entered_cs || info.new_exited_cs; 466 467 /* Test if new reader is waiting in CS before setting synched. */ 468 compiler_barrier(); 469 info.synched = true; 470 471 if (new_outside_cs) { 472 printf("Error: new reader CS held up rcu_sync(). (4)\n"); 473 info.failed = 1; 474 } else { 475 /* Wait for the new reader. */ 476 rcu_synchronize(); 477 478 if (!info.new_exited_cs) { 479 printf("Error: 2nd rcu_sync() returned before new reader exited.\n"); 480 info.failed = 1; 481 } 482 483 printf("\n"); 484 } 485 486 if (info.failed) { 487 /* 488 * Sleep some more so we don't free info on stack while readers 489 * are using it. 490 */ 491 async_usleep(WAIT_STEP_US); 492 } 493 494 return 0 == info.failed; 495 } 496 497 #undef WAIT_STEP_US 498 /*--------------------------------------------------------------------*/ 499 /*--------------------------------------------------------------------*/ 500 static test_desc_t *find_test(const char *name) 501 { 502 /* First match for test name. */ 503 for (size_t k = 0; k < test_desc_cnt; ++k) { 504 test_desc_t *t = &test_desc[k]; 505 506 if (t->func && 0 == str_cmp(t->name, name)) 507 return t; 508 } 509 510 /* Try to match the test number. */ 511 uint32_t test_num = 0; 512 513 if (EOK == str_uint32_t(name, NULL, 0, true, &test_num)) { 514 if (test_num < test_desc_cnt && test_desc[test_num].func) { 515 printf("[%u]\n", test_num); 516 return &test_desc[test_num]; 517 } 518 } 519 520 return NULL; 521 } 522 523 static void list_tests(void) 524 { 525 printf("Available tests: \n"); 526 527 for (size_t i = 0; i < test_desc_cnt; ++i) { 528 test_desc_t *t = &test_desc[i]; 529 530 if (!t->func) 531 continue; 532 533 const char *type = ""; 534 535 if (t->type == T_SANITY) 536 type = " (sanity)"; 537 if (t->type == T_STRESS) 538 type = " (stress)"; 539 540 printf("%zu: %s ..%s %s\n", i, t->name, type, t->desc); 541 } 542 } 543 62 544 63 545 static void print_usage(void) 64 546 { 65 printf("Usage: rcutest \n"); 547 printf("Usage: rcutest [test_name|test_number]\n"); 548 list_tests(); 549 550 printf("\nExample usage:\n"); 551 printf("\trcutest *\n"); 552 printf("\trcutest sanity-tests\n"); 66 553 } 67 554 … … 69 556 int main(int argc, char **argv) 70 557 { 71 if (argc >= 2) { 558 rcu_register_fibril(); 559 560 if (argc != 2) { 72 561 print_usage(); 73 return 1; 74 } 75 76 rcu_register_fibril(); 77 78 invoke_rcu(); 79 /* todo: impl */ 80 81 rcu_deregister_fibril(); 82 83 return 0; 562 563 rcu_deregister_fibril(); 564 return 2; 565 } 566 567 test_desc_t *t = find_test(argv[1]); 568 569 if (t) { 570 printf("Running '%s'...\n", t->name); 571 bool ok = t->func(); 572 573 printf("%s: '%s'\n", ok ? "Passed" : "FAILED", t->name); 574 575 rcu_deregister_fibril(); 576 return ok ? 0 : 1; 577 } else { 578 printf("Non-existent test name.\n"); 579 list_tests(); 580 581 rcu_deregister_fibril(); 582 return 3; 583 } 84 584 } 85 585
Note:
See TracChangeset
for help on using the changeset viewer.