ChibiOS/RT Architecture - Reference Manual - Guides |
00001 /* 00002 ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010 Giovanni Di Sirio. 00003 00004 This file is part of ChibiOS/RT. 00005 00006 ChibiOS/RT is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 3 of the License, or 00009 (at your option) any later version. 00010 00011 ChibiOS/RT is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program. If not, see <http://www.gnu.org/licenses/>. 00018 00019 --- 00020 00021 A special exception to the GPL can be applied should you wish to distribute 00022 a combined work that includes ChibiOS/RT, without being obliged to provide 00023 the source code for any proprietary components. See the file exception.txt 00024 for full details of how and when the exception can be applied. 00025 */ 00026 00027 #include "ch.h" 00028 #include "test.h" 00029 00030 /** 00031 * @page test_mtx Mutexes test 00032 * 00033 * File: @ref testmtx.c 00034 * 00035 * <h2>Description</h2> 00036 * This module implements the test sequence for the @ref mutexes and 00037 * @ref condvars subsystems.<br> 00038 * Tests on those subsystems are particularly critical because the system-wide 00039 * implications of the Priority Inheritance mechanism. 00040 * 00041 * <h2>Objective</h2> 00042 * Objective of the test module is to cover 100% of the subsystems code. 00043 * 00044 * <h2>Preconditions</h2> 00045 * The module requires the following kernel options: 00046 * - @p CH_USE_MUTEXES 00047 * - @p CH_USE_CONDVARS 00048 * - @p CH_DBG_THREADS_PROFILING 00049 * . 00050 * In case some of the required options are not enabled then some or all tests 00051 * may be skipped. 00052 * 00053 * <h2>Test Cases</h2> 00054 * - @subpage test_mtx_001 00055 * - @subpage test_mtx_002 00056 * - @subpage test_mtx_003 00057 * - @subpage test_mtx_004 00058 * - @subpage test_mtx_005 00059 * - @subpage test_mtx_006 00060 * - @subpage test_mtx_007 00061 * - @subpage test_mtx_008 00062 * . 00063 * @file testmtx.c 00064 * @brief Mutexes and CondVars test source file 00065 * @file testmtx.h 00066 * @brief Mutexes and CondVars test header file 00067 */ 00068 00069 #if CH_USE_MUTEXES 00070 00071 #define ALLOWED_DELAY 5 00072 00073 /* 00074 * Note, the static initializers are not really required because the 00075 * variables are explicitly initialized in each test case. It is done in order 00076 * to test the macros. 00077 */ 00078 static MUTEX_DECL(m1); 00079 static MUTEX_DECL(m2); 00080 #if CH_USE_CONDVARS 00081 static CONDVAR_DECL(c1); 00082 #endif 00083 00084 /** 00085 * @page test_mtx_001 Priority enqueuing test 00086 * 00087 * <h2>Description</h2> 00088 * Five threads, with increasing priority, are enqueued on a locked mutex then 00089 * the mutex is unlocked.<br> 00090 * The test expects the threads to perform their operations in increasing 00091 * priority order regardless of the initial order. 00092 */ 00093 static char *mtx1_gettest(void) { 00094 00095 return "Mutexes, priority enqueuing test"; 00096 } 00097 00098 static void mtx1_setup(void) { 00099 00100 chMtxInit(&m1); 00101 } 00102 00103 static msg_t thread1(void *p) { 00104 00105 chMtxLock(&m1); 00106 test_emit_token(*(char *)p); 00107 chMtxUnlock(); 00108 return 0; 00109 } 00110 00111 static void mtx1_execute(void) { 00112 00113 tprio_t prio = chThdGetPriority(); // Because priority inheritance. 00114 chMtxLock(&m1); 00115 threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread1, "E"); 00116 threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread1, "D"); 00117 threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread1, "C"); 00118 threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread1, "B"); 00119 threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread1, "A"); 00120 chMtxUnlock(); 00121 test_wait_threads(); 00122 test_assert(1, prio == chThdGetPriority(), "wrong priority level"); 00123 test_assert_sequence(2, "ABCDE"); 00124 } 00125 00126 const struct testcase testmtx1 = { 00127 mtx1_gettest, 00128 mtx1_setup, 00129 NULL, 00130 mtx1_execute 00131 }; 00132 00133 #if CH_DBG_THREADS_PROFILING 00134 /** 00135 * @page test_mtx_002 Priority inheritance, simple case 00136 * 00137 * <h2>Description</h2> 00138 * Three threads are involved in the classic priority inversion scenario, a 00139 * medium priority thread tries to starve an high priority thread by 00140 * blocking a low priority thread into a mutex lock zone.<br> 00141 * The test expects the threads to reach their goal in increasing priority 00142 * order by rearranging their priorities in order to avoid the priority 00143 * inversion trap. 00144 * 00145 * <h2>Scenario</h2> 00146 * This weird looking diagram should explain what happens in the test case: 00147 * @code 00148 * Time ----> 0 10 20 30 40 50 60 70 80 90 100 00149 * 0 ......AL++++++++++............2+++++++++++AU0---------------++++++G... 00150 * 1 ..................++++++++++++------------------++++++++++++G......... 00151 * 2 .............................AL..........++++++AUG................... 00152 * ^ ^ 00153 * Legend: 00154 * 0..2 - Priority levels 00155 * +++ - Running 00156 * --- - Ready 00157 * ... - Waiting or Terminated 00158 * xL - Lock operation on mutex 'x' 00159 * xUn - Unlock operation on mutex 'x' with priority returning to level 'n' 00160 * G - Goal 00161 * ^ - Priority transition (boost or return). 00162 * @endcode 00163 */ 00164 00165 static char *mtx2_gettest(void) { 00166 00167 return "Mutexes, priority inheritance, simple case"; 00168 } 00169 00170 static void mtx2_setup(void) { 00171 00172 chMtxInit(&m1); 00173 } 00174 00175 /* Low priority thread */ 00176 static msg_t thread2L(void *p) { 00177 00178 (void)p; 00179 chMtxLock(&m1); 00180 test_cpu_pulse(40); 00181 chMtxUnlock(); 00182 test_cpu_pulse(10); 00183 test_emit_token('C'); 00184 return 0; 00185 } 00186 00187 /* Medium priority thread */ 00188 static msg_t thread2M(void *p) { 00189 00190 (void)p; 00191 chThdSleepMilliseconds(20); 00192 test_cpu_pulse(40); 00193 test_emit_token('B'); 00194 return 0; 00195 } 00196 00197 /* High priority thread */ 00198 static msg_t thread2H(void *p) { 00199 00200 (void)p; 00201 chThdSleepMilliseconds(40); 00202 chMtxLock(&m1); 00203 test_cpu_pulse(10); 00204 chMtxUnlock(); 00205 test_emit_token('A'); 00206 return 0; 00207 } 00208 00209 static void mtx2_execute(void) { 00210 systime_t time; 00211 00212 test_wait_tick(); 00213 time = chTimeNow(); 00214 threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()-1, thread2H, 0); 00215 threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()-2, thread2M, 0); 00216 threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriority()-3, thread2L, 0); 00217 test_wait_threads(); 00218 test_assert_sequence(1, "ABC"); 00219 test_assert_time_window(2, time + MS2ST(100), time + MS2ST(100) + ALLOWED_DELAY); 00220 } 00221 00222 const struct testcase testmtx2 = { 00223 mtx2_gettest, 00224 mtx2_setup, 00225 NULL, 00226 mtx2_execute 00227 }; 00228 00229 /** 00230 * @page test_mtx_003 Priority inheritance, complex case 00231 * 00232 * <h2>Description</h2> 00233 * Five threads are involved in the complex priority inversion scenario, 00234 * please refer to the diagram below for the complete scenario.<br> 00235 * The test expects the threads to perform their operations in increasing 00236 * priority order by rearranging their priorities in order to avoid the 00237 * priority inversion trap. 00238 * 00239 * <h2>Scenario</h2> 00240 * This weird looking diagram should explain what happens in the test case: 00241 * @code 00242 * Time ----> 0 10 20 30 40 50 60 70 80 90 100 110 00243 * 0 ......BL++++------------2+++++------4+++++BU0---------------------------G..... 00244 * 1 ............AL++++2+++++BL----------4-----++++++BU4+++AU1---------------G..... 00245 * 2 ..................AL----------------------------------------------++++++AUG... 00246 * 3 ..............................+++++++-----------------------++++++G........... 00247 * 4 ....................................AL................++++++AUG............... 00248 * ^ ^ ^ ^ ^ ^ 00249 * Legend: 00250 * 0..4 - Priority levels 00251 * +++ - Running 00252 * --- - Ready 00253 * ... - Waiting or Terminated 00254 * xL - Lock operation on mutex 'x' 00255 * xUn - Unlock operation on mutex 'x' with priority returning to level 'n' 00256 * ^ - Priority transition (boost or return). 00257 * @endcode 00258 */ 00259 static char *mtx3_gettest(void) { 00260 00261 return "Mutexes, priority inheritance, complex case"; 00262 } 00263 00264 static void mtx3_setup(void) { 00265 00266 chMtxInit(&m1); // Mutex B 00267 chMtxInit(&m2); // Mutex A 00268 } 00269 00270 /* Lowest priority thread */ 00271 static msg_t thread3LL(void *p) { 00272 00273 (void)p; 00274 chMtxLock(&m1); 00275 test_cpu_pulse(30); 00276 chMtxUnlock(); 00277 test_emit_token('E'); 00278 return 0; 00279 } 00280 00281 /* Low priority thread */ 00282 static msg_t thread3L(void *p) { 00283 00284 (void)p; 00285 chThdSleepMilliseconds(10); 00286 chMtxLock(&m2); 00287 test_cpu_pulse(20); 00288 chMtxLock(&m1); 00289 test_cpu_pulse(10); 00290 chMtxUnlock(); 00291 test_cpu_pulse(10); 00292 chMtxUnlock(); 00293 test_emit_token('D'); 00294 return 0; 00295 } 00296 00297 /* Medium priority thread */ 00298 static msg_t thread3M(void *p) { 00299 00300 (void)p; 00301 chThdSleepMilliseconds(20); 00302 chMtxLock(&m2); 00303 test_cpu_pulse(10); 00304 chMtxUnlock(); 00305 test_emit_token('C'); 00306 return 0; 00307 } 00308 00309 /* High priority thread */ 00310 static msg_t thread3H(void *p) { 00311 00312 (void)p; 00313 chThdSleepMilliseconds(40); 00314 test_cpu_pulse(20); 00315 test_emit_token('B'); 00316 return 0; 00317 } 00318 00319 /* Highest priority thread */ 00320 static msg_t thread3HH(void *p) { 00321 00322 (void)p; 00323 chThdSleepMilliseconds(50); 00324 chMtxLock(&m2); 00325 test_cpu_pulse(10); 00326 chMtxUnlock(); 00327 test_emit_token('A'); 00328 return 0; 00329 } 00330 00331 static void mtx3_execute(void) { 00332 systime_t time; 00333 00334 test_wait_tick(); 00335 time = chTimeNow(); 00336 threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()-5, thread3LL, 0); 00337 threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()-4, thread3L, 0); 00338 threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriority()-3, thread3M, 0); 00339 threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriority()-2, thread3H, 0); 00340 threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriority()-1, thread3HH, 0); 00341 test_wait_threads(); 00342 test_assert_sequence(1, "ABCDE"); 00343 test_assert_time_window(2, time + MS2ST(110), time + MS2ST(110) + ALLOWED_DELAY); 00344 } 00345 00346 const struct testcase testmtx3 = { 00347 mtx3_gettest, 00348 mtx3_setup, 00349 NULL, 00350 mtx3_execute 00351 }; 00352 #endif /* CH_DBG_THREADS_PROFILING */ 00353 00354 /** 00355 * @page test_mtx_004 Priority return verification 00356 * 00357 * <h2>Description</h2> 00358 * Two threads are spawned that try to lock the mutexes locked by the tester 00359 * thread with precise timing.<br> 00360 * The test expects that the priority changes caused by the priority 00361 * inheritance algorithm happen at the right moment and with the right values. 00362 */ 00363 static char *mtx4_gettest(void) { 00364 00365 return "Mutexes, priority return"; 00366 } 00367 00368 static void mtx4_setup(void) { 00369 00370 chMtxInit(&m1); 00371 chMtxInit(&m2); 00372 } 00373 00374 static msg_t thread4a(void *p) { 00375 00376 (void)p; 00377 chThdSleepMilliseconds(50); 00378 chMtxLock(&m2); 00379 chMtxUnlock(); 00380 return 0; 00381 } 00382 00383 static msg_t thread4b(void *p) { 00384 00385 (void)p; 00386 chThdSleepMilliseconds(150); 00387 chMtxLock(&m1); 00388 chMtxUnlock(); 00389 return 0; 00390 } 00391 00392 static void mtx4_execute(void) { 00393 tprio_t p, p1, p2; 00394 00395 p = chThdGetPriority(); 00396 p1 = p + 1; 00397 p2 = p + 2; 00398 threads[0] = chThdCreateStatic(wa[0], WA_SIZE, p1, thread4a, "B"); 00399 threads[1] = chThdCreateStatic(wa[1], WA_SIZE, p2, thread4b, "A"); 00400 chMtxLock(&m2); 00401 test_assert(1, chThdGetPriority() == p, "wrong priority level"); 00402 chThdSleepMilliseconds(100); 00403 test_assert(2, chThdGetPriority() == p1, "wrong priority level"); 00404 chMtxLock(&m1); 00405 test_assert(3, chThdGetPriority() == p1, "wrong priority level"); 00406 chThdSleepMilliseconds(100); 00407 test_assert(4, chThdGetPriority() == p2, "wrong priority level"); 00408 chMtxUnlock(); 00409 test_assert(5, chThdGetPriority() == p1, "wrong priority level"); 00410 chThdSleepMilliseconds(100); 00411 test_assert(6, chThdGetPriority() == p1, "wrong priority level"); 00412 chMtxUnlockAll(); 00413 test_assert(7, chThdGetPriority() == p, "wrong priority level"); 00414 test_wait_threads(); 00415 00416 /* Test repeated in order to cover chMtxUnlockS().*/ 00417 threads[0] = chThdCreateStatic(wa[0], WA_SIZE, p1, thread4a, "D"); 00418 threads[1] = chThdCreateStatic(wa[1], WA_SIZE, p2, thread4b, "C"); 00419 chMtxLock(&m2); 00420 test_assert(8, chThdGetPriority() == p, "wrong priority level"); 00421 chThdSleepMilliseconds(100); 00422 test_assert(9, chThdGetPriority() == p1, "wrong priority level"); 00423 chMtxLock(&m1); 00424 test_assert(10, chThdGetPriority() == p1, "wrong priority level"); 00425 chThdSleepMilliseconds(100); 00426 test_assert(11, chThdGetPriority() == p2, "wrong priority level"); 00427 chSysLock(); 00428 chMtxUnlockS(); 00429 chSysUnlock(); 00430 test_assert(12, chThdGetPriority() == p1, "wrong priority level"); 00431 chThdSleepMilliseconds(100); 00432 test_assert(13, chThdGetPriority() == p1, "wrong priority level"); 00433 chMtxUnlockAll(); 00434 test_assert(14, chThdGetPriority() == p, "wrong priority level"); 00435 test_wait_threads(); 00436 } 00437 00438 const struct testcase testmtx4 = { 00439 mtx4_gettest, 00440 mtx4_setup, 00441 NULL, 00442 mtx4_execute 00443 }; 00444 00445 /** 00446 * @page test_mtx_005 Mutex status 00447 * 00448 * <h2>Description</h2> 00449 * Various tests on the mutex structure status after performing some lock and 00450 * unlock operations.<br> 00451 * The test expects that the internal mutex status is consistent after each 00452 * operation. 00453 */ 00454 static char *mtx5_gettest(void) { 00455 00456 return "Mutexes, status"; 00457 } 00458 00459 static void mtx5_setup(void) { 00460 00461 chMtxInit(&m1); 00462 } 00463 00464 static void mtx5_execute(void) { 00465 bool_t b; 00466 tprio_t prio; 00467 00468 prio = chThdGetPriority(); 00469 00470 b = chMtxTryLock(&m1); 00471 test_assert(1, b, "already locked"); 00472 00473 b = chMtxTryLock(&m1); 00474 test_assert(2, !b, "not locked"); 00475 00476 chSysLock(); 00477 chMtxUnlockS(); 00478 chSysUnlock(); 00479 00480 test_assert(3, isempty(&m1.m_queue), "queue not empty"); 00481 test_assert(4, m1.m_owner == NULL, "still owned"); 00482 test_assert(5, chThdGetPriority() == prio, "wrong priority level"); 00483 } 00484 00485 const struct testcase testmtx5 = { 00486 mtx5_gettest, 00487 mtx5_setup, 00488 NULL, 00489 mtx5_execute 00490 }; 00491 00492 #if CH_USE_CONDVARS 00493 /** 00494 * @page test_mtx_006 Condition Variable signal test 00495 * 00496 * <h2>Description</h2> 00497 * Five threads take a mutex and then enter a conditional variable queue, the 00498 * tester thread then proceeds to signal the conditional variable five times 00499 * atomically.<br> 00500 * The test expects the threads to reach their goal in increasing priority 00501 * order regardless of the initial order. 00502 */ 00503 static char *mtx6_gettest(void) { 00504 00505 return "CondVar, signal test"; 00506 } 00507 00508 static void mtx6_setup(void) { 00509 00510 chCondInit(&c1); 00511 chMtxInit(&m1); 00512 } 00513 00514 static msg_t thread10(void *p) { 00515 00516 chMtxLock(&m1); 00517 chCondWait(&c1); 00518 test_emit_token(*(char *)p); 00519 chMtxUnlock(); 00520 return 0; 00521 } 00522 00523 static void mtx6_execute(void) { 00524 00525 tprio_t prio = chThdGetPriority(); 00526 threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread10, "E"); 00527 threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread10, "D"); 00528 threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread10, "C"); 00529 threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread10, "B"); 00530 threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread10, "A"); 00531 chSysLock(); 00532 chCondSignalI(&c1); 00533 chCondSignalI(&c1); 00534 chCondSignalI(&c1); 00535 chCondSignalI(&c1); 00536 chCondSignalI(&c1); 00537 chSchRescheduleS(); 00538 chSysUnlock(); 00539 test_wait_threads(); 00540 test_assert_sequence(1, "ABCDE"); 00541 } 00542 00543 const struct testcase testmtx6 = { 00544 mtx6_gettest, 00545 mtx6_setup, 00546 NULL, 00547 mtx6_execute 00548 }; 00549 00550 /** 00551 * @page test_mtx_007 Condition Variable broadcast test 00552 * 00553 * <h2>Description</h2> 00554 * Five threads take a mutex and then enter a conditional variable queue, the 00555 * tester thread then proceeds to broadcast the conditional variable.<br> 00556 * The test expects the threads to reach their goal in increasing priority 00557 * order regardless of the initial order. 00558 */ 00559 static char *mtx7_gettest(void) { 00560 00561 return "CondVar, broadcast test"; 00562 } 00563 00564 static void mtx7_setup(void) { 00565 00566 chCondInit(&c1); 00567 chMtxInit(&m1); 00568 } 00569 00570 static void mtx7_execute(void) { 00571 00572 // Bacause priority inheritance. 00573 tprio_t prio = chThdGetPriority(); 00574 threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread10, "E"); 00575 threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread10, "D"); 00576 threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread10, "C"); 00577 threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread10, "B"); 00578 threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread10, "A"); 00579 chCondBroadcast(&c1); 00580 test_wait_threads(); 00581 test_assert_sequence(1, "ABCDE"); 00582 } 00583 00584 const struct testcase testmtx7 = { 00585 mtx7_gettest, 00586 mtx7_setup, 00587 NULL, 00588 mtx7_execute 00589 }; 00590 00591 /** 00592 * @page test_mtx_008 Condition Variable priority boost test 00593 * 00594 * <h2>Description</h2> 00595 * This test case verifies the priority boost of a thread waiting on a 00596 * conditional variable queue. It tests this very specific situation in order 00597 * to complete the code coverage. 00598 */ 00599 static char *mtx8_gettest(void) { 00600 00601 return "CondVar, boost test"; 00602 } 00603 00604 static void mtx8_setup(void) { 00605 00606 chCondInit(&c1); 00607 chMtxInit(&m1); 00608 chMtxInit(&m2); 00609 } 00610 00611 static msg_t thread11(void *p) { 00612 00613 chMtxLock(&m2); 00614 chMtxLock(&m1); 00615 #if CH_USE_CONDVARS_TIMEOUT 00616 chCondWaitTimeout(&c1, TIME_INFINITE); 00617 #else 00618 chCondWait(&c1); 00619 #endif 00620 test_emit_token(*(char *)p); 00621 chMtxUnlock(); 00622 chMtxUnlock(); 00623 return 0; 00624 } 00625 00626 static msg_t thread12(void *p) { 00627 00628 chMtxLock(&m2); 00629 test_emit_token(*(char *)p); 00630 chMtxUnlock(); 00631 return 0; 00632 } 00633 00634 static void mtx8_execute(void) { 00635 00636 tprio_t prio = chThdGetPriority(); 00637 threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread11, "A"); 00638 threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread10, "C"); 00639 threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread12, "B"); 00640 chCondSignal(&c1); 00641 chCondSignal(&c1); 00642 test_wait_threads(); 00643 test_assert_sequence(1, "ABC"); 00644 } 00645 00646 const struct testcase testmtx8 = { 00647 mtx8_gettest, 00648 mtx8_setup, 00649 NULL, 00650 mtx8_execute 00651 }; 00652 #endif /* CH_USE_CONDVARS */ 00653 #endif /* CH_USE_MUTEXES */ 00654 00655 /** 00656 * @brief Test sequence for mutexes. 00657 */ 00658 const struct testcase * const patternmtx[] = { 00659 #if CH_USE_MUTEXES 00660 &testmtx1, 00661 #if CH_DBG_THREADS_PROFILING 00662 &testmtx2, 00663 &testmtx3, 00664 #endif 00665 &testmtx4, 00666 &testmtx5, 00667 #if CH_USE_CONDVARS 00668 &testmtx6, 00669 &testmtx7, 00670 &testmtx8, 00671 #endif 00672 #endif 00673 NULL 00674 };