diff --git a/docs/src/main.dox b/docs/src/main.dox index 184bfab11..f4e638ca7 100644 --- a/docs/src/main.dox +++ b/docs/src/main.dox @@ -83,6 +83,7 @@ * - @subpage test_threads * - @subpage test_dynamic * - @subpage test_msg + * - @subpage test_mtx * - @subpage test_events * - @subpage test_mbox * - @subpage test_queues @@ -230,8 +231,8 @@ * In ChibiOS/RT the Unlock operations are always performed in Lock-reverse * order. The Unlock API does not even have a parameter, the mutex to unlock * is taken from an internal stack of owned mutexes. - * This both improves the performance and is required by the priority - * inheritance mechanism. + * This both improves the performance and is required by an efficient + * implementation of the priority inheritance mechanism. * *

The priority inversion problem

* The mutexes in ChibiOS/RT implements the full priority diff --git a/readme.txt b/readme.txt index 78db255e1..b0ef64678 100644 --- a/readme.txt +++ b/readme.txt @@ -75,6 +75,8 @@ Win32-MinGW - ChibiOS/RT simulator and demo into a WIN32 process, - FIX: Found new instances of the obsolete function chSysGetTime() in the C++ wrapper and in the WEB demo (bug 2772237)(backported in stable branch). - FIX: Fixed macro in test.h (bug 2781176)(backported in stable branch). +- FIX: Fixed sequence assertion in test.c (bug 2789377)(backported in stable + branch). - NEW: Abstract I/O Channels mechanism introduced. This mechanism allows to access I/O resources through a standard interface and hides implementation details. The existing serial drivers were modified to offer a standard diff --git a/test/test.c b/test/test.c index 02e56788a..aeb2b4058 100644 --- a/test/test.c +++ b/test/test.c @@ -145,6 +145,7 @@ bool_t _test_assert_sequence(unsigned point, char *expected) { } if (*expected) return _test_fail(point); + clear_tokens(); return FALSE; } @@ -174,16 +175,17 @@ void test_wait_threads(void) { } } -void test_cpu_pulse(unsigned ms) { +#if CH_DBG_THREADS_PROFILING +void test_cpu_pulse(unsigned duration) { - systime_t duration = MS2ST(ms); - systime_t start = chTimeNow(); - while (chTimeIsWithin(start, start + duration)) { + systime_t end = chThdSelf()->p_time + MS2ST(duration); + while (chThdSelf()->p_time < end) { #if defined(WIN32) ChkIntSources(); #endif } } +#endif systime_t test_wait_tick(void) { diff --git a/test/test.h b/test/test.h index c74610c95..761ee08b9 100644 --- a/test/test.h +++ b/test/test.h @@ -62,8 +62,10 @@ extern "C" { void test_terminate_threads(void); void test_wait_threads(void); systime_t test_wait_tick(void); - void test_cpu_pulse(unsigned ms); void test_start_timer(unsigned ms); +#if CH_DBG_THREADS_PROFILING + void test_cpu_pulse(unsigned duration); +#endif #if defined(WIN32) void ChkIntSources(void); #endif @@ -72,7 +74,7 @@ extern "C" { #endif #define test_fail(point) { \ - _test_fail(point); \ + _test_fail(point); \ return; \ } diff --git a/test/testdyn.c b/test/testdyn.c index 536bcadda..e881781f9 100644 --- a/test/testdyn.c +++ b/test/testdyn.c @@ -30,7 +30,7 @@ * *

Objective

* Objective of the test module is to cover 100% of the dynamic APIs code - * as a necessary step in order to assess their readyness. + * as a necessary step in order to assess their maturity. * *

Preconditions

* The module requires the following kernel options: diff --git a/test/testevt.c b/test/testevt.c index aa0a00da6..31f8870b0 100644 --- a/test/testevt.c +++ b/test/testevt.c @@ -29,7 +29,7 @@ * *

Objective

* Objective of the test module is to cover 100% of the @ref Events subsystem - * code as a necessary step in order to assess its readyness. + * code as a necessary step in order to assess its maturity level. * *

Preconditions

* The module requires the following kernel options: diff --git a/test/testheap.c b/test/testheap.c index a617c4b68..7c5b97918 100644 --- a/test/testheap.c +++ b/test/testheap.c @@ -29,7 +29,7 @@ * *

Objective

* Objective of the test module is to cover 100% of the @ref Heap subsystem - * code as a necessary step in order to assess its readyness. + * code as a necessary step in order to assess its maturity level. * *

Preconditions

* The module requires the following kernel options: diff --git a/test/testmbox.c b/test/testmbox.c index 2fe88b2b9..92897b1dc 100644 --- a/test/testmbox.c +++ b/test/testmbox.c @@ -29,7 +29,10 @@ * *

Objective

* Objective of the test module is to cover 100% of the @ref Mailboxes - * subsystem code as a necessary step in order to assess its readyness. + * subsystem code as a necessary step in order to assess its maturity + * level.
+ * Note that the @ref Mailboxes subsystem depends on the @ref Semaphores + * subsystem that has to met its testing objectives as well. * *

Preconditions

* The module requires the following kernel options: diff --git a/test/testmsg.c b/test/testmsg.c index 87ee64231..a44686ba6 100644 --- a/test/testmsg.c +++ b/test/testmsg.c @@ -29,7 +29,7 @@ * *

Objective

* Objective of the test module is to cover 100% of the @ref Messages - * subsystem code as a necessary step in order to assess its readyness. + * subsystem code as a necessary step in order to assess its maturity level. * *

Preconditions

* The module requires the following kernel options: diff --git a/test/testmtx.c b/test/testmtx.c index ddc32e7d5..59e18fa4e 100644 --- a/test/testmtx.c +++ b/test/testmtx.c @@ -21,6 +21,44 @@ #include "test.h" +/** + * @page test_mtx Mutexes test + * + *

Description

+ * This module implements the test sequence for the @ref Mutexes and + * @ref CondVars subsystems.
+ * Tests on those subsystems are particularly critical because the system-wide + * implications of the Priority Inheritance mechanism. + * + *

Objective

+ * Objective of the test module is to cover 100% of the subsystem code + * as a necessary step in order to assess their maturity level. + * + *

Preconditions

+ * The module requires the following kernel options: + * - @p CH_USE_MUTEXES + * - @p CH_USE_CONDVARS + * - @p CH_DBG_THREADS_PROFILING + * . + * In case some of the required options are not enabled then some or all tests + * may be skipped. + * + *

Test Cases

+ * - @subpage test_mtx_001 + * - @subpage test_mtx_002 + * - @subpage test_mtx_003 + * - @subpage test_mtx_004 + * - @subpage test_mtx_005 + * - @subpage test_mtx_006 + * - @subpage test_mtx_007 + * - @subpage test_mtx_008 + * . + * @file testmtx.c + * @brief @ref Mutexes and @ref CondVars test source file + * @file testmtx.h + * @brief @ref Mutexes and @ref CondVars test header file + */ + #if CH_USE_MUTEXES #define ALLOWED_DELAY 5 @@ -28,6 +66,15 @@ static Mutex m1, m2; static CondVar c1; +/** + * @page test_mtx_001 Priority enqueuing test + * + *

Description

+ * Five threads, with increasing priority, are enqueued on a locked mutex then + * the mutex is unlocked.
+ * The test expects the threads to perform their operations in increasing + * priority order regardless of the initial order. + */ static char *mtx1_gettest(void) { return "Mutexes, priority enqueuing test"; @@ -68,6 +115,38 @@ const struct testcase testmtx1 = { mtx1_execute }; +/** + * @page test_mtx_002 Priority inheritance, simple case + * + *

Description

+ * Three threads are involved in the classic priority inversion scenario, a + * medium priority thread tries to starve an high priority thread by + * blocking a low priority thread into a mutex lock zone.
+ * The test expects the threads to perform their operations in increasing + * priority order by rearranging their priorities in order to avoid the + * priority inversion trap. + * + *

Scenario

+ * This weird looking diagram should explain what happens in the test case: + * @code + * Time ----> 0 10 20 30 40 50 60 70 80 90 100 + * 0 ......AL++++++++++............2+++++++++++AU0---------------++++++G... + * 1 ..................++++++++++++------------------++++++++++++G......... + * 2 .............................AL..........++++++AUG................... + * ^ ^ + * + * Legend: + * 0..2 - Priority levels + * +++ - Running + * --- - Ready + * ... - Waiting + * AL - Lock operation on mutex A + * AUn - Unlock operation on mutex A with priority going to level 'n' + * G - Goal + * ^ - Priority transition (boost or return). + * @endcode + */ + static char *mtx2_gettest(void) { return "Mutexes, priority inheritance, simple case"; @@ -78,45 +157,48 @@ static void mtx2_setup(void) { chMtxInit(&m1); } -static msg_t thread2(void *p) { +/* Low priority thread */ +static msg_t thread2L(void *p) { - chThdSleepMilliseconds(10); chMtxLock(&m1); + test_cpu_pulse(40); chMtxUnlock(); - test_emit_token(*(char *)p); + test_cpu_pulse(10); + test_emit_token('C'); return 0; } -static msg_t thread3(void *p) { - - chMtxLock(&m1); - chThdSleepMilliseconds(40); - chMtxUnlock(); - test_emit_token(*(char *)p); - return 0; -} - -static msg_t thread4(void *p) { +/* Medium priority thread */ +static msg_t thread2M(void *p) { chThdSleepMilliseconds(20); - test_cpu_pulse(50); - test_emit_token(*(char *)p); + test_cpu_pulse(40); + test_emit_token('B'); return 0; } -/* - * Time - * 0 ++++++++++++++++++AL+....2++++++++++++++AU0------------------------------ - * 1 .....................++-------------------------------------------------- - * 2 .......................++AL.............+++++++++AU++++++++++++++++++++++ - */ -static void mtx2_execute(void) { +/* High priority thread */ +static msg_t thread2H(void *p) { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()-1, thread2, "A"); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()-3, thread3, "C"); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriority()-2, thread4, "B"); + chThdSleepMilliseconds(40); + chMtxLock(&m1); + test_cpu_pulse(10); + chMtxUnlock(); + test_emit_token('A'); + return 0; +} + +static void mtx2_execute(void) { + systime_t time; + + test_wait_tick(); + time = chTimeNow(); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority()-1, thread2H, 0); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriority()-3, thread2L, 0); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriority()-2, thread2M, 0); test_wait_threads(); test_assert_sequence(1, "ABC"); + test_assert_time_window(2, time + MS2ST(100), time + MS2ST(100) + ALLOWED_DELAY); } const struct testcase testmtx2 = { @@ -126,6 +208,38 @@ const struct testcase testmtx2 = { mtx2_execute }; +/** + * @page test_mtx_003 Priority inheritance, complex case + * + *

Description

+ * Five threads are involved in the complex priority inversion scenario, + * please refer to the diagram below for the complete scenario.
+ * The test expects the threads to perform their operations in increasing + * priority order by rearranging their priorities in order to avoid the + * priority inversion trap. + * + *

Scenario

+ * This weird looking diagram should explain what happens in the test case: + * @code + * Time ----> + * 0 10 20 30 40 50 + * 0 +++BL++------------------2++++------4+++++BU0-------------------------- + * 1 .......++AL++--2+++++++++BL.........4.....++++++++BU4++++AU1----------- + * 2 .............++AL............................................------++AU + * 3 ..............................++++-------------------------------++.... + * 4 ..................................++AL...................++++AU++...... + * ^ ^ + * + * Legend: + * 0..2 - Priority levels + * +++ - Running + * --- - Ready + * ... - Waiting + * AL - Lock operation on mutex A + * AUn - Unlock operation on mutex A with priority going to level 'n' + * ^ - Priority transition (boost or return). + * @endcode + */ static char *mtx3_gettest(void) { return "Mutexes, priority inheritance, complex case"; diff --git a/test/testqueues.c b/test/testqueues.c index af0ade6e6..be8d6fc62 100644 --- a/test/testqueues.c +++ b/test/testqueues.c @@ -32,7 +32,7 @@ * *

Objective

* Objective of the test module is to cover 100% of the @ref IOQueues code - * as a necessary step in order to assess its readyness.
+ * as a necessary step in order to assess its maturity level.
* Note that the @ref IOQueues subsystem depends on the @ref Semaphores * subsystem that has to met its testing objectives as well. * diff --git a/test/testserial.c b/test/testserial.c index 818d09e21..2da4b428b 100644 --- a/test/testserial.c +++ b/test/testserial.c @@ -32,7 +32,7 @@ * *

Objective

* Objective of the test module is to cover 100% of the @ref Serial code - * as a necessary step in order to assess its readyness.
+ * as a necessary step in order to assess its maturity level.
* Note that the @ref Serial subsystem depends on the @ref Semaphores and * @ref Events subsystems that have to met their testing objectives as well. * diff --git a/test/testthd.c b/test/testthd.c index daf3e2063..5ec9079d9 100644 --- a/test/testthd.c +++ b/test/testthd.c @@ -28,12 +28,12 @@ * This module implements the test sequence for the @ref Scheduler, * @ref Threads and @ref Time subsystems.
* Note that the tests on those subsystems are formally required but most of - * their functionality is already demostrated because the test suite itself - * depends on them, anyway doublecheck is good. + * their functionality is already demonstrated because the test suite itself + * depends on them, anyway double check is good. * *

Objective

* Objective of the test module is to cover 100% of the subsystems code - * as a necessary step in order to assess their readyness. + * as a necessary step in order to assess their maturity level. * *

Preconditions

* None. diff --git a/todo.txt b/todo.txt index 298a905af..801ec4302 100644 --- a/todo.txt +++ b/todo.txt @@ -8,11 +8,11 @@ After 1.2.0: * Abstract I/O channels rather than just serial ports. ? Move the serial drivers implementations in library. Better keep the core as compact as possible. +X Add tests documentation to the general documentation via doxygen. - Static object initializers. - Remove any instance of unnamed structures/unions. - Objects registry in the kernel. - OSEK-style simple tasks within the idle thread. -- Add tests documentation to the general documentation via doxygen. - Code examples into the documentation. - Dedicated syscalls.c support for newlib users. - Threads Pools manager in the library.