diff --git a/os/rt/src/chvt.c b/os/rt/src/chvt.c index 1085cf45c..c9636920b 100644 --- a/os/rt/src/chvt.c +++ b/os/rt/src/chvt.c @@ -495,17 +495,24 @@ void chVTDoTickI(void) { */ systimestamp_t chVTGetTimeStampI(void) { systimestamp_t last, stamp; + systime_t now; chDbgCheckClassI(); + /* Current system time.*/ + now = chVTGetSystemTimeX(); + /* Last time stamp generated.*/ last = ch.vtlist.laststamp; /* Interval between the last time stamp and current time used for a new time stamp. Note that this fails if the interval is larger than a systime_t type.*/ - stamp = last + (systimestamp_t)chTimeDiffX((sysinterval_t)last, - chVTGetSystemTimeX()); + stamp = last + (systimestamp_t)chTimeDiffX((sysinterval_t)last, now); + + chDbgAssert(ch.vtlist.laststamp <= stamp, "wrapped"); + + /* Storing the new stamp.*/ ch.vtlist.laststamp = stamp; return stamp; diff --git a/test/rt/configuration.xml b/test/rt/configuration.xml index 6a3e0a22f..873ff3871 100644 --- a/test/rt/configuration.xml +++ b/test/rt/configuration.xml @@ -647,7 +647,7 @@ chSysEnable();]]> Time and Intervals Functionality. - This sequence tests the ChibiOS/NIL functionalities related to time and intervals management. + This sequence tests the ChibiOS/RT functionalities related to time and intervals management. @@ -788,6 +788,74 @@ test_assert(b == false, "in range"); + + + Internal Tests + + + Time Stamps Functionality. + + + This sequence tests the ChibiOS/RT functionalities related to time stamps. + + + + + + + + + + + Time Stamps functionality. + + + The functionality of the API @p chVTGetTimeStamp() is tested. + + + + + + + + + + + + + + + + + + + Time stamps are generated and checked for monotonicity. + + + + + + + + + + + + Internal Tests diff --git a/test/rt/rt_test.mk b/test/rt/rt_test.mk index bba0a09fc..38ff54ffa 100644 --- a/test/rt/rt_test.mk +++ b/test/rt/rt_test.mk @@ -10,7 +10,8 @@ TESTSRC += ${CHIBIOS}/test/rt/source/test/rt_test_root.c \ ${CHIBIOS}/test/rt/source/test/rt_test_sequence_008.c \ ${CHIBIOS}/test/rt/source/test/rt_test_sequence_009.c \ ${CHIBIOS}/test/rt/source/test/rt_test_sequence_010.c \ - ${CHIBIOS}/test/rt/source/test/rt_test_sequence_011.c + ${CHIBIOS}/test/rt/source/test/rt_test_sequence_011.c \ + ${CHIBIOS}/test/rt/source/test/rt_test_sequence_012.c # Required include directories TESTINC += ${CHIBIOS}/test/rt/source/test diff --git a/test/rt/source/test/rt_test_root.c b/test/rt/source/test/rt_test_root.c index 4a8f4e1f8..0db7ce8cf 100644 --- a/test/rt/source/test/rt_test_root.c +++ b/test/rt/source/test/rt_test_root.c @@ -32,6 +32,7 @@ * - @subpage rt_test_sequence_009 * - @subpage rt_test_sequence_010 * - @subpage rt_test_sequence_011 + * - @subpage rt_test_sequence_012 * . */ @@ -58,22 +59,23 @@ const testsequence_t * const rt_test_suite_array[] = { &rt_test_sequence_003, &rt_test_sequence_004, &rt_test_sequence_005, -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) &rt_test_sequence_006, -#endif -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) &rt_test_sequence_007, #endif -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) &rt_test_sequence_008, #endif -#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) &rt_test_sequence_009, #endif -#if (CH_CFG_USE_DYNAMIC) || defined(__DOXYGEN__) +#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) &rt_test_sequence_010, #endif +#if (CH_CFG_USE_DYNAMIC) || defined(__DOXYGEN__) &rt_test_sequence_011, +#endif + &rt_test_sequence_012, NULL }; diff --git a/test/rt/source/test/rt_test_root.h b/test/rt/source/test/rt_test_root.h index 963a4a45c..9bda53b1a 100644 --- a/test/rt/source/test/rt_test_root.h +++ b/test/rt/source/test/rt_test_root.h @@ -35,6 +35,7 @@ #include "rt_test_sequence_009.h" #include "rt_test_sequence_010.h" #include "rt_test_sequence_011.h" +#include "rt_test_sequence_012.h" #if !defined(__DOXYGEN__) diff --git a/test/rt/source/test/rt_test_sequence_003.c b/test/rt/source/test/rt_test_sequence_003.c index 761b7baae..f99433892 100644 --- a/test/rt/source/test/rt_test_sequence_003.c +++ b/test/rt/source/test/rt_test_sequence_003.c @@ -26,7 +26,7 @@ * File: @ref rt_test_sequence_003.c * *

Description

- * This sequence tests the ChibiOS/NIL functionalities related to time + * This sequence tests the ChibiOS/RT functionalities related to time * and intervals management. * *

Test Cases

diff --git a/test/rt/source/test/rt_test_sequence_004.c b/test/rt/source/test/rt_test_sequence_004.c index 167494ad0..4d9259507 100644 --- a/test/rt/source/test/rt_test_sequence_004.c +++ b/test/rt/source/test/rt_test_sequence_004.c @@ -21,19 +21,16 @@ * @file rt_test_sequence_004.c * @brief Test Sequence 004 code. * - * @page rt_test_sequence_004 [4] Threads Functionality + * @page rt_test_sequence_004 [4] Time Stamps Functionality * * File: @ref rt_test_sequence_004.c * *

Description

- * This sequence tests the ChibiOS/RT functionalities related to - * threading. + * This sequence tests the ChibiOS/RT functionalities related to time + * stamps. * *

Test Cases

* - @subpage rt_test_004_001 - * - @subpage rt_test_004_002 - * - @subpage rt_test_004_003 - * - @subpage rt_test_004_004 * . */ @@ -41,304 +38,53 @@ * Shared code. ****************************************************************************/ -static THD_FUNCTION(thread, p) { - - test_emit_token(*(char *)p); -} +#include "ch.h" /**************************************************************************** * Test cases. ****************************************************************************/ /** - * @page rt_test_004_001 [4.1] Thread Sleep functionality + * @page rt_test_004_001 [4.1] Time Stamps functionality * *

Description

- * The functionality of @p chThdSleep() and derivatives is tested. + * The functionality of the API @p chVTGetTimeStamp() is tested. * *

Test Steps

- * - [4.1.1] The current system time is read then a sleep is performed - * for 100 system ticks and on exit the system time is verified - * again. - * - [4.1.2] The current system time is read then a sleep is performed - * for 100000 microseconds and on exit the system time is verified - * again. - * - [4.1.3] The current system time is read then a sleep is performed - * for 100 milliseconds and on exit the system time is verified - * again. - * - [4.1.4] The current system time is read then a sleep is performed - * for 1 second and on exit the system time is verified again. - * - [4.1.5] Function chThdSleepUntil() is tested with a timeline of - * "now" + 100 ticks. + * - [4.1.1] Time stamps are generated and checked for monotonicity. * . */ static void rt_test_004_001_execute(void) { - systime_t time; - /* [4.1.1] The current system time is read then a sleep is performed - for 100 system ticks and on exit the system time is verified - again.*/ + /* [4.1.1] Time stamps are generated and checked for monotonicity.*/ test_set_step(1); { - time = chVTGetSystemTimeX(); - chThdSleep(100); - test_assert_time_window(chTimeAddX(time, 100), - chTimeAddX(time, 100 + CH_CFG_ST_TIMEDELTA + 1), - "out of time window"); + systime_t start, end; + systimestamp_t last, now; + + last = chVTGetTimeStamp(); + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + now = chVTGetTimeStamp(); + test_assert(last <= now, "not monotonic"); + last = now; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); } test_end_step(1); - - /* [4.1.2] The current system time is read then a sleep is performed - for 100000 microseconds and on exit the system time is verified - again.*/ - test_set_step(2); - { - time = chVTGetSystemTimeX(); - chThdSleepMicroseconds(100000); - test_assert_time_window(chTimeAddX(time, TIME_US2I(100000)), - chTimeAddX(time, TIME_US2I(100000) + CH_CFG_ST_TIMEDELTA + 1), - "out of time window"); - } - test_end_step(2); - - /* [4.1.3] The current system time is read then a sleep is performed - for 100 milliseconds and on exit the system time is verified - again.*/ - test_set_step(3); - { - time = chVTGetSystemTimeX(); - chThdSleepMilliseconds(100); - test_assert_time_window(chTimeAddX(time, TIME_MS2I(100)), - chTimeAddX(time, TIME_MS2I(100) + CH_CFG_ST_TIMEDELTA + 1), - "out of time window"); - } - test_end_step(3); - - /* [4.1.4] The current system time is read then a sleep is performed - for 1 second and on exit the system time is verified again.*/ - test_set_step(4); - { - time = chVTGetSystemTimeX(); - chThdSleepSeconds(1); - test_assert_time_window(chTimeAddX(time, TIME_S2I(1)), - chTimeAddX(time, TIME_S2I(1) + CH_CFG_ST_TIMEDELTA + 1), - "out of time window"); - } - test_end_step(4); - - /* [4.1.5] Function chThdSleepUntil() is tested with a timeline of - "now" + 100 ticks.*/ - test_set_step(5); - { - time = chVTGetSystemTimeX(); - chThdSleepUntil(chTimeAddX(time, 100)); - test_assert_time_window(chTimeAddX(time, 100), - chTimeAddX(time, 100 + CH_CFG_ST_TIMEDELTA + 1), - "out of time window"); - } - test_end_step(5); } static const testcase_t rt_test_004_001 = { - "Thread Sleep functionality", + "Time Stamps functionality", NULL, NULL, rt_test_004_001_execute }; -/** - * @page rt_test_004_002 [4.2] Ready List functionality, threads priority order - * - *

Description

- * Five threads, are enqueued in the ready list and atomically - * executed. The test expects the threads to perform their operations - * in correct priority order regardless of the initial order. - * - *

Test Steps

- * - [4.2.1] Creating 5 threads with increasing priority, execution - * sequence is tested. - * - [4.2.2] Creating 5 threads with decreasing priority, execution - * sequence is tested. - * - [4.2.3] Creating 5 threads with pseudo-random priority, execution - * sequence is tested. - * . - */ - -static void rt_test_004_002_execute(void) { - - /* [4.2.1] Creating 5 threads with increasing priority, execution - sequence is tested.*/ - test_set_step(1); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E"); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D"); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C"); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B"); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A"); - test_wait_threads(); - test_assert_sequence("ABCDE", "invalid sequence"); - } - test_end_step(1); - - /* [4.2.2] Creating 5 threads with decreasing priority, execution - sequence is tested.*/ - test_set_step(2); - { - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A"); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B"); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C"); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D"); - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E"); - test_wait_threads(); - test_assert_sequence("ABCDE", "invalid sequence"); - } - test_end_step(2); - - /* [4.2.3] Creating 5 threads with pseudo-random priority, execution - sequence is tested.*/ - test_set_step(3); - { - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D"); - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E"); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A"); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B"); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C"); - test_wait_threads(); - test_assert_sequence("ABCDE", "invalid sequence"); - } - test_end_step(3); -} - -static const testcase_t rt_test_004_002 = { - "Ready List functionality, threads priority order", - NULL, - NULL, - rt_test_004_002_execute -}; - -/** - * @page rt_test_004_003 [4.3] Priority change test - * - *

Description

- * A series of priority changes are performed on the current thread in - * order to verify that the priority change happens as expected. - * - *

Test Steps

- * - [4.3.1] Thread priority is increased by one then a check is - * performed. - * - [4.3.2] Thread priority is returned to the previous value then a - * check is performed. - * . - */ - -static void rt_test_004_003_execute(void) { - tprio_t prio, p1; - - /* [4.3.1] Thread priority is increased by one then a check is - performed.*/ - test_set_step(1); - { - prio = chThdGetPriorityX(); - p1 = chThdSetPriority(prio + 1); - test_assert(p1 == prio, "unexpected returned priority level"); - test_assert(chThdGetPriorityX() == prio + 1, "unexpected priority level"); - } - test_end_step(1); - - /* [4.3.2] Thread priority is returned to the previous value then a - check is performed.*/ - test_set_step(2); - { - p1 = chThdSetPriority(p1); - test_assert(p1 == prio + 1, "unexpected returned priority level"); - test_assert(chThdGetPriorityX() == prio, "unexpected priority level"); - } - test_end_step(2); -} - -static const testcase_t rt_test_004_003 = { - "Priority change test", - NULL, - NULL, - rt_test_004_003_execute -}; - -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) -/** - * @page rt_test_004_004 [4.4] Priority change test with Priority Inheritance - * - *

Description

- * A series of priority changes are performed on the current thread in - * order to verify that the priority change happens as expected. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_MUTEXES - * . - * - *

Test Steps

- * - [4.4.1] Simulating a priority boost situation (prio > realprio). - * - [4.4.2] Raising thread priority above original priority but below - * the boosted level. - * - [4.4.3] Raising thread priority above the boosted level. - * - [4.4.4] Restoring original conditions. - * . - */ - -static void rt_test_004_004_execute(void) { - tprio_t prio, p1; - - /* [4.4.1] Simulating a priority boost situation (prio > realprio).*/ - test_set_step(1); - { - prio = chThdGetPriorityX(); - chThdGetSelfX()->prio += 2; - test_assert(chThdGetPriorityX() == prio + 2, "unexpected priority level"); - } - test_end_step(1); - - /* [4.4.2] Raising thread priority above original priority but below - the boosted level.*/ - test_set_step(2); - { - p1 = chThdSetPriority(prio + 1); - test_assert(p1 == prio, "unexpected returned priority level"); - test_assert(chThdGetSelfX()->prio == prio + 2, "unexpected priority level"); - test_assert(chThdGetSelfX()->realprio == prio + 1, "unexpected returned real priority level"); - } - test_end_step(2); - - /* [4.4.3] Raising thread priority above the boosted level.*/ - test_set_step(3); - { - p1 = chThdSetPriority(prio + 3); - test_assert(p1 == prio + 1, "unexpected returned priority level"); - test_assert(chThdGetSelfX()->prio == prio + 3, "unexpected priority level"); - test_assert(chThdGetSelfX()->realprio == prio + 3, "unexpected real priority level"); - } - test_end_step(3); - - /* [4.4.4] Restoring original conditions.*/ - test_set_step(4); - { - chSysLock(); - chThdGetSelfX()->prio = prio; - chThdGetSelfX()->realprio = prio; - chSysUnlock(); - } - test_end_step(4); -} - -static const testcase_t rt_test_004_004 = { - "Priority change test with Priority Inheritance", - NULL, - NULL, - rt_test_004_004_execute -}; -#endif /* CH_CFG_USE_MUTEXES */ - /**************************************************************************** * Exported data. ****************************************************************************/ @@ -348,18 +94,13 @@ static const testcase_t rt_test_004_004 = { */ const testcase_t * const rt_test_sequence_004_array[] = { &rt_test_004_001, - &rt_test_004_002, - &rt_test_004_003, -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) - &rt_test_004_004, -#endif NULL }; /** - * @brief Threads Functionality. + * @brief Time Stamps Functionality. */ const testsequence_t rt_test_sequence_004 = { - "Threads Functionality", + "Time Stamps Functionality", rt_test_sequence_004_array }; diff --git a/test/rt/source/test/rt_test_sequence_005.c b/test/rt/source/test/rt_test_sequence_005.c index 6576d5700..5b1ceb863 100644 --- a/test/rt/source/test/rt_test_sequence_005.c +++ b/test/rt/source/test/rt_test_sequence_005.c @@ -21,16 +21,19 @@ * @file rt_test_sequence_005.c * @brief Test Sequence 005 code. * - * @page rt_test_sequence_005 [5] Suspend/Resume + * @page rt_test_sequence_005 [5] Threads Functionality * * File: @ref rt_test_sequence_005.c * *

Description

* This sequence tests the ChibiOS/RT functionalities related to - * threads suspend/resume. + * threading. * *

Test Cases

* - @subpage rt_test_005_001 + * - @subpage rt_test_005_002 + * - @subpage rt_test_005_003 + * - @subpage rt_test_005_004 * . */ @@ -38,14 +41,8 @@ * Shared code. ****************************************************************************/ -static thread_reference_t tr1; +static THD_FUNCTION(thread, p) { -static THD_FUNCTION(thread1, p) { - - chSysLock(); - chThdResumeI(&tr1, MSG_OK); - chSchRescheduleS(); - chSysUnlock(); test_emit_token(*(char *)p); } @@ -54,70 +51,294 @@ static THD_FUNCTION(thread1, p) { ****************************************************************************/ /** - * @page rt_test_005_001 [5.1] Suspend and Resume functionality + * @page rt_test_005_001 [5.1] Thread Sleep functionality * *

Description

- * The functionality of chThdSuspendTimeoutS() and chThdResumeI() is - * tested. + * The functionality of @p chThdSleep() and derivatives is tested. * *

Test Steps

- * - [5.1.1] The function chThdSuspendTimeoutS() is invoked, the thread - * is remotely resumed with message @p MSG_OK. On return the message - * and the state of the reference are tested. - * - [5.1.2] The function chThdSuspendTimeoutS() is invoked, the thread - * is not resumed so a timeout must occur. On return the message and - * the state of the reference are tested. + * - [5.1.1] The current system time is read then a sleep is performed + * for 100 system ticks and on exit the system time is verified + * again. + * - [5.1.2] The current system time is read then a sleep is performed + * for 100000 microseconds and on exit the system time is verified + * again. + * - [5.1.3] The current system time is read then a sleep is performed + * for 100 milliseconds and on exit the system time is verified + * again. + * - [5.1.4] The current system time is read then a sleep is performed + * for 1 second and on exit the system time is verified again. + * - [5.1.5] Function chThdSleepUntil() is tested with a timeline of + * "now" + 100 ticks. * . */ -static void rt_test_005_001_setup(void) { - tr1 = NULL; -} - static void rt_test_005_001_execute(void) { systime_t time; - msg_t msg; - /* [5.1.1] The function chThdSuspendTimeoutS() is invoked, the thread - is remotely resumed with message @p MSG_OK. On return the message - and the state of the reference are tested.*/ + /* [5.1.1] The current system time is read then a sleep is performed + for 100 system ticks and on exit the system time is verified + again.*/ test_set_step(1); { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, thread1, "A"); - chSysLock(); - msg = chThdSuspendTimeoutS(&tr1, TIME_INFINITE); - chSysUnlock(); - test_assert(NULL == tr1, "not NULL"); - test_assert(MSG_OK == msg,"wrong returned message"); - test_wait_threads(); + time = chVTGetSystemTimeX(); + chThdSleep(100); + test_assert_time_window(chTimeAddX(time, 100), + chTimeAddX(time, 100 + CH_CFG_ST_TIMEDELTA + 1), + "out of time window"); } test_end_step(1); - /* [5.1.2] The function chThdSuspendTimeoutS() is invoked, the thread - is not resumed so a timeout must occur. On return the message and - the state of the reference are tested.*/ + /* [5.1.2] The current system time is read then a sleep is performed + for 100000 microseconds and on exit the system time is verified + again.*/ test_set_step(2); { - chSysLock(); time = chVTGetSystemTimeX(); - msg = chThdSuspendTimeoutS(&tr1, TIME_MS2I(1000)); - chSysUnlock(); - test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), - chTimeAddX(time, TIME_MS2I(1000) + CH_CFG_ST_TIMEDELTA + 1), + chThdSleepMicroseconds(100000); + test_assert_time_window(chTimeAddX(time, TIME_US2I(100000)), + chTimeAddX(time, TIME_US2I(100000) + CH_CFG_ST_TIMEDELTA + 1), "out of time window"); - test_assert(NULL == tr1, "not NULL"); - test_assert(MSG_TIMEOUT == msg, "wrong returned message"); + } + test_end_step(2); + + /* [5.1.3] The current system time is read then a sleep is performed + for 100 milliseconds and on exit the system time is verified + again.*/ + test_set_step(3); + { + time = chVTGetSystemTimeX(); + chThdSleepMilliseconds(100); + test_assert_time_window(chTimeAddX(time, TIME_MS2I(100)), + chTimeAddX(time, TIME_MS2I(100) + CH_CFG_ST_TIMEDELTA + 1), + "out of time window"); + } + test_end_step(3); + + /* [5.1.4] The current system time is read then a sleep is performed + for 1 second and on exit the system time is verified again.*/ + test_set_step(4); + { + time = chVTGetSystemTimeX(); + chThdSleepSeconds(1); + test_assert_time_window(chTimeAddX(time, TIME_S2I(1)), + chTimeAddX(time, TIME_S2I(1) + CH_CFG_ST_TIMEDELTA + 1), + "out of time window"); + } + test_end_step(4); + + /* [5.1.5] Function chThdSleepUntil() is tested with a timeline of + "now" + 100 ticks.*/ + test_set_step(5); + { + time = chVTGetSystemTimeX(); + chThdSleepUntil(chTimeAddX(time, 100)); + test_assert_time_window(chTimeAddX(time, 100), + chTimeAddX(time, 100 + CH_CFG_ST_TIMEDELTA + 1), + "out of time window"); + } + test_end_step(5); +} + +static const testcase_t rt_test_005_001 = { + "Thread Sleep functionality", + NULL, + NULL, + rt_test_005_001_execute +}; + +/** + * @page rt_test_005_002 [5.2] Ready List functionality, threads priority order + * + *

Description

+ * Five threads, are enqueued in the ready list and atomically + * executed. The test expects the threads to perform their operations + * in correct priority order regardless of the initial order. + * + *

Test Steps

+ * - [5.2.1] Creating 5 threads with increasing priority, execution + * sequence is tested. + * - [5.2.2] Creating 5 threads with decreasing priority, execution + * sequence is tested. + * - [5.2.3] Creating 5 threads with pseudo-random priority, execution + * sequence is tested. + * . + */ + +static void rt_test_005_002_execute(void) { + + /* [5.2.1] Creating 5 threads with increasing priority, execution + sequence is tested.*/ + test_set_step(1); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E"); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D"); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C"); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B"); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A"); + test_wait_threads(); + test_assert_sequence("ABCDE", "invalid sequence"); + } + test_end_step(1); + + /* [5.2.2] Creating 5 threads with decreasing priority, execution + sequence is tested.*/ + test_set_step(2); + { + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A"); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B"); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C"); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D"); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E"); + test_wait_threads(); + test_assert_sequence("ABCDE", "invalid sequence"); + } + test_end_step(2); + + /* [5.2.3] Creating 5 threads with pseudo-random priority, execution + sequence is tested.*/ + test_set_step(3); + { + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D"); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E"); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A"); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B"); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C"); + test_wait_threads(); + test_assert_sequence("ABCDE", "invalid sequence"); + } + test_end_step(3); +} + +static const testcase_t rt_test_005_002 = { + "Ready List functionality, threads priority order", + NULL, + NULL, + rt_test_005_002_execute +}; + +/** + * @page rt_test_005_003 [5.3] Priority change test + * + *

Description

+ * A series of priority changes are performed on the current thread in + * order to verify that the priority change happens as expected. + * + *

Test Steps

+ * - [5.3.1] Thread priority is increased by one then a check is + * performed. + * - [5.3.2] Thread priority is returned to the previous value then a + * check is performed. + * . + */ + +static void rt_test_005_003_execute(void) { + tprio_t prio, p1; + + /* [5.3.1] Thread priority is increased by one then a check is + performed.*/ + test_set_step(1); + { + prio = chThdGetPriorityX(); + p1 = chThdSetPriority(prio + 1); + test_assert(p1 == prio, "unexpected returned priority level"); + test_assert(chThdGetPriorityX() == prio + 1, "unexpected priority level"); + } + test_end_step(1); + + /* [5.3.2] Thread priority is returned to the previous value then a + check is performed.*/ + test_set_step(2); + { + p1 = chThdSetPriority(p1); + test_assert(p1 == prio + 1, "unexpected returned priority level"); + test_assert(chThdGetPriorityX() == prio, "unexpected priority level"); } test_end_step(2); } -static const testcase_t rt_test_005_001 = { - "Suspend and Resume functionality", - rt_test_005_001_setup, +static const testcase_t rt_test_005_003 = { + "Priority change test", NULL, - rt_test_005_001_execute + NULL, + rt_test_005_003_execute }; +#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) +/** + * @page rt_test_005_004 [5.4] Priority change test with Priority Inheritance + * + *

Description

+ * A series of priority changes are performed on the current thread in + * order to verify that the priority change happens as expected. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_MUTEXES + * . + * + *

Test Steps

+ * - [5.4.1] Simulating a priority boost situation (prio > realprio). + * - [5.4.2] Raising thread priority above original priority but below + * the boosted level. + * - [5.4.3] Raising thread priority above the boosted level. + * - [5.4.4] Restoring original conditions. + * . + */ + +static void rt_test_005_004_execute(void) { + tprio_t prio, p1; + + /* [5.4.1] Simulating a priority boost situation (prio > realprio).*/ + test_set_step(1); + { + prio = chThdGetPriorityX(); + chThdGetSelfX()->prio += 2; + test_assert(chThdGetPriorityX() == prio + 2, "unexpected priority level"); + } + test_end_step(1); + + /* [5.4.2] Raising thread priority above original priority but below + the boosted level.*/ + test_set_step(2); + { + p1 = chThdSetPriority(prio + 1); + test_assert(p1 == prio, "unexpected returned priority level"); + test_assert(chThdGetSelfX()->prio == prio + 2, "unexpected priority level"); + test_assert(chThdGetSelfX()->realprio == prio + 1, "unexpected returned real priority level"); + } + test_end_step(2); + + /* [5.4.3] Raising thread priority above the boosted level.*/ + test_set_step(3); + { + p1 = chThdSetPriority(prio + 3); + test_assert(p1 == prio + 1, "unexpected returned priority level"); + test_assert(chThdGetSelfX()->prio == prio + 3, "unexpected priority level"); + test_assert(chThdGetSelfX()->realprio == prio + 3, "unexpected real priority level"); + } + test_end_step(3); + + /* [5.4.4] Restoring original conditions.*/ + test_set_step(4); + { + chSysLock(); + chThdGetSelfX()->prio = prio; + chThdGetSelfX()->realprio = prio; + chSysUnlock(); + } + test_end_step(4); +} + +static const testcase_t rt_test_005_004 = { + "Priority change test with Priority Inheritance", + NULL, + NULL, + rt_test_005_004_execute +}; +#endif /* CH_CFG_USE_MUTEXES */ + /**************************************************************************** * Exported data. ****************************************************************************/ @@ -127,13 +348,18 @@ static const testcase_t rt_test_005_001 = { */ const testcase_t * const rt_test_sequence_005_array[] = { &rt_test_005_001, + &rt_test_005_002, + &rt_test_005_003, +#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) + &rt_test_005_004, +#endif NULL }; /** - * @brief Suspend/Resume. + * @brief Threads Functionality. */ const testsequence_t rt_test_sequence_005 = { - "Suspend/Resume", + "Threads Functionality", rt_test_sequence_005_array }; diff --git a/test/rt/source/test/rt_test_sequence_006.c b/test/rt/source/test/rt_test_sequence_006.c index cf4db61b7..3bfb63bc5 100644 --- a/test/rt/source/test/rt_test_sequence_006.c +++ b/test/rt/source/test/rt_test_sequence_006.c @@ -21,66 +21,32 @@ * @file rt_test_sequence_006.c * @brief Test Sequence 006 code. * - * @page rt_test_sequence_006 [6] Counter Semaphores + * @page rt_test_sequence_006 [6] Suspend/Resume * * File: @ref rt_test_sequence_006.c * *

Description

* This sequence tests the ChibiOS/RT functionalities related to - * counter semaphores. - * - *

Conditions

- * This sequence is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_SEMAPHORES - * . + * threads suspend/resume. * *

Test Cases

* - @subpage rt_test_006_001 - * - @subpage rt_test_006_002 - * - @subpage rt_test_006_003 - * - @subpage rt_test_006_004 - * - @subpage rt_test_006_005 - * - @subpage rt_test_006_006 * . */ -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) - /**************************************************************************** * Shared code. ****************************************************************************/ -#include "ch.h" - -static semaphore_t sem1; +static thread_reference_t tr1; static THD_FUNCTION(thread1, p) { - chSemWait(&sem1); - test_emit_token(*(char *)p); -} - -static THD_FUNCTION(thread2, p) { - - (void)p; - chThdSleepMilliseconds(50); chSysLock(); - chSemSignalI(&sem1); /* For coverage reasons */ + chThdResumeI(&tr1, MSG_OK); chSchRescheduleS(); chSysUnlock(); -} - -static THD_FUNCTION(thread3, p) { - - (void)p; - chSemWait(&sem1); - chSemSignal(&sem1); -} - -static THD_FUNCTION(thread4, p) { - - chBSemSignal((binary_semaphore_t *)p); + test_emit_token(*(char *)p); } /**************************************************************************** @@ -88,423 +54,68 @@ static THD_FUNCTION(thread4, p) { ****************************************************************************/ /** - * @page rt_test_006_001 [6.1] Semaphore primitives, no state change + * @page rt_test_006_001 [6.1] Suspend and Resume functionality * *

Description

- * Wait, Signal and Reset primitives are tested. The testing thread - * does not trigger a state change. + * The functionality of chThdSuspendTimeoutS() and chThdResumeI() is + * tested. * *

Test Steps

- * - [6.1.1] The function chSemWait() is invoked, after return the - * counter and the returned message are tested. - * - [6.1.2] The function chSemSignal() is invoked, after return the - * counter is tested. - * - [6.1.3] The function chSemReset() is invoked, after return the - * counter is tested. + * - [6.1.1] The function chThdSuspendTimeoutS() is invoked, the thread + * is remotely resumed with message @p MSG_OK. On return the message + * and the state of the reference are tested. + * - [6.1.2] The function chThdSuspendTimeoutS() is invoked, the thread + * is not resumed so a timeout must occur. On return the message and + * the state of the reference are tested. * . */ static void rt_test_006_001_setup(void) { - chSemObjectInit(&sem1, 1); -} - -static void rt_test_006_001_teardown(void) { - chSemReset(&sem1, 0); + tr1 = NULL; } static void rt_test_006_001_execute(void) { - - /* [6.1.1] The function chSemWait() is invoked, after return the - counter and the returned message are tested.*/ - test_set_step(1); - { - msg_t msg; - - msg = chSemWait(&sem1); - test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value"); - test_assert(MSG_OK == msg, "wrong returned message"); - } - test_end_step(1); - - /* [6.1.2] The function chSemSignal() is invoked, after return the - counter is tested.*/ - test_set_step(2); - { - chSemSignal(&sem1); - test_assert_lock(chSemGetCounterI(&sem1) == 1, "wrong counter value"); - } - test_end_step(2); - - /* [6.1.3] The function chSemReset() is invoked, after return the - counter is tested.*/ - test_set_step(3); - { - chSemReset(&sem1, 2); - test_assert_lock(chSemGetCounterI(&sem1) == 2, "wrong counter value"); - } - test_end_step(3); -} - -static const testcase_t rt_test_006_001 = { - "Semaphore primitives, no state change", - rt_test_006_001_setup, - rt_test_006_001_teardown, - rt_test_006_001_execute -}; - -/** - * @page rt_test_006_002 [6.2] Semaphore enqueuing test - * - *

Description

- * Five threads with randomized priorities are enqueued to a semaphore - * then awakened one at time. The test expects that the threads reach - * their goal in FIFO order or priority order depending on the @p - * CH_CFG_USE_SEMAPHORES_PRIORITY configuration setting. - * - *

Test Steps

- * - [6.2.1] Five threads are created with mixed priority levels (not - * increasing nor decreasing). Threads enqueue on a semaphore - * initialized to zero. - * - [6.2.2] The semaphore is signaled 5 times. The thread activation - * sequence is tested. - * . - */ - -static void rt_test_006_002_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_006_002_execute(void) { - - /* [6.2.1] Five threads are created with mixed priority levels (not - increasing nor decreasing). Threads enqueue on a semaphore - initialized to zero.*/ - test_set_step(1); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+5, thread1, "A"); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()+1, thread1, "B"); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()+3, thread1, "C"); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()+4, thread1, "D"); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()+2, thread1, "E"); - } - test_end_step(1); - - /* [6.2.2] The semaphore is signaled 5 times. The thread activation - sequence is tested.*/ - test_set_step(2); - { - chSemSignal(&sem1); - chSemSignal(&sem1); - chSemSignal(&sem1); - chSemSignal(&sem1); - chSemSignal(&sem1); - test_wait_threads(); -#if CH_CFG_USE_SEMAPHORES_PRIORITY - test_assert_sequence("ADCEB", "invalid sequence"); -#else - test_assert_sequence("ABCDE", "invalid sequence"); -#endif - } - test_end_step(2); -} - -static const testcase_t rt_test_006_002 = { - "Semaphore enqueuing test", - rt_test_006_002_setup, - NULL, - rt_test_006_002_execute -}; - -/** - * @page rt_test_006_003 [6.3] Semaphore timeout test - * - *

Description

- * The three possible semaphore waiting modes (do not wait, wait with - * timeout, wait without timeout) are explored. The test expects that - * the semaphore wait function returns the correct value in each of the - * above scenario and that the semaphore structure status is correct - * after each operation. - * - *

Test Steps

- * - [6.3.1] Testing special case TIME_IMMEDIATE. - * - [6.3.2] Testing non-timeout condition. - * - [6.3.3] Testing timeout condition. - * . - */ - -static void rt_test_006_003_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_006_003_execute(void) { - unsigned i; - systime_t target_time; + systime_t time; msg_t msg; - /* [6.3.1] Testing special case TIME_IMMEDIATE.*/ + /* [6.1.1] The function chThdSuspendTimeoutS() is invoked, the thread + is remotely resumed with message @p MSG_OK. On return the message + and the state of the reference are tested.*/ test_set_step(1); { - msg = chSemWaitTimeout(&sem1, TIME_IMMEDIATE); - test_assert(msg == MSG_TIMEOUT, "wrong wake-up message"); - test_assert(queue_isempty(&sem1.queue), "queue not empty"); - test_assert(sem1.cnt == 0, "counter not zero"); - } - test_end_step(1); - - /* [6.3.2] Testing non-timeout condition.*/ - test_set_step(2); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, - thread2, 0); - msg = chSemWaitTimeout(&sem1, TIME_MS2I(500)); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, thread1, "A"); + chSysLock(); + msg = chThdSuspendTimeoutS(&tr1, TIME_INFINITE); + chSysUnlock(); + test_assert(NULL == tr1, "not NULL"); + test_assert(MSG_OK == msg,"wrong returned message"); test_wait_threads(); - test_assert(msg == MSG_OK, "wrong wake-up message"); - test_assert(queue_isempty(&sem1.queue), "queue not empty"); - test_assert(sem1.cnt == 0, "counter not zero"); - } - test_end_step(2); - - /* [6.3.3] Testing timeout condition.*/ - test_set_step(3); - { - target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(5 * 50)); - for (i = 0; i < 5; i++) { - test_emit_token('A' + i); - msg = chSemWaitTimeout(&sem1, TIME_MS2I(50)); - test_assert(msg == MSG_TIMEOUT, "wrong wake-up message"); - test_assert(queue_isempty(&sem1.queue), "queue not empty"); - test_assert(sem1.cnt == 0, "counter not zero"); - } - test_assert_sequence("ABCDE", "invalid sequence"); - test_assert_time_window(target_time, - chTimeAddX(target_time, ALLOWED_DELAY), - "out of time window"); - } - test_end_step(3); -} - -static const testcase_t rt_test_006_003 = { - "Semaphore timeout test", - rt_test_006_003_setup, - NULL, - rt_test_006_003_execute -}; - -/** - * @page rt_test_006_004 [6.4] Testing chSemAddCounterI() functionality - * - *

Description

- * The functon is tested by waking up a thread then the semaphore - * counter value is tested. - * - *

Test Steps

- * - [6.4.1] A thread is created, it goes to wait on the semaphore. - * - [6.4.2] The semaphore counter is increased by two, it is then - * tested to be one, the thread must have completed. - * . - */ - -static void rt_test_006_004_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_006_004_execute(void) { - - /* [6.4.1] A thread is created, it goes to wait on the semaphore.*/ - test_set_step(1); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, thread1, "A"); } test_end_step(1); - /* [6.4.2] The semaphore counter is increased by two, it is then - tested to be one, the thread must have completed.*/ + /* [6.1.2] The function chThdSuspendTimeoutS() is invoked, the thread + is not resumed so a timeout must occur. On return the message and + the state of the reference are tested.*/ test_set_step(2); { chSysLock(); - chSemAddCounterI(&sem1, 2); - chSchRescheduleS(); + time = chVTGetSystemTimeX(); + msg = chThdSuspendTimeoutS(&tr1, TIME_MS2I(1000)); chSysUnlock(); - test_wait_threads(); - test_assert_lock(chSemGetCounterI(&sem1) == 1, "invalid counter"); - test_assert_sequence("A", "invalid sequence"); + test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), + chTimeAddX(time, TIME_MS2I(1000) + CH_CFG_ST_TIMEDELTA + 1), + "out of time window"); + test_assert(NULL == tr1, "not NULL"); + test_assert(MSG_TIMEOUT == msg, "wrong returned message"); } test_end_step(2); } -static const testcase_t rt_test_006_004 = { - "Testing chSemAddCounterI() functionality", - rt_test_006_004_setup, +static const testcase_t rt_test_006_001 = { + "Suspend and Resume functionality", + rt_test_006_001_setup, NULL, - rt_test_006_004_execute -}; - -/** - * @page rt_test_006_005 [6.5] Testing chSemWaitSignal() functionality - * - *

Description

- * This test case explicitly addresses the @p chSemWaitSignal() - * function. A thread is created that performs a wait and a signal - * operations. The tester thread is awakened from an atomic wait/signal - * operation. The test expects that the semaphore wait function returns - * the correct value in each of the above scenario and that the - * semaphore structure status is correct after each operation. - * - *

Test Steps

- * - [6.5.1] An higher priority thread is created that performs - * non-atomical wait and signal operations on a semaphore. - * - [6.5.2] The function chSemSignalWait() is invoked by specifying - * the same semaphore for the wait and signal phases. The counter - * value must be one on exit. - * - [6.5.3] The function chSemSignalWait() is invoked again by - * specifying the same semaphore for the wait and signal phases. The - * counter value must be one on exit. - * . - */ - -static void rt_test_006_005_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_006_005_teardown(void) { - test_wait_threads(); -} - -static void rt_test_006_005_execute(void) { - - /* [6.5.1] An higher priority thread is created that performs - non-atomical wait and signal operations on a semaphore.*/ - test_set_step(1); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, thread3, 0); - } - test_end_step(1); - - /* [6.5.2] The function chSemSignalWait() is invoked by specifying - the same semaphore for the wait and signal phases. The counter - value must be one on exit.*/ - test_set_step(2); - { - chSemSignalWait(&sem1, &sem1); - test_assert(queue_isempty(&sem1.queue), "queue not empty"); - test_assert(sem1.cnt == 0, "counter not zero"); - } - test_end_step(2); - - /* [6.5.3] The function chSemSignalWait() is invoked again by - specifying the same semaphore for the wait and signal phases. The - counter value must be one on exit.*/ - test_set_step(3); - { - chSemSignalWait(&sem1, &sem1); - test_assert(queue_isempty(&sem1.queue), "queue not empty"); - test_assert(sem1.cnt == 0, "counter not zero"); - } - test_end_step(3); -} - -static const testcase_t rt_test_006_005 = { - "Testing chSemWaitSignal() functionality", - rt_test_006_005_setup, - rt_test_006_005_teardown, - rt_test_006_005_execute -}; - -/** - * @page rt_test_006_006 [6.6] Testing Binary Semaphores special case - * - *

Description

- * This test case tests the binary semaphores functionality. The test - * both checks the binary semaphore status and the expected status of - * the underlying counting semaphore. - * - *

Test Steps

- * - [6.6.1] Creating a binary semaphore in "taken" state, the state is - * checked. - * - [6.6.2] Resetting the binary semaphore in "taken" state, the state - * must not change. - * - [6.6.3] Starting a signaler thread at a lower priority. - * - [6.6.4] Waiting for the binary semaphore to be signaled, the - * semaphore is expected to be taken. - * - [6.6.5] Signaling the binary semaphore, checking the binary - * semaphore state to be "not taken" and the underlying counter - * semaphore counter to be one. - * - [6.6.6] Signaling the binary semaphore again, the internal state - * must not change from "not taken". - * . - */ - -static void rt_test_006_006_teardown(void) { - test_wait_threads(); -} - -static void rt_test_006_006_execute(void) { - binary_semaphore_t bsem; - msg_t msg; - - /* [6.6.1] Creating a binary semaphore in "taken" state, the state is - checked.*/ - test_set_step(1); - { - chBSemObjectInit(&bsem, true); - test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken"); - } - test_end_step(1); - - /* [6.6.2] Resetting the binary semaphore in "taken" state, the state - must not change.*/ - test_set_step(2); - { - chBSemReset(&bsem, true); - test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken"); - } - test_end_step(2); - - /* [6.6.3] Starting a signaler thread at a lower priority.*/ - test_set_step(3); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, - chThdGetPriorityX()-1, thread4, &bsem); - } - test_end_step(3); - - /* [6.6.4] Waiting for the binary semaphore to be signaled, the - semaphore is expected to be taken.*/ - test_set_step(4); - { - msg = chBSemWait(&bsem); - test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken"); - test_assert(msg == MSG_OK, "unexpected message"); - } - test_end_step(4); - - /* [6.6.5] Signaling the binary semaphore, checking the binary - semaphore state to be "not taken" and the underlying counter - semaphore counter to be one.*/ - test_set_step(5); - { - chBSemSignal(&bsem); - test_assert_lock(chBSemGetStateI(&bsem) ==false, "still taken"); - test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter"); - } - test_end_step(5); - - /* [6.6.6] Signaling the binary semaphore again, the internal state - must not change from "not taken".*/ - test_set_step(6); - { - chBSemSignal(&bsem); - test_assert_lock(chBSemGetStateI(&bsem) == false, "taken"); - test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter"); - } - test_end_step(6); -} - -static const testcase_t rt_test_006_006 = { - "Testing Binary Semaphores special case", - NULL, - rt_test_006_006_teardown, - rt_test_006_006_execute + rt_test_006_001_execute }; /**************************************************************************** @@ -516,20 +127,13 @@ static const testcase_t rt_test_006_006 = { */ const testcase_t * const rt_test_sequence_006_array[] = { &rt_test_006_001, - &rt_test_006_002, - &rt_test_006_003, - &rt_test_006_004, - &rt_test_006_005, - &rt_test_006_006, NULL }; /** - * @brief Counter Semaphores. + * @brief Suspend/Resume. */ const testsequence_t rt_test_sequence_006 = { - "Counter Semaphores", + "Suspend/Resume", rt_test_sequence_006_array }; - -#endif /* CH_CFG_USE_SEMAPHORES */ diff --git a/test/rt/source/test/rt_test_sequence_007.c b/test/rt/source/test/rt_test_sequence_007.c index aa717c9bf..f226b351d 100644 --- a/test/rt/source/test/rt_test_sequence_007.c +++ b/test/rt/source/test/rt_test_sequence_007.c @@ -21,18 +21,18 @@ * @file rt_test_sequence_007.c * @brief Test Sequence 007 code. * - * @page rt_test_sequence_007 [7] Mutexes, Condition Variables and Priority Inheritance + * @page rt_test_sequence_007 [7] Counter Semaphores * * File: @ref rt_test_sequence_007.c * *

Description

* This sequence tests the ChibiOS/RT functionalities related to - * mutexes, condition variables and priority inheritance algorithm. + * counter semaphores. * *

Conditions

* This sequence is only executed if the following preprocessor condition * evaluates to true: - * - CH_CFG_USE_MUTEXES + * - CH_CFG_USE_SEMAPHORES * . * *

Test Cases

@@ -42,1041 +42,470 @@ * - @subpage rt_test_007_004 * - @subpage rt_test_007_005 * - @subpage rt_test_007_006 - * - @subpage rt_test_007_007 - * - @subpage rt_test_007_008 - * - @subpage rt_test_007_009 * . */ -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) /**************************************************************************** * Shared code. ****************************************************************************/ -static MUTEX_DECL(m1); -static MUTEX_DECL(m2); -#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__) -static CONDVAR_DECL(c1); -#endif +#include "ch.h" -#if CH_DBG_THREADS_PROFILING || defined(__DOXYGEN__) -/** - * @brief CPU pulse. - * @note The current implementation is not totally reliable. - * - * @param[in] duration CPU pulse duration in milliseconds - */ -void test_cpu_pulse(unsigned duration) { - systime_t start, end, now; - - start = chThdGetTicksX(chThdGetSelfX()); - end = chTimeAddX(start, TIME_MS2I(duration)); - do { - now = chThdGetTicksX(chThdGetSelfX()); -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } - while (chTimeIsInRangeX(now, start, end)); -} -#endif /* CH_DBG_THREADS_PROFILING */ +static semaphore_t sem1; static THD_FUNCTION(thread1, p) { - chMtxLock(&m1); + chSemWait(&sem1); test_emit_token(*(char *)p); - chMtxUnlock(&m1); } -#if CH_DBG_THREADS_PROFILING || defined(__DOXYGEN__) -/* Low priority thread */ -static THD_FUNCTION(thread2L, p) { - - (void)p; - chMtxLock(&m1); - test_cpu_pulse(40); - chMtxUnlock(&m1); - test_cpu_pulse(10); - test_emit_token('C'); -} - -/* Medium priority thread */ -static THD_FUNCTION(thread2M, p) { - - (void)p; - chThdSleepMilliseconds(20); - test_cpu_pulse(40); - test_emit_token('B'); -} - -/* High priority thread */ -static THD_FUNCTION(thread2H, p) { - - (void)p; - chThdSleepMilliseconds(40); - chMtxLock(&m1); - test_cpu_pulse(10); - chMtxUnlock(&m1); - test_emit_token('A'); -} - -/* Lowest priority thread */ -static THD_FUNCTION(thread3LL, p) { - - (void)p; - chMtxLock(&m1); - test_cpu_pulse(30); - chMtxUnlock(&m1); - test_emit_token('E'); -} - -/* Low priority thread */ -static THD_FUNCTION(thread3L, p) { - - (void)p; - chThdSleepMilliseconds(10); - chMtxLock(&m2); - test_cpu_pulse(20); - chMtxLock(&m1); - test_cpu_pulse(10); - chMtxUnlock(&m1); - test_cpu_pulse(10); - chMtxUnlock(&m2); - test_emit_token('D'); -} - -/* Medium priority thread */ -static THD_FUNCTION(thread3M, p) { - - (void)p; - chThdSleepMilliseconds(20); - chMtxLock(&m2); - test_cpu_pulse(10); - chMtxUnlock(&m2); - test_emit_token('C'); -} - -/* High priority thread */ -static THD_FUNCTION(thread3H, p) { - - (void)p; - chThdSleepMilliseconds(40); - test_cpu_pulse(20); - test_emit_token('B'); -} - -/* Highest priority thread */ -static THD_FUNCTION(thread3HH, p) { +static THD_FUNCTION(thread2, p) { (void)p; chThdSleepMilliseconds(50); - chMtxLock(&m2); - test_cpu_pulse(10); - chMtxUnlock(&m2); - test_emit_token('A'); -} -#endif /* CH_DBG_THREADS_PROFILING */ - -static THD_FUNCTION(thread4A, p) { - - (void)p; - chThdSleepMilliseconds(50); - chMtxLock(&m1); - chMtxUnlock(&m1); -} - -static THD_FUNCTION(thread4B, p) { - - (void)p; - chThdSleepMilliseconds(150); chSysLock(); - chMtxLockS(&m2); /* For coverage of the chMtxLockS() function variant.*/ - chMtxUnlockS(&m2); /* For coverage of the chMtxUnlockS() function variant.*/ + chSemSignalI(&sem1); /* For coverage reasons */ chSchRescheduleS(); chSysUnlock(); } -#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__) -static THD_FUNCTION(thread6, p) { +static THD_FUNCTION(thread3, p) { - chMtxLock(&m1); - chCondWait(&c1); - test_emit_token(*(char *)p); - chMtxUnlock(&m1); + (void)p; + chSemWait(&sem1); + chSemSignal(&sem1); } -static THD_FUNCTION(thread8, p) { +static THD_FUNCTION(thread4, p) { - chMtxLock(&m2); - chMtxLock(&m1); -#if CH_CFG_USE_CONDVARS_TIMEOUT || defined(__DOXYGEN__) - chCondWaitTimeout(&c1, TIME_INFINITE); -#else - chCondWait(&c1); -#endif - test_emit_token(*(char *)p); - chMtxUnlock(&m1); - chMtxUnlock(&m2); + chBSemSignal((binary_semaphore_t *)p); } -static THD_FUNCTION(thread9, p) { - - chMtxLock(&m2); - test_emit_token(*(char *)p); - chMtxUnlock(&m2); -} -#endif /* CH_CFG_USE_CONDVARS */ - /**************************************************************************** * Test cases. ****************************************************************************/ /** - * @page rt_test_007_001 [7.1] Priority enqueuing test + * @page rt_test_007_001 [7.1] Semaphore primitives, no state change * *

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. + * Wait, Signal and Reset primitives are tested. The testing thread + * does not trigger a state change. * *

Test Steps

- * - [7.1.1] Getting the initial priority. - * - [7.1.2] Locking the mutex. - * - [7.1.3] Five threads are created that try to lock and unlock the - * mutex then terminate. The threads are created in ascending - * priority order. - * - [7.1.4] Unlocking the mutex, the threads will wakeup in priority - * order because the mutext queue is an ordered one. + * - [7.1.1] The function chSemWait() is invoked, after return the + * counter and the returned message are tested. + * - [7.1.2] The function chSemSignal() is invoked, after return the + * counter is tested. + * - [7.1.3] The function chSemReset() is invoked, after return the + * counter is tested. * . */ static void rt_test_007_001_setup(void) { - chMtxObjectInit(&m1); + chSemObjectInit(&sem1, 1); +} + +static void rt_test_007_001_teardown(void) { + chSemReset(&sem1, 0); } static void rt_test_007_001_execute(void) { - tprio_t prio; - /* [7.1.1] Getting the initial priority.*/ + /* [7.1.1] The function chSemWait() is invoked, after return the + counter and the returned message are tested.*/ test_set_step(1); { - prio = chThdGetPriorityX(); + msg_t msg; + + msg = chSemWait(&sem1); + test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value"); + test_assert(MSG_OK == msg, "wrong returned message"); } test_end_step(1); - /* [7.1.2] Locking the mutex.*/ + /* [7.1.2] The function chSemSignal() is invoked, after return the + counter is tested.*/ test_set_step(2); { - chMtxLock(&m1); + chSemSignal(&sem1); + test_assert_lock(chSemGetCounterI(&sem1) == 1, "wrong counter value"); } test_end_step(2); - /* [7.1.3] Five threads are created that try to lock and unlock the - mutex then terminate. The threads are created in ascending - priority order.*/ + /* [7.1.3] The function chSemReset() is invoked, after return the + counter is tested.*/ test_set_step(3); { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread1, "E"); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread1, "D"); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread1, "C"); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread1, "B"); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread1, "A"); + chSemReset(&sem1, 2); + test_assert_lock(chSemGetCounterI(&sem1) == 2, "wrong counter value"); } test_end_step(3); - - /* [7.1.4] Unlocking the mutex, the threads will wakeup in priority - order because the mutext queue is an ordered one.*/ - test_set_step(4); - { - chMtxUnlock(&m1); - test_wait_threads(); - test_assert(prio == chThdGetPriorityX(), "wrong priority level"); - test_assert_sequence("ABCDE", "invalid sequence"); - } - test_end_step(4); } static const testcase_t rt_test_007_001 = { - "Priority enqueuing test", + "Semaphore primitives, no state change", rt_test_007_001_setup, - NULL, + rt_test_007_001_teardown, rt_test_007_001_execute }; -#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) /** - * @page rt_test_007_002 [7.2] Priority inheritance, simple case + * @page rt_test_007_002 [7.2] Semaphore enqueuing test * *

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 reach their goal in increasing priority - * order by rearranging their priorities in order to avoid the priority - * inversion trap. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_DBG_THREADS_PROFILING - * . + * Five threads with randomized priorities are enqueued to a semaphore + * then awakened one at time. The test expects that the threads reach + * their goal in FIFO order or priority order depending on the @p + * CH_CFG_USE_SEMAPHORES_PRIORITY configuration setting. * *

Test Steps

- * - [7.2.1] Getting the system time for test duration measurement. - * - [7.2.2] The three contenders threads are created and let run - * atomically, the goals sequence is tested, the threads must - * complete in priority order. - * - [7.2.3] Testing that all threads completed within the specified - * time windows (100mS...100mS+ALLOWED_DELAY). + * - [7.2.1] Five threads are created with mixed priority levels (not + * increasing nor decreasing). Threads enqueue on a semaphore + * initialized to zero. + * - [7.2.2] The semaphore is signaled 5 times. The thread activation + * sequence is tested. * . */ static void rt_test_007_002_setup(void) { - chMtxObjectInit(&m1); + chSemObjectInit(&sem1, 0); } static void rt_test_007_002_execute(void) { - systime_t time; - /* [7.2.1] Getting the system time for test duration measurement.*/ + /* [7.2.1] Five threads are created with mixed priority levels (not + increasing nor decreasing). Threads enqueue on a semaphore + initialized to zero.*/ test_set_step(1); { - time = test_wait_tick(); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+5, thread1, "A"); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()+1, thread1, "B"); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()+3, thread1, "C"); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()+4, thread1, "D"); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()+2, thread1, "E"); } test_end_step(1); - /* [7.2.2] The three contenders threads are created and let run - atomically, the goals sequence is tested, the threads must - complete in priority order.*/ + /* [7.2.2] The semaphore is signaled 5 times. The thread activation + sequence is tested.*/ test_set_step(2); { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, thread2H, 0); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-2, thread2M, 0); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread2L, 0); + chSemSignal(&sem1); + chSemSignal(&sem1); + chSemSignal(&sem1); + chSemSignal(&sem1); + chSemSignal(&sem1); test_wait_threads(); - test_assert_sequence("ABC", "invalid sequence"); +#if CH_CFG_USE_SEMAPHORES_PRIORITY + test_assert_sequence("ADCEB", "invalid sequence"); +#else + test_assert_sequence("ABCDE", "invalid sequence"); +#endif } test_end_step(2); - - /* [7.2.3] Testing that all threads completed within the specified - time windows (100mS...100mS+ALLOWED_DELAY).*/ - test_set_step(3); - { - test_assert_time_window(chTimeAddX(time, TIME_MS2I(100)), - chTimeAddX(time, TIME_MS2I(100) + ALLOWED_DELAY), - "out of time window"); - } - test_end_step(3); } static const testcase_t rt_test_007_002 = { - "Priority inheritance, simple case", + "Semaphore enqueuing test", rt_test_007_002_setup, NULL, rt_test_007_002_execute }; -#endif /* CH_DBG_THREADS_PROFILING */ -#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) /** - * @page rt_test_007_003 [7.3] Priority inheritance, complex case + * @page rt_test_007_003 [7.3] Semaphore timeout test * *

Description

- * Five threads are involved in the complex priority inversion - * scenario, the priority inheritance algorithm is tested for depths - * greater than one. 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. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_DBG_THREADS_PROFILING - * . + * The three possible semaphore waiting modes (do not wait, wait with + * timeout, wait without timeout) are explored. The test expects that + * the semaphore wait function returns the correct value in each of the + * above scenario and that the semaphore structure status is correct + * after each operation. * *

Test Steps

- * - [7.3.1] Getting the system time for test duration measurement. - * - [7.3.2] The five contenders threads are created and let run - * atomically, the goals sequence is tested, the threads must - * complete in priority order. - * - [7.3.3] Testing that all threads completed within the specified - * time windows (110mS...110mS+ALLOWED_DELAY). + * - [7.3.1] Testing special case TIME_IMMEDIATE. + * - [7.3.2] Testing non-timeout condition. + * - [7.3.3] Testing timeout condition. * . */ static void rt_test_007_003_setup(void) { - chMtxObjectInit(&m1); /* Mutex B.*/ - chMtxObjectInit(&m2); /* Mutex A.*/ + chSemObjectInit(&sem1, 0); } static void rt_test_007_003_execute(void) { - systime_t time; + unsigned i; + systime_t target_time; + msg_t msg; - /* [7.3.1] Getting the system time for test duration measurement.*/ + /* [7.3.1] Testing special case TIME_IMMEDIATE.*/ test_set_step(1); { - time = test_wait_tick(); + msg = chSemWaitTimeout(&sem1, TIME_IMMEDIATE); + test_assert(msg == MSG_TIMEOUT, "wrong wake-up message"); + test_assert(queue_isempty(&sem1.queue), "queue not empty"); + test_assert(sem1.cnt == 0, "counter not zero"); } test_end_step(1); - /* [7.3.2] The five contenders threads are created and let run - atomically, the goals sequence is tested, the threads must - complete in priority order.*/ + /* [7.3.2] Testing non-timeout condition.*/ test_set_step(2); { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread3LL, 0); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread3L, 0); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread3M, 0); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread3H, 0); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread3HH, 0); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, + thread2, 0); + msg = chSemWaitTimeout(&sem1, TIME_MS2I(500)); test_wait_threads(); - test_assert_sequence("ABCDE", "invalid sequence"); + test_assert(msg == MSG_OK, "wrong wake-up message"); + test_assert(queue_isempty(&sem1.queue), "queue not empty"); + test_assert(sem1.cnt == 0, "counter not zero"); } test_end_step(2); - /* [7.3.3] Testing that all threads completed within the specified - time windows (110mS...110mS+ALLOWED_DELAY).*/ + /* [7.3.3] Testing timeout condition.*/ test_set_step(3); { - test_assert_time_window(chTimeAddX(time, TIME_MS2I(110)), - chTimeAddX(time, TIME_MS2I(110) + ALLOWED_DELAY), + target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(5 * 50)); + for (i = 0; i < 5; i++) { + test_emit_token('A' + i); + msg = chSemWaitTimeout(&sem1, TIME_MS2I(50)); + test_assert(msg == MSG_TIMEOUT, "wrong wake-up message"); + test_assert(queue_isempty(&sem1.queue), "queue not empty"); + test_assert(sem1.cnt == 0, "counter not zero"); + } + test_assert_sequence("ABCDE", "invalid sequence"); + test_assert_time_window(target_time, + chTimeAddX(target_time, ALLOWED_DELAY), "out of time window"); } test_end_step(3); } static const testcase_t rt_test_007_003 = { - "Priority inheritance, complex case", + "Semaphore timeout test", rt_test_007_003_setup, NULL, rt_test_007_003_execute }; -#endif /* CH_DBG_THREADS_PROFILING */ /** - * @page rt_test_007_004 [7.4] Priority return verification + * @page rt_test_007_004 [7.4] Testing chSemAddCounterI() functionality * *

Description

- * Two threads are spawned that try to lock the mutexes already locked - * by the tester thread with precise timing. The test expects that the - * priority changes caused by the priority inheritance algorithm happen - * at the right moment and with the right values.
Thread A performs - * wait(50), lock(m1), unlock(m1), exit. Thread B performs wait(150), - * lock(m2), unlock(m2), exit. + * The functon is tested by waking up a thread then the semaphore + * counter value is tested. * *

Test Steps

- * - [7.4.1] Getting current thread priority P(0) and assigning to the - * threads A and B priorities +1 and +2. - * - [7.4.2] Spawning threads A and B at priorities P(A) and P(B). - * - [7.4.3] Locking the mutex M1 before thread A has a chance to lock - * it. The priority must not change because A has not yet reached - * chMtxLock(M1). the mutex is not locked. - * - [7.4.4] Waiting 100mS, this makes thread A reach chMtxLock(M1) and - * get the mutex. This must boost the priority of the current thread - * at the same level of thread A. - * - [7.4.5] Locking the mutex M2 before thread B has a chance to lock - * it. The priority must not change because B has not yet reached - * chMtxLock(M2). the mutex is not locked. - * - [7.4.6] Waiting 100mS, this makes thread B reach chMtxLock(M2) and - * get the mutex. This must boost the priority of the current thread - * at the same level of thread B. - * - [7.4.7] Unlocking M2, the priority should fall back to P(A). - * - [7.4.8] Unlocking M1, the priority should fall back to P(0). + * - [7.4.1] A thread is created, it goes to wait on the semaphore. + * - [7.4.2] The semaphore counter is increased by two, it is then + * tested to be one, the thread must have completed. * . */ static void rt_test_007_004_setup(void) { - chMtxObjectInit(&m1); - chMtxObjectInit(&m2); -} - -static void rt_test_007_004_teardown(void) { - test_wait_threads(); + chSemObjectInit(&sem1, 0); } static void rt_test_007_004_execute(void) { - tprio_t p, pa, pb; - /* [7.4.1] Getting current thread priority P(0) and assigning to the - threads A and B priorities +1 and +2.*/ + /* [7.4.1] A thread is created, it goes to wait on the semaphore.*/ test_set_step(1); { - p = chThdGetPriorityX(); - pa = p + 1; - pb = p + 2; + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, thread1, "A"); } test_end_step(1); - /* [7.4.2] Spawning threads A and B at priorities P(A) and P(B).*/ + /* [7.4.2] The semaphore counter is increased by two, it is then + tested to be one, the thread must have completed.*/ test_set_step(2); { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, pa, thread4A, "A"); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, pb, thread4B, "B"); + chSysLock(); + chSemAddCounterI(&sem1, 2); + chSchRescheduleS(); + chSysUnlock(); + test_wait_threads(); + test_assert_lock(chSemGetCounterI(&sem1) == 1, "invalid counter"); + test_assert_sequence("A", "invalid sequence"); } test_end_step(2); - - /* [7.4.3] Locking the mutex M1 before thread A has a chance to lock - it. The priority must not change because A has not yet reached - chMtxLock(M1). the mutex is not locked.*/ - test_set_step(3); - { - chMtxLock(&m1); - test_assert(chThdGetPriorityX() == p, "wrong priority level"); - } - test_end_step(3); - - /* [7.4.4] Waiting 100mS, this makes thread A reach chMtxLock(M1) and - get the mutex. This must boost the priority of the current thread - at the same level of thread A.*/ - test_set_step(4); - { - chThdSleepMilliseconds(100); - test_assert(chThdGetPriorityX() == pa, "wrong priority level"); - } - test_end_step(4); - - /* [7.4.5] Locking the mutex M2 before thread B has a chance to lock - it. The priority must not change because B has not yet reached - chMtxLock(M2). the mutex is not locked.*/ - test_set_step(5); - { - chMtxLock(&m2); - test_assert(chThdGetPriorityX() == pa, "wrong priority level"); - } - test_end_step(5); - - /* [7.4.6] Waiting 100mS, this makes thread B reach chMtxLock(M2) and - get the mutex. This must boost the priority of the current thread - at the same level of thread B.*/ - test_set_step(6); - { - chThdSleepMilliseconds(100); - test_assert(chThdGetPriorityX() == pb, "wrong priority level"); - } - test_end_step(6); - - /* [7.4.7] Unlocking M2, the priority should fall back to P(A).*/ - test_set_step(7); - { - chMtxUnlock(&m2); - test_assert(chThdGetPriorityX() == pa, "wrong priority level"); - } - test_end_step(7); - - /* [7.4.8] Unlocking M1, the priority should fall back to P(0).*/ - test_set_step(8); - { - chMtxUnlock(&m1); - test_assert(chThdGetPriorityX() == p, "wrong priority level"); - } - test_end_step(8); } static const testcase_t rt_test_007_004 = { - "Priority return verification", + "Testing chSemAddCounterI() functionality", rt_test_007_004_setup, - rt_test_007_004_teardown, + NULL, rt_test_007_004_execute }; -#if (!CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) /** - * @page rt_test_007_005 [7.5] Repeated locks, non recursive scenario + * @page rt_test_007_005 [7.5] Testing chSemWaitSignal() functionality * *

Description

- * The behavior of multiple mutex locks from the same thread is tested - * when recursion is disabled. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - !CH_CFG_USE_MUTEXES_RECURSIVE - * . + * This test case explicitly addresses the @p chSemWaitSignal() + * function. A thread is created that performs a wait and a signal + * operations. The tester thread is awakened from an atomic wait/signal + * operation. The test expects that the semaphore wait function returns + * the correct value in each of the above scenario and that the + * semaphore structure status is correct after each operation. * *

Test Steps

- * - [7.5.1] Getting current thread priority for later checks. - * - [7.5.2] Locking the mutex first time, it must be possible because - * it is not owned. - * - [7.5.3] Locking the mutex second time, it must fail because it is - * already owned. - * - [7.5.4] Unlocking the mutex then it must not be owned anymore and - * the queue must be empty. - * - [7.5.5] Testing that priority has not changed after operations. - * - [7.5.6] Testing chMtxUnlockAll() behavior. - * - [7.5.7] Testing that priority has not changed after operations. + * - [7.5.1] An higher priority thread is created that performs + * non-atomical wait and signal operations on a semaphore. + * - [7.5.2] The function chSemSignalWait() is invoked by specifying + * the same semaphore for the wait and signal phases. The counter + * value must be one on exit. + * - [7.5.3] The function chSemSignalWait() is invoked again by + * specifying the same semaphore for the wait and signal phases. The + * counter value must be one on exit. * . */ static void rt_test_007_005_setup(void) { - chMtxObjectInit(&m1); + chSemObjectInit(&sem1, 0); +} + +static void rt_test_007_005_teardown(void) { + test_wait_threads(); } static void rt_test_007_005_execute(void) { - bool b; - tprio_t prio; - /* [7.5.1] Getting current thread priority for later checks.*/ + /* [7.5.1] An higher priority thread is created that performs + non-atomical wait and signal operations on a semaphore.*/ test_set_step(1); { - prio = chThdGetPriorityX(); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, thread3, 0); } test_end_step(1); - /* [7.5.2] Locking the mutex first time, it must be possible because - it is not owned.*/ + /* [7.5.2] The function chSemSignalWait() is invoked by specifying + the same semaphore for the wait and signal phases. The counter + value must be one on exit.*/ test_set_step(2); { - b = chMtxTryLock(&m1); - test_assert(b, "already locked"); + chSemSignalWait(&sem1, &sem1); + test_assert(queue_isempty(&sem1.queue), "queue not empty"); + test_assert(sem1.cnt == 0, "counter not zero"); } test_end_step(2); - /* [7.5.3] Locking the mutex second time, it must fail because it is - already owned.*/ + /* [7.5.3] The function chSemSignalWait() is invoked again by + specifying the same semaphore for the wait and signal phases. The + counter value must be one on exit.*/ test_set_step(3); { - b = chMtxTryLock(&m1); - test_assert(!b, "not locked"); + chSemSignalWait(&sem1, &sem1); + test_assert(queue_isempty(&sem1.queue), "queue not empty"); + test_assert(sem1.cnt == 0, "counter not zero"); } test_end_step(3); - - /* [7.5.4] Unlocking the mutex then it must not be owned anymore and - the queue must be empty.*/ - test_set_step(4); - { - chMtxUnlock(&m1); - test_assert(m1.owner == NULL, "still owned"); - test_assert(queue_isempty(&m1.queue), "queue not empty"); - } - test_end_step(4); - - /* [7.5.5] Testing that priority has not changed after operations.*/ - test_set_step(5); - { - test_assert(chThdGetPriorityX() == prio, "wrong priority level"); - } - test_end_step(5); - - /* [7.5.6] Testing chMtxUnlockAll() behavior.*/ - test_set_step(6); - { - b = chMtxTryLock(&m1); - test_assert(b, "already locked"); - b = chMtxTryLock(&m1); - test_assert(!b, "not locked"); - - chMtxUnlockAll(); - test_assert(m1.owner == NULL, "still owned"); - test_assert(queue_isempty(&m1.queue), "queue not empty"); - } - test_end_step(6); - - /* [7.5.7] Testing that priority has not changed after operations.*/ - test_set_step(7); - { - test_assert(chThdGetPriorityX() == prio, "wrong priority level"); - } - test_end_step(7); } static const testcase_t rt_test_007_005 = { - "Repeated locks, non recursive scenario", + "Testing chSemWaitSignal() functionality", rt_test_007_005_setup, - NULL, + rt_test_007_005_teardown, rt_test_007_005_execute }; -#endif /* !CH_CFG_USE_MUTEXES_RECURSIVE */ -#if (CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) /** - * @page rt_test_007_006 [7.6] Repeated locks using, recursive scenario + * @page rt_test_007_006 [7.6] Testing Binary Semaphores special case * *

Description

- * The behavior of multiple mutex locks from the same thread is tested - * when recursion is enabled. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_MUTEXES_RECURSIVE - * . + * This test case tests the binary semaphores functionality. The test + * both checks the binary semaphore status and the expected status of + * the underlying counting semaphore. * *

Test Steps

- * - [7.6.1] Getting current thread priority for later checks. - * - [7.6.2] Locking the mutex first time, it must be possible because - * it is not owned. - * - [7.6.3] Locking the mutex second time, it must be possible because - * it is recursive. - * - [7.6.4] Unlocking the mutex then it must be still owned because - * recursivity. - * - [7.6.5] Unlocking the mutex then it must not be owned anymore and - * the queue must be empty. - * - [7.6.6] Testing that priority has not changed after operations. - * - [7.6.7] Testing consecutive chMtxTryLock()/chMtxTryLockS() calls - * and a final chMtxUnlockAllS(). - * - [7.6.8] Testing consecutive chMtxLock()/chMtxLockS() calls and a - * final chMtxUnlockAll(). - * - [7.6.9] Testing that priority has not changed after operations. + * - [7.6.1] Creating a binary semaphore in "taken" state, the state is + * checked. + * - [7.6.2] Resetting the binary semaphore in "taken" state, the state + * must not change. + * - [7.6.3] Starting a signaler thread at a lower priority. + * - [7.6.4] Waiting for the binary semaphore to be signaled, the + * semaphore is expected to be taken. + * - [7.6.5] Signaling the binary semaphore, checking the binary + * semaphore state to be "not taken" and the underlying counter + * semaphore counter to be one. + * - [7.6.6] Signaling the binary semaphore again, the internal state + * must not change from "not taken". * . */ -static void rt_test_007_006_setup(void) { - chMtxObjectInit(&m1); +static void rt_test_007_006_teardown(void) { + test_wait_threads(); } static void rt_test_007_006_execute(void) { - bool b; - tprio_t prio; + binary_semaphore_t bsem; + msg_t msg; - /* [7.6.1] Getting current thread priority for later checks.*/ + /* [7.6.1] Creating a binary semaphore in "taken" state, the state is + checked.*/ test_set_step(1); { - prio = chThdGetPriorityX(); + chBSemObjectInit(&bsem, true); + test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken"); } test_end_step(1); - /* [7.6.2] Locking the mutex first time, it must be possible because - it is not owned.*/ + /* [7.6.2] Resetting the binary semaphore in "taken" state, the state + must not change.*/ test_set_step(2); { - b = chMtxTryLock(&m1); - test_assert(b, "already locked"); + chBSemReset(&bsem, true); + test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken"); } test_end_step(2); - /* [7.6.3] Locking the mutex second time, it must be possible because - it is recursive.*/ + /* [7.6.3] Starting a signaler thread at a lower priority.*/ test_set_step(3); { - b = chMtxTryLock(&m1); - test_assert(b, "already locked"); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, + chThdGetPriorityX()-1, thread4, &bsem); } test_end_step(3); - /* [7.6.4] Unlocking the mutex then it must be still owned because - recursivity.*/ + /* [7.6.4] Waiting for the binary semaphore to be signaled, the + semaphore is expected to be taken.*/ test_set_step(4); { - chMtxUnlock(&m1); - test_assert(m1.owner != NULL, "not owned"); + msg = chBSemWait(&bsem); + test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken"); + test_assert(msg == MSG_OK, "unexpected message"); } test_end_step(4); - /* [7.6.5] Unlocking the mutex then it must not be owned anymore and - the queue must be empty.*/ + /* [7.6.5] Signaling the binary semaphore, checking the binary + semaphore state to be "not taken" and the underlying counter + semaphore counter to be one.*/ test_set_step(5); { - chMtxUnlock(&m1); - test_assert(m1.owner == NULL, "still owned"); - test_assert(queue_isempty(&m1.queue), "queue not empty"); + chBSemSignal(&bsem); + test_assert_lock(chBSemGetStateI(&bsem) ==false, "still taken"); + test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter"); } test_end_step(5); - /* [7.6.6] Testing that priority has not changed after operations.*/ + /* [7.6.6] Signaling the binary semaphore again, the internal state + must not change from "not taken".*/ test_set_step(6); { - test_assert(chThdGetPriorityX() == prio, "wrong priority level"); + chBSemSignal(&bsem); + test_assert_lock(chBSemGetStateI(&bsem) == false, "taken"); + test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter"); } test_end_step(6); - - /* [7.6.7] Testing consecutive chMtxTryLock()/chMtxTryLockS() calls - and a final chMtxUnlockAllS().*/ - test_set_step(7); - { - b = chMtxTryLock(&m1); - test_assert(b, "already locked"); - chSysLock(); - b = chMtxTryLockS(&m1); - chSysUnlock(); - test_assert(b, "already locked"); - test_assert(m1.cnt == 2, "invalid recursion counter"); - chSysLock(); - chMtxUnlockAllS(); - chSysUnlock(); - test_assert(m1.owner == NULL, "still owned"); - test_assert(queue_isempty(&m1.queue), "queue not empty"); - test_assert(m1.cnt == 0, "invalid recursion counter"); - } - test_end_step(7); - - /* [7.6.8] Testing consecutive chMtxLock()/chMtxLockS() calls and a - final chMtxUnlockAll().*/ - test_set_step(8); - { - chMtxLock(&m1); - test_assert(m1.owner != NULL, "not owned"); - chSysLock(); - chMtxLockS(&m1); - chSysUnlock(); - test_assert(m1.owner != NULL, "not owned"); - test_assert(m1.cnt == 2, "invalid recursion counter"); - chMtxUnlockAll(); - test_assert(m1.owner == NULL, "still owned"); - test_assert(queue_isempty(&m1.queue), "queue not empty"); - test_assert(m1.cnt == 0, "invalid recursion counter"); - } - test_end_step(8); - - /* [7.6.9] Testing that priority has not changed after operations.*/ - test_set_step(9); - { - test_assert(chThdGetPriorityX() == prio, "wrong priority level"); - } - test_end_step(9); } static const testcase_t rt_test_007_006 = { - "Repeated locks using, recursive scenario", - rt_test_007_006_setup, + "Testing Binary Semaphores special case", NULL, + rt_test_007_006_teardown, rt_test_007_006_execute }; -#endif /* CH_CFG_USE_MUTEXES_RECURSIVE */ - -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) -/** - * @page rt_test_007_007 [7.7] Condition Variable signal test - * - *

Description

- * Five threads take a mutex and then enter a conditional variable - * queue, the tester thread then proceeds to signal the conditional - * variable five times atomically.
The test expects the threads to - * reach their goal in increasing priority order regardless of the - * initial order. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_CONDVARS - * . - * - *

Test Steps

- * - [7.7.1] Starting the five threads with increasing priority, the - * threads will queue on the condition variable. - * - [7.7.2] Atomically signaling the condition variable five times - * then waiting for the threads to terminate in priority order, the - * order is tested. - * . - */ - -static void rt_test_007_007_setup(void) { - chCondObjectInit(&c1); - chMtxObjectInit(&m1); -} - -static void rt_test_007_007_execute(void) { - - /* [7.7.1] Starting the five threads with increasing priority, the - threads will queue on the condition variable.*/ - test_set_step(1); - { - tprio_t prio = chThdGetPriorityX(); - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread6, "E"); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "D"); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread6, "C"); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread6, "B"); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread6, "A"); - } - test_end_step(1); - - /* [7.7.2] Atomically signaling the condition variable five times - then waiting for the threads to terminate in priority order, the - order is tested.*/ - test_set_step(2); - { - chSysLock(); - chCondSignalI(&c1); - chCondSignalI(&c1); - chCondSignalI(&c1); - chCondSignalI(&c1); - chCondSignalI(&c1); - chSchRescheduleS(); - chSysUnlock(); - test_wait_threads(); - test_assert_sequence("ABCDE", "invalid sequence"); - } - test_end_step(2); -} - -static const testcase_t rt_test_007_007 = { - "Condition Variable signal test", - rt_test_007_007_setup, - NULL, - rt_test_007_007_execute -}; -#endif /* CH_CFG_USE_CONDVARS */ - -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) -/** - * @page rt_test_007_008 [7.8] Condition Variable broadcast test - * - *

Description

- * Five threads take a mutex and then enter a conditional variable - * queue, the tester thread then proceeds to broadcast the conditional - * variable.
The test expects the threads to reach their goal in - * increasing priority order regardless of the initial order. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_CONDVARS - * . - * - *

Test Steps

- * - [7.8.1] Starting the five threads with increasing priority, the - * threads will queue on the condition variable. - * - [7.8.2] Broarcasting on the condition variable then waiting for - * the threads to terminate in priority order, the order is tested. - * . - */ - -static void rt_test_007_008_setup(void) { - chCondObjectInit(&c1); - chMtxObjectInit(&m1); -} - -static void rt_test_007_008_execute(void) { - - /* [7.8.1] Starting the five threads with increasing priority, the - threads will queue on the condition variable.*/ - test_set_step(1); - { - tprio_t prio = chThdGetPriorityX(); - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread6, "E"); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "D"); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread6, "C"); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread6, "B"); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread6, "A"); - } - test_end_step(1); - - /* [7.8.2] Broarcasting on the condition variable then waiting for - the threads to terminate in priority order, the order is tested.*/ - test_set_step(2); - { - chCondBroadcast(&c1); - test_wait_threads(); - test_assert_sequence("ABCDE", "invalid sequence"); - } - test_end_step(2); -} - -static const testcase_t rt_test_007_008 = { - "Condition Variable broadcast test", - rt_test_007_008_setup, - NULL, - rt_test_007_008_execute -}; -#endif /* CH_CFG_USE_CONDVARS */ - -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) -/** - * @page rt_test_007_009 [7.9] Condition Variable priority boost test - * - *

Description

- * This test case verifies the priority boost of a thread waiting on a - * conditional variable queue. It tests this very specific situation in - * order to improve code coverage. The created threads perform the - * following operations: TA{lock(M2), lock(M1), wait(C1), unlock(M1), - * unlock(M2)}, TB{lock(M2), wait(C1), unlock(M2)}. TC{lock(M1), - * unlock(M1)}. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_CONDVARS - * . - * - *

Test Steps

- * - [7.9.1] Reading current base priority. - * - [7.9.2] Thread A is created at priority P(+1), it locks M2, locks - * M1 and goes to wait on C1. - * - [7.9.3] Thread C is created at priority P(+2), it enqueues on M1 - * and boosts TA priority at P(+2). - * - [7.9.4] Thread B is created at priority P(+3), it enqueues on M2 - * and boosts TA priority at P(+3). - * - [7.9.5] Signaling C1: TA wakes up, unlocks M1 and priority goes to - * P(+2). TB locks M1, unlocks M1 and completes. TA unlocks M2 and - * priority goes to P(+1). TC waits on C1. TA completes. - * - [7.9.6] Signaling C1: TC wakes up, unlocks M1 and completes. - * - [7.9.7] Checking the order of operations. - * . - */ - -static void rt_test_007_009_setup(void) { - chCondObjectInit(&c1); - chMtxObjectInit(&m1); - chMtxObjectInit(&m2); -} - -static void rt_test_007_009_execute(void) { - tprio_t prio; - - /* [7.9.1] Reading current base priority.*/ - test_set_step(1); - { - prio = chThdGetPriorityX(); - } - test_end_step(1); - - /* [7.9.2] Thread A is created at priority P(+1), it locks M2, locks - M1 and goes to wait on C1.*/ - test_set_step(2); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread8, "A"); - } - test_end_step(2); - - /* [7.9.3] Thread C is created at priority P(+2), it enqueues on M1 - and boosts TA priority at P(+2).*/ - test_set_step(3); - { - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "C"); - } - test_end_step(3); - - /* [7.9.4] Thread B is created at priority P(+3), it enqueues on M2 - and boosts TA priority at P(+3).*/ - test_set_step(4); - { - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread9, "B"); - } - test_end_step(4); - - /* [7.9.5] Signaling C1: TA wakes up, unlocks M1 and priority goes to - P(+2). TB locks M1, unlocks M1 and completes. TA unlocks M2 and - priority goes to P(+1). TC waits on C1. TA completes.*/ - test_set_step(5); - { - chCondSignal(&c1); - } - test_end_step(5); - - /* [7.9.6] Signaling C1: TC wakes up, unlocks M1 and completes.*/ - test_set_step(6); - { - chCondSignal(&c1); - } - test_end_step(6); - - /* [7.9.7] Checking the order of operations.*/ - test_set_step(7); - { - test_wait_threads(); - test_assert_sequence("ABC", "invalid sequence"); - } - test_end_step(7); -} - -static const testcase_t rt_test_007_009 = { - "Condition Variable priority boost test", - rt_test_007_009_setup, - NULL, - rt_test_007_009_execute -}; -#endif /* CH_CFG_USE_CONDVARS */ /**************************************************************************** * Exported data. @@ -1087,37 +516,20 @@ static const testcase_t rt_test_007_009 = { */ const testcase_t * const rt_test_sequence_007_array[] = { &rt_test_007_001, -#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) &rt_test_007_002, -#endif -#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) &rt_test_007_003, -#endif &rt_test_007_004, -#if (!CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) &rt_test_007_005, -#endif -#if (CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) &rt_test_007_006, -#endif -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) - &rt_test_007_007, -#endif -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) - &rt_test_007_008, -#endif -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) - &rt_test_007_009, -#endif NULL }; /** - * @brief Mutexes, Condition Variables and Priority Inheritance. + * @brief Counter Semaphores. */ const testsequence_t rt_test_sequence_007 = { - "Mutexes, Condition Variables and Priority Inheritance", + "Counter Semaphores", rt_test_sequence_007_array }; -#endif /* CH_CFG_USE_MUTEXES */ +#endif /* CH_CFG_USE_SEMAPHORES */ diff --git a/test/rt/source/test/rt_test_sequence_008.c b/test/rt/source/test/rt_test_sequence_008.c index 26eba5940..531f7e924 100644 --- a/test/rt/source/test/rt_test_sequence_008.c +++ b/test/rt/source/test/rt_test_sequence_008.c @@ -21,93 +21,1062 @@ * @file rt_test_sequence_008.c * @brief Test Sequence 008 code. * - * @page rt_test_sequence_008 [8] Synchronous Messages + * @page rt_test_sequence_008 [8] Mutexes, Condition Variables and Priority Inheritance * * File: @ref rt_test_sequence_008.c * *

Description

- * This module implements the test sequence for the Synchronous - * Messages subsystem. + * This sequence tests the ChibiOS/RT functionalities related to + * mutexes, condition variables and priority inheritance algorithm. * *

Conditions

* This sequence is only executed if the following preprocessor condition * evaluates to true: - * - CH_CFG_USE_MESSAGES + * - CH_CFG_USE_MUTEXES * . * *

Test Cases

* - @subpage rt_test_008_001 + * - @subpage rt_test_008_002 + * - @subpage rt_test_008_003 + * - @subpage rt_test_008_004 + * - @subpage rt_test_008_005 + * - @subpage rt_test_008_006 + * - @subpage rt_test_008_007 + * - @subpage rt_test_008_008 + * - @subpage rt_test_008_009 * . */ -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) /**************************************************************************** * Shared code. ****************************************************************************/ -static THD_FUNCTION(msg_thread1, p) { +static MUTEX_DECL(m1); +static MUTEX_DECL(m2); +#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__) +static CONDVAR_DECL(c1); +#endif - chMsgSend(p, 'A'); - chMsgSend(p, 'B'); - chMsgSend(p, 'C'); - chMsgSend(p, 'D'); +#if CH_DBG_THREADS_PROFILING || defined(__DOXYGEN__) +/** + * @brief CPU pulse. + * @note The current implementation is not totally reliable. + * + * @param[in] duration CPU pulse duration in milliseconds + */ +void test_cpu_pulse(unsigned duration) { + systime_t start, end, now; + + start = chThdGetTicksX(chThdGetSelfX()); + end = chTimeAddX(start, TIME_MS2I(duration)); + do { + now = chThdGetTicksX(chThdGetSelfX()); +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } + while (chTimeIsInRangeX(now, start, end)); } +#endif /* CH_DBG_THREADS_PROFILING */ + +static THD_FUNCTION(thread1, p) { + + chMtxLock(&m1); + test_emit_token(*(char *)p); + chMtxUnlock(&m1); +} + +#if CH_DBG_THREADS_PROFILING || defined(__DOXYGEN__) +/* Low priority thread */ +static THD_FUNCTION(thread2L, p) { + + (void)p; + chMtxLock(&m1); + test_cpu_pulse(40); + chMtxUnlock(&m1); + test_cpu_pulse(10); + test_emit_token('C'); +} + +/* Medium priority thread */ +static THD_FUNCTION(thread2M, p) { + + (void)p; + chThdSleepMilliseconds(20); + test_cpu_pulse(40); + test_emit_token('B'); +} + +/* High priority thread */ +static THD_FUNCTION(thread2H, p) { + + (void)p; + chThdSleepMilliseconds(40); + chMtxLock(&m1); + test_cpu_pulse(10); + chMtxUnlock(&m1); + test_emit_token('A'); +} + +/* Lowest priority thread */ +static THD_FUNCTION(thread3LL, p) { + + (void)p; + chMtxLock(&m1); + test_cpu_pulse(30); + chMtxUnlock(&m1); + test_emit_token('E'); +} + +/* Low priority thread */ +static THD_FUNCTION(thread3L, p) { + + (void)p; + chThdSleepMilliseconds(10); + chMtxLock(&m2); + test_cpu_pulse(20); + chMtxLock(&m1); + test_cpu_pulse(10); + chMtxUnlock(&m1); + test_cpu_pulse(10); + chMtxUnlock(&m2); + test_emit_token('D'); +} + +/* Medium priority thread */ +static THD_FUNCTION(thread3M, p) { + + (void)p; + chThdSleepMilliseconds(20); + chMtxLock(&m2); + test_cpu_pulse(10); + chMtxUnlock(&m2); + test_emit_token('C'); +} + +/* High priority thread */ +static THD_FUNCTION(thread3H, p) { + + (void)p; + chThdSleepMilliseconds(40); + test_cpu_pulse(20); + test_emit_token('B'); +} + +/* Highest priority thread */ +static THD_FUNCTION(thread3HH, p) { + + (void)p; + chThdSleepMilliseconds(50); + chMtxLock(&m2); + test_cpu_pulse(10); + chMtxUnlock(&m2); + test_emit_token('A'); +} +#endif /* CH_DBG_THREADS_PROFILING */ + +static THD_FUNCTION(thread4A, p) { + + (void)p; + chThdSleepMilliseconds(50); + chMtxLock(&m1); + chMtxUnlock(&m1); +} + +static THD_FUNCTION(thread4B, p) { + + (void)p; + chThdSleepMilliseconds(150); + chSysLock(); + chMtxLockS(&m2); /* For coverage of the chMtxLockS() function variant.*/ + chMtxUnlockS(&m2); /* For coverage of the chMtxUnlockS() function variant.*/ + chSchRescheduleS(); + chSysUnlock(); +} + +#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__) +static THD_FUNCTION(thread6, p) { + + chMtxLock(&m1); + chCondWait(&c1); + test_emit_token(*(char *)p); + chMtxUnlock(&m1); +} + +static THD_FUNCTION(thread8, p) { + + chMtxLock(&m2); + chMtxLock(&m1); +#if CH_CFG_USE_CONDVARS_TIMEOUT || defined(__DOXYGEN__) + chCondWaitTimeout(&c1, TIME_INFINITE); +#else + chCondWait(&c1); +#endif + test_emit_token(*(char *)p); + chMtxUnlock(&m1); + chMtxUnlock(&m2); +} + +static THD_FUNCTION(thread9, p) { + + chMtxLock(&m2); + test_emit_token(*(char *)p); + chMtxUnlock(&m2); +} +#endif /* CH_CFG_USE_CONDVARS */ /**************************************************************************** * Test cases. ****************************************************************************/ /** - * @page rt_test_008_001 [8.1] Messages Server loop + * @page rt_test_008_001 [8.1] Priority enqueuing test * *

Description

- * A messenger thread is spawned that sends four messages back to the - * tester thread.
The test expect to receive the messages in the - * correct sequence and to not find a fifth message waiting. + * 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. * *

Test Steps

- * - [8.1.1] Starting the messenger thread. - * - [8.1.2] Waiting for four messages then testing the receive order. + * - [8.1.1] Getting the initial priority. + * - [8.1.2] Locking the mutex. + * - [8.1.3] Five threads are created that try to lock and unlock the + * mutex then terminate. The threads are created in ascending + * priority order. + * - [8.1.4] Unlocking the mutex, the threads will wakeup in priority + * order because the mutext queue is an ordered one. * . */ -static void rt_test_008_001_execute(void) { - thread_t *tp; - msg_t msg; +static void rt_test_008_001_setup(void) { + chMtxObjectInit(&m1); +} - /* [8.1.1] Starting the messenger thread.*/ +static void rt_test_008_001_execute(void) { + tprio_t prio; + + /* [8.1.1] Getting the initial priority.*/ test_set_step(1); { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() + 1, - msg_thread1, chThdGetSelfX()); + prio = chThdGetPriorityX(); } test_end_step(1); - /* [8.1.2] Waiting for four messages then testing the receive - order.*/ + /* [8.1.2] Locking the mutex.*/ test_set_step(2); { - unsigned i; + chMtxLock(&m1); + } + test_end_step(2); - for (i = 0; i < 4; i++) { - tp = chMsgWait(); - msg = chMsgGet(tp); - chMsgRelease(tp, msg); - test_emit_token(msg); - } + /* [8.1.3] Five threads are created that try to lock and unlock the + mutex then terminate. The threads are created in ascending + priority order.*/ + test_set_step(3); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread1, "E"); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread1, "D"); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread1, "C"); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread1, "B"); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread1, "A"); + } + test_end_step(3); + + /* [8.1.4] Unlocking the mutex, the threads will wakeup in priority + order because the mutext queue is an ordered one.*/ + test_set_step(4); + { + chMtxUnlock(&m1); test_wait_threads(); - test_assert_sequence("ABCD", "invalid sequence"); + test_assert(prio == chThdGetPriorityX(), "wrong priority level"); + test_assert_sequence("ABCDE", "invalid sequence"); + } + test_end_step(4); +} + +static const testcase_t rt_test_008_001 = { + "Priority enqueuing test", + rt_test_008_001_setup, + NULL, + rt_test_008_001_execute +}; + +#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) +/** + * @page rt_test_008_002 [8.2] 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 reach their goal in increasing priority + * order by rearranging their priorities in order to avoid the priority + * inversion trap. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_DBG_THREADS_PROFILING + * . + * + *

Test Steps

+ * - [8.2.1] Getting the system time for test duration measurement. + * - [8.2.2] The three contenders threads are created and let run + * atomically, the goals sequence is tested, the threads must + * complete in priority order. + * - [8.2.3] Testing that all threads completed within the specified + * time windows (100mS...100mS+ALLOWED_DELAY). + * . + */ + +static void rt_test_008_002_setup(void) { + chMtxObjectInit(&m1); +} + +static void rt_test_008_002_execute(void) { + systime_t time; + + /* [8.2.1] Getting the system time for test duration measurement.*/ + test_set_step(1); + { + time = test_wait_tick(); + } + test_end_step(1); + + /* [8.2.2] The three contenders threads are created and let run + atomically, the goals sequence is tested, the threads must + complete in priority order.*/ + test_set_step(2); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, thread2H, 0); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-2, thread2M, 0); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread2L, 0); + test_wait_threads(); + test_assert_sequence("ABC", "invalid sequence"); + } + test_end_step(2); + + /* [8.2.3] Testing that all threads completed within the specified + time windows (100mS...100mS+ALLOWED_DELAY).*/ + test_set_step(3); + { + test_assert_time_window(chTimeAddX(time, TIME_MS2I(100)), + chTimeAddX(time, TIME_MS2I(100) + ALLOWED_DELAY), + "out of time window"); + } + test_end_step(3); +} + +static const testcase_t rt_test_008_002 = { + "Priority inheritance, simple case", + rt_test_008_002_setup, + NULL, + rt_test_008_002_execute +}; +#endif /* CH_DBG_THREADS_PROFILING */ + +#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) +/** + * @page rt_test_008_003 [8.3] Priority inheritance, complex case + * + *

Description

+ * Five threads are involved in the complex priority inversion + * scenario, the priority inheritance algorithm is tested for depths + * greater than one. 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. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_DBG_THREADS_PROFILING + * . + * + *

Test Steps

+ * - [8.3.1] Getting the system time for test duration measurement. + * - [8.3.2] The five contenders threads are created and let run + * atomically, the goals sequence is tested, the threads must + * complete in priority order. + * - [8.3.3] Testing that all threads completed within the specified + * time windows (110mS...110mS+ALLOWED_DELAY). + * . + */ + +static void rt_test_008_003_setup(void) { + chMtxObjectInit(&m1); /* Mutex B.*/ + chMtxObjectInit(&m2); /* Mutex A.*/ +} + +static void rt_test_008_003_execute(void) { + systime_t time; + + /* [8.3.1] Getting the system time for test duration measurement.*/ + test_set_step(1); + { + time = test_wait_tick(); + } + test_end_step(1); + + /* [8.3.2] The five contenders threads are created and let run + atomically, the goals sequence is tested, the threads must + complete in priority order.*/ + test_set_step(2); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread3LL, 0); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread3L, 0); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread3M, 0); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread3H, 0); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread3HH, 0); + test_wait_threads(); + test_assert_sequence("ABCDE", "invalid sequence"); + } + test_end_step(2); + + /* [8.3.3] Testing that all threads completed within the specified + time windows (110mS...110mS+ALLOWED_DELAY).*/ + test_set_step(3); + { + test_assert_time_window(chTimeAddX(time, TIME_MS2I(110)), + chTimeAddX(time, TIME_MS2I(110) + ALLOWED_DELAY), + "out of time window"); + } + test_end_step(3); +} + +static const testcase_t rt_test_008_003 = { + "Priority inheritance, complex case", + rt_test_008_003_setup, + NULL, + rt_test_008_003_execute +}; +#endif /* CH_DBG_THREADS_PROFILING */ + +/** + * @page rt_test_008_004 [8.4] Priority return verification + * + *

Description

+ * Two threads are spawned that try to lock the mutexes already locked + * by the tester thread with precise timing. The test expects that the + * priority changes caused by the priority inheritance algorithm happen + * at the right moment and with the right values.
Thread A performs + * wait(50), lock(m1), unlock(m1), exit. Thread B performs wait(150), + * lock(m2), unlock(m2), exit. + * + *

Test Steps

+ * - [8.4.1] Getting current thread priority P(0) and assigning to the + * threads A and B priorities +1 and +2. + * - [8.4.2] Spawning threads A and B at priorities P(A) and P(B). + * - [8.4.3] Locking the mutex M1 before thread A has a chance to lock + * it. The priority must not change because A has not yet reached + * chMtxLock(M1). the mutex is not locked. + * - [8.4.4] Waiting 100mS, this makes thread A reach chMtxLock(M1) and + * get the mutex. This must boost the priority of the current thread + * at the same level of thread A. + * - [8.4.5] Locking the mutex M2 before thread B has a chance to lock + * it. The priority must not change because B has not yet reached + * chMtxLock(M2). the mutex is not locked. + * - [8.4.6] Waiting 100mS, this makes thread B reach chMtxLock(M2) and + * get the mutex. This must boost the priority of the current thread + * at the same level of thread B. + * - [8.4.7] Unlocking M2, the priority should fall back to P(A). + * - [8.4.8] Unlocking M1, the priority should fall back to P(0). + * . + */ + +static void rt_test_008_004_setup(void) { + chMtxObjectInit(&m1); + chMtxObjectInit(&m2); +} + +static void rt_test_008_004_teardown(void) { + test_wait_threads(); +} + +static void rt_test_008_004_execute(void) { + tprio_t p, pa, pb; + + /* [8.4.1] Getting current thread priority P(0) and assigning to the + threads A and B priorities +1 and +2.*/ + test_set_step(1); + { + p = chThdGetPriorityX(); + pa = p + 1; + pb = p + 2; + } + test_end_step(1); + + /* [8.4.2] Spawning threads A and B at priorities P(A) and P(B).*/ + test_set_step(2); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, pa, thread4A, "A"); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, pb, thread4B, "B"); + } + test_end_step(2); + + /* [8.4.3] Locking the mutex M1 before thread A has a chance to lock + it. The priority must not change because A has not yet reached + chMtxLock(M1). the mutex is not locked.*/ + test_set_step(3); + { + chMtxLock(&m1); + test_assert(chThdGetPriorityX() == p, "wrong priority level"); + } + test_end_step(3); + + /* [8.4.4] Waiting 100mS, this makes thread A reach chMtxLock(M1) and + get the mutex. This must boost the priority of the current thread + at the same level of thread A.*/ + test_set_step(4); + { + chThdSleepMilliseconds(100); + test_assert(chThdGetPriorityX() == pa, "wrong priority level"); + } + test_end_step(4); + + /* [8.4.5] Locking the mutex M2 before thread B has a chance to lock + it. The priority must not change because B has not yet reached + chMtxLock(M2). the mutex is not locked.*/ + test_set_step(5); + { + chMtxLock(&m2); + test_assert(chThdGetPriorityX() == pa, "wrong priority level"); + } + test_end_step(5); + + /* [8.4.6] Waiting 100mS, this makes thread B reach chMtxLock(M2) and + get the mutex. This must boost the priority of the current thread + at the same level of thread B.*/ + test_set_step(6); + { + chThdSleepMilliseconds(100); + test_assert(chThdGetPriorityX() == pb, "wrong priority level"); + } + test_end_step(6); + + /* [8.4.7] Unlocking M2, the priority should fall back to P(A).*/ + test_set_step(7); + { + chMtxUnlock(&m2); + test_assert(chThdGetPriorityX() == pa, "wrong priority level"); + } + test_end_step(7); + + /* [8.4.8] Unlocking M1, the priority should fall back to P(0).*/ + test_set_step(8); + { + chMtxUnlock(&m1); + test_assert(chThdGetPriorityX() == p, "wrong priority level"); + } + test_end_step(8); +} + +static const testcase_t rt_test_008_004 = { + "Priority return verification", + rt_test_008_004_setup, + rt_test_008_004_teardown, + rt_test_008_004_execute +}; + +#if (!CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) +/** + * @page rt_test_008_005 [8.5] Repeated locks, non recursive scenario + * + *

Description

+ * The behavior of multiple mutex locks from the same thread is tested + * when recursion is disabled. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - !CH_CFG_USE_MUTEXES_RECURSIVE + * . + * + *

Test Steps

+ * - [8.5.1] Getting current thread priority for later checks. + * - [8.5.2] Locking the mutex first time, it must be possible because + * it is not owned. + * - [8.5.3] Locking the mutex second time, it must fail because it is + * already owned. + * - [8.5.4] Unlocking the mutex then it must not be owned anymore and + * the queue must be empty. + * - [8.5.5] Testing that priority has not changed after operations. + * - [8.5.6] Testing chMtxUnlockAll() behavior. + * - [8.5.7] Testing that priority has not changed after operations. + * . + */ + +static void rt_test_008_005_setup(void) { + chMtxObjectInit(&m1); +} + +static void rt_test_008_005_execute(void) { + bool b; + tprio_t prio; + + /* [8.5.1] Getting current thread priority for later checks.*/ + test_set_step(1); + { + prio = chThdGetPriorityX(); + } + test_end_step(1); + + /* [8.5.2] Locking the mutex first time, it must be possible because + it is not owned.*/ + test_set_step(2); + { + b = chMtxTryLock(&m1); + test_assert(b, "already locked"); + } + test_end_step(2); + + /* [8.5.3] Locking the mutex second time, it must fail because it is + already owned.*/ + test_set_step(3); + { + b = chMtxTryLock(&m1); + test_assert(!b, "not locked"); + } + test_end_step(3); + + /* [8.5.4] Unlocking the mutex then it must not be owned anymore and + the queue must be empty.*/ + test_set_step(4); + { + chMtxUnlock(&m1); + test_assert(m1.owner == NULL, "still owned"); + test_assert(queue_isempty(&m1.queue), "queue not empty"); + } + test_end_step(4); + + /* [8.5.5] Testing that priority has not changed after operations.*/ + test_set_step(5); + { + test_assert(chThdGetPriorityX() == prio, "wrong priority level"); + } + test_end_step(5); + + /* [8.5.6] Testing chMtxUnlockAll() behavior.*/ + test_set_step(6); + { + b = chMtxTryLock(&m1); + test_assert(b, "already locked"); + b = chMtxTryLock(&m1); + test_assert(!b, "not locked"); + + chMtxUnlockAll(); + test_assert(m1.owner == NULL, "still owned"); + test_assert(queue_isempty(&m1.queue), "queue not empty"); + } + test_end_step(6); + + /* [8.5.7] Testing that priority has not changed after operations.*/ + test_set_step(7); + { + test_assert(chThdGetPriorityX() == prio, "wrong priority level"); + } + test_end_step(7); +} + +static const testcase_t rt_test_008_005 = { + "Repeated locks, non recursive scenario", + rt_test_008_005_setup, + NULL, + rt_test_008_005_execute +}; +#endif /* !CH_CFG_USE_MUTEXES_RECURSIVE */ + +#if (CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) +/** + * @page rt_test_008_006 [8.6] Repeated locks using, recursive scenario + * + *

Description

+ * The behavior of multiple mutex locks from the same thread is tested + * when recursion is enabled. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_MUTEXES_RECURSIVE + * . + * + *

Test Steps

+ * - [8.6.1] Getting current thread priority for later checks. + * - [8.6.2] Locking the mutex first time, it must be possible because + * it is not owned. + * - [8.6.3] Locking the mutex second time, it must be possible because + * it is recursive. + * - [8.6.4] Unlocking the mutex then it must be still owned because + * recursivity. + * - [8.6.5] Unlocking the mutex then it must not be owned anymore and + * the queue must be empty. + * - [8.6.6] Testing that priority has not changed after operations. + * - [8.6.7] Testing consecutive chMtxTryLock()/chMtxTryLockS() calls + * and a final chMtxUnlockAllS(). + * - [8.6.8] Testing consecutive chMtxLock()/chMtxLockS() calls and a + * final chMtxUnlockAll(). + * - [8.6.9] Testing that priority has not changed after operations. + * . + */ + +static void rt_test_008_006_setup(void) { + chMtxObjectInit(&m1); +} + +static void rt_test_008_006_execute(void) { + bool b; + tprio_t prio; + + /* [8.6.1] Getting current thread priority for later checks.*/ + test_set_step(1); + { + prio = chThdGetPriorityX(); + } + test_end_step(1); + + /* [8.6.2] Locking the mutex first time, it must be possible because + it is not owned.*/ + test_set_step(2); + { + b = chMtxTryLock(&m1); + test_assert(b, "already locked"); + } + test_end_step(2); + + /* [8.6.3] Locking the mutex second time, it must be possible because + it is recursive.*/ + test_set_step(3); + { + b = chMtxTryLock(&m1); + test_assert(b, "already locked"); + } + test_end_step(3); + + /* [8.6.4] Unlocking the mutex then it must be still owned because + recursivity.*/ + test_set_step(4); + { + chMtxUnlock(&m1); + test_assert(m1.owner != NULL, "not owned"); + } + test_end_step(4); + + /* [8.6.5] Unlocking the mutex then it must not be owned anymore and + the queue must be empty.*/ + test_set_step(5); + { + chMtxUnlock(&m1); + test_assert(m1.owner == NULL, "still owned"); + test_assert(queue_isempty(&m1.queue), "queue not empty"); + } + test_end_step(5); + + /* [8.6.6] Testing that priority has not changed after operations.*/ + test_set_step(6); + { + test_assert(chThdGetPriorityX() == prio, "wrong priority level"); + } + test_end_step(6); + + /* [8.6.7] Testing consecutive chMtxTryLock()/chMtxTryLockS() calls + and a final chMtxUnlockAllS().*/ + test_set_step(7); + { + b = chMtxTryLock(&m1); + test_assert(b, "already locked"); + chSysLock(); + b = chMtxTryLockS(&m1); + chSysUnlock(); + test_assert(b, "already locked"); + test_assert(m1.cnt == 2, "invalid recursion counter"); + chSysLock(); + chMtxUnlockAllS(); + chSysUnlock(); + test_assert(m1.owner == NULL, "still owned"); + test_assert(queue_isempty(&m1.queue), "queue not empty"); + test_assert(m1.cnt == 0, "invalid recursion counter"); + } + test_end_step(7); + + /* [8.6.8] Testing consecutive chMtxLock()/chMtxLockS() calls and a + final chMtxUnlockAll().*/ + test_set_step(8); + { + chMtxLock(&m1); + test_assert(m1.owner != NULL, "not owned"); + chSysLock(); + chMtxLockS(&m1); + chSysUnlock(); + test_assert(m1.owner != NULL, "not owned"); + test_assert(m1.cnt == 2, "invalid recursion counter"); + chMtxUnlockAll(); + test_assert(m1.owner == NULL, "still owned"); + test_assert(queue_isempty(&m1.queue), "queue not empty"); + test_assert(m1.cnt == 0, "invalid recursion counter"); + } + test_end_step(8); + + /* [8.6.9] Testing that priority has not changed after operations.*/ + test_set_step(9); + { + test_assert(chThdGetPriorityX() == prio, "wrong priority level"); + } + test_end_step(9); +} + +static const testcase_t rt_test_008_006 = { + "Repeated locks using, recursive scenario", + rt_test_008_006_setup, + NULL, + rt_test_008_006_execute +}; +#endif /* CH_CFG_USE_MUTEXES_RECURSIVE */ + +#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) +/** + * @page rt_test_008_007 [8.7] Condition Variable signal test + * + *

Description

+ * Five threads take a mutex and then enter a conditional variable + * queue, the tester thread then proceeds to signal the conditional + * variable five times atomically.
The test expects the threads to + * reach their goal in increasing priority order regardless of the + * initial order. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_CONDVARS + * . + * + *

Test Steps

+ * - [8.7.1] Starting the five threads with increasing priority, the + * threads will queue on the condition variable. + * - [8.7.2] Atomically signaling the condition variable five times + * then waiting for the threads to terminate in priority order, the + * order is tested. + * . + */ + +static void rt_test_008_007_setup(void) { + chCondObjectInit(&c1); + chMtxObjectInit(&m1); +} + +static void rt_test_008_007_execute(void) { + + /* [8.7.1] Starting the five threads with increasing priority, the + threads will queue on the condition variable.*/ + test_set_step(1); + { + tprio_t prio = chThdGetPriorityX(); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread6, "E"); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "D"); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread6, "C"); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread6, "B"); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread6, "A"); + } + test_end_step(1); + + /* [8.7.2] Atomically signaling the condition variable five times + then waiting for the threads to terminate in priority order, the + order is tested.*/ + test_set_step(2); + { + chSysLock(); + chCondSignalI(&c1); + chCondSignalI(&c1); + chCondSignalI(&c1); + chCondSignalI(&c1); + chCondSignalI(&c1); + chSchRescheduleS(); + chSysUnlock(); + test_wait_threads(); + test_assert_sequence("ABCDE", "invalid sequence"); } test_end_step(2); } -static const testcase_t rt_test_008_001 = { - "Messages Server loop", +static const testcase_t rt_test_008_007 = { + "Condition Variable signal test", + rt_test_008_007_setup, NULL, - NULL, - rt_test_008_001_execute + rt_test_008_007_execute }; +#endif /* CH_CFG_USE_CONDVARS */ + +#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) +/** + * @page rt_test_008_008 [8.8] Condition Variable broadcast test + * + *

Description

+ * Five threads take a mutex and then enter a conditional variable + * queue, the tester thread then proceeds to broadcast the conditional + * variable.
The test expects the threads to reach their goal in + * increasing priority order regardless of the initial order. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_CONDVARS + * . + * + *

Test Steps

+ * - [8.8.1] Starting the five threads with increasing priority, the + * threads will queue on the condition variable. + * - [8.8.2] Broarcasting on the condition variable then waiting for + * the threads to terminate in priority order, the order is tested. + * . + */ + +static void rt_test_008_008_setup(void) { + chCondObjectInit(&c1); + chMtxObjectInit(&m1); +} + +static void rt_test_008_008_execute(void) { + + /* [8.8.1] Starting the five threads with increasing priority, the + threads will queue on the condition variable.*/ + test_set_step(1); + { + tprio_t prio = chThdGetPriorityX(); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread6, "E"); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "D"); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread6, "C"); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread6, "B"); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread6, "A"); + } + test_end_step(1); + + /* [8.8.2] Broarcasting on the condition variable then waiting for + the threads to terminate in priority order, the order is tested.*/ + test_set_step(2); + { + chCondBroadcast(&c1); + test_wait_threads(); + test_assert_sequence("ABCDE", "invalid sequence"); + } + test_end_step(2); +} + +static const testcase_t rt_test_008_008 = { + "Condition Variable broadcast test", + rt_test_008_008_setup, + NULL, + rt_test_008_008_execute +}; +#endif /* CH_CFG_USE_CONDVARS */ + +#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) +/** + * @page rt_test_008_009 [8.9] Condition Variable priority boost test + * + *

Description

+ * This test case verifies the priority boost of a thread waiting on a + * conditional variable queue. It tests this very specific situation in + * order to improve code coverage. The created threads perform the + * following operations: TA{lock(M2), lock(M1), wait(C1), unlock(M1), + * unlock(M2)}, TB{lock(M2), wait(C1), unlock(M2)}. TC{lock(M1), + * unlock(M1)}. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_CONDVARS + * . + * + *

Test Steps

+ * - [8.9.1] Reading current base priority. + * - [8.9.2] Thread A is created at priority P(+1), it locks M2, locks + * M1 and goes to wait on C1. + * - [8.9.3] Thread C is created at priority P(+2), it enqueues on M1 + * and boosts TA priority at P(+2). + * - [8.9.4] Thread B is created at priority P(+3), it enqueues on M2 + * and boosts TA priority at P(+3). + * - [8.9.5] Signaling C1: TA wakes up, unlocks M1 and priority goes to + * P(+2). TB locks M1, unlocks M1 and completes. TA unlocks M2 and + * priority goes to P(+1). TC waits on C1. TA completes. + * - [8.9.6] Signaling C1: TC wakes up, unlocks M1 and completes. + * - [8.9.7] Checking the order of operations. + * . + */ + +static void rt_test_008_009_setup(void) { + chCondObjectInit(&c1); + chMtxObjectInit(&m1); + chMtxObjectInit(&m2); +} + +static void rt_test_008_009_execute(void) { + tprio_t prio; + + /* [8.9.1] Reading current base priority.*/ + test_set_step(1); + { + prio = chThdGetPriorityX(); + } + test_end_step(1); + + /* [8.9.2] Thread A is created at priority P(+1), it locks M2, locks + M1 and goes to wait on C1.*/ + test_set_step(2); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread8, "A"); + } + test_end_step(2); + + /* [8.9.3] Thread C is created at priority P(+2), it enqueues on M1 + and boosts TA priority at P(+2).*/ + test_set_step(3); + { + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "C"); + } + test_end_step(3); + + /* [8.9.4] Thread B is created at priority P(+3), it enqueues on M2 + and boosts TA priority at P(+3).*/ + test_set_step(4); + { + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread9, "B"); + } + test_end_step(4); + + /* [8.9.5] Signaling C1: TA wakes up, unlocks M1 and priority goes to + P(+2). TB locks M1, unlocks M1 and completes. TA unlocks M2 and + priority goes to P(+1). TC waits on C1. TA completes.*/ + test_set_step(5); + { + chCondSignal(&c1); + } + test_end_step(5); + + /* [8.9.6] Signaling C1: TC wakes up, unlocks M1 and completes.*/ + test_set_step(6); + { + chCondSignal(&c1); + } + test_end_step(6); + + /* [8.9.7] Checking the order of operations.*/ + test_set_step(7); + { + test_wait_threads(); + test_assert_sequence("ABC", "invalid sequence"); + } + test_end_step(7); +} + +static const testcase_t rt_test_008_009 = { + "Condition Variable priority boost test", + rt_test_008_009_setup, + NULL, + rt_test_008_009_execute +}; +#endif /* CH_CFG_USE_CONDVARS */ /**************************************************************************** * Exported data. @@ -118,15 +1087,37 @@ static const testcase_t rt_test_008_001 = { */ const testcase_t * const rt_test_sequence_008_array[] = { &rt_test_008_001, +#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) + &rt_test_008_002, +#endif +#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) + &rt_test_008_003, +#endif + &rt_test_008_004, +#if (!CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) + &rt_test_008_005, +#endif +#if (CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) + &rt_test_008_006, +#endif +#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) + &rt_test_008_007, +#endif +#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) + &rt_test_008_008, +#endif +#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) + &rt_test_008_009, +#endif NULL }; /** - * @brief Synchronous Messages. + * @brief Mutexes, Condition Variables and Priority Inheritance. */ const testsequence_t rt_test_sequence_008 = { - "Synchronous Messages", + "Mutexes, Condition Variables and Priority Inheritance", rt_test_sequence_008_array }; -#endif /* CH_CFG_USE_MESSAGES */ +#endif /* CH_CFG_USE_MUTEXES */ diff --git a/test/rt/source/test/rt_test_sequence_009.c b/test/rt/source/test/rt_test_sequence_009.c index b54cab562..0387a29c0 100644 --- a/test/rt/source/test/rt_test_sequence_009.c +++ b/test/rt/source/test/rt_test_sequence_009.c @@ -21,56 +21,37 @@ * @file rt_test_sequence_009.c * @brief Test Sequence 009 code. * - * @page rt_test_sequence_009 [9] Event Sources and Event Flags + * @page rt_test_sequence_009 [9] Synchronous Messages * * File: @ref rt_test_sequence_009.c * *

Description

- * This module implements the test sequence for the Events subsystem. + * This module implements the test sequence for the Synchronous + * Messages subsystem. * *

Conditions

* This sequence is only executed if the following preprocessor condition * evaluates to true: - * - CH_CFG_USE_EVENTS + * - CH_CFG_USE_MESSAGES * . * *

Test Cases

* - @subpage rt_test_009_001 - * - @subpage rt_test_009_002 - * - @subpage rt_test_009_003 - * - @subpage rt_test_009_004 - * - @subpage rt_test_009_005 - * - @subpage rt_test_009_006 - * - @subpage rt_test_009_007 * . */ -#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) /**************************************************************************** * Shared code. ****************************************************************************/ -static EVENTSOURCE_DECL(es1); -static EVENTSOURCE_DECL(es2); +static THD_FUNCTION(msg_thread1, p) { -static void h1(eventid_t id) {(void)id;test_emit_token('A');} -static void h2(eventid_t id) {(void)id;test_emit_token('B');} -static void h3(eventid_t id) {(void)id;test_emit_token('C');} -static ROMCONST evhandler_t evhndl[] = {h1, h2, h3}; - -static THD_FUNCTION(evt_thread3, p) { - - chThdSleepMilliseconds(50); - chEvtSignal((thread_t *)p, 1); -} - -static THD_FUNCTION(evt_thread7, p) { - - (void)p; - chEvtBroadcast(&es1); - chThdSleepMilliseconds(50); - chEvtBroadcast(&es2); + chMsgSend(p, 'A'); + chMsgSend(p, 'B'); + chMsgSend(p, 'C'); + chMsgSend(p, 'D'); } /**************************************************************************** @@ -78,501 +59,56 @@ static THD_FUNCTION(evt_thread7, p) { ****************************************************************************/ /** - * @page rt_test_009_001 [9.1] Events registration + * @page rt_test_009_001 [9.1] Messages Server loop * *

Description

- * Two event listeners are registered on an event source and then - * unregistered in the same order.
The test expects that the even - * source has listeners after the registrations and after the first - * unregistration, then, after the second unegistration, the test - * expects no more listeners. + * A messenger thread is spawned that sends four messages back to the + * tester thread.
The test expect to receive the messages in the + * correct sequence and to not find a fifth message waiting. * *

Test Steps

- * - [9.1.1] An Event Source is initialized. - * - [9.1.2] Two Event Listeners are registered on the Event Source, - * the Event Source is tested to have listeners. - * - [9.1.3] An Event Listener is unregistered, the Event Source must - * still have listeners. - * - [9.1.4] An Event Listener is unregistered, the Event Source must - * not have listeners. + * - [9.1.1] Starting the messenger thread. + * - [9.1.2] Waiting for four messages then testing the receive order. * . */ static void rt_test_009_001_execute(void) { - event_listener_t el1, el2; + thread_t *tp; + msg_t msg; - /* [9.1.1] An Event Source is initialized.*/ + /* [9.1.1] Starting the messenger thread.*/ test_set_step(1); { - chEvtObjectInit(&es1); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() + 1, + msg_thread1, chThdGetSelfX()); } test_end_step(1); - /* [9.1.2] Two Event Listeners are registered on the Event Source, - the Event Source is tested to have listeners.*/ + /* [9.1.2] Waiting for four messages then testing the receive + order.*/ test_set_step(2); { - chEvtRegisterMask(&es1, &el1, 1); - chEvtRegisterMask(&es1, &el2, 2); - test_assert_lock(chEvtIsListeningI(&es1), "no listener"); + unsigned i; + + for (i = 0; i < 4; i++) { + tp = chMsgWait(); + msg = chMsgGet(tp); + chMsgRelease(tp, msg); + test_emit_token(msg); + } + test_wait_threads(); + test_assert_sequence("ABCD", "invalid sequence"); } test_end_step(2); - - /* [9.1.3] An Event Listener is unregistered, the Event Source must - still have listeners.*/ - test_set_step(3); - { - chEvtUnregister(&es1, &el1); - test_assert_lock(chEvtIsListeningI(&es1), "no listener"); - } - test_end_step(3); - - /* [9.1.4] An Event Listener is unregistered, the Event Source must - not have listeners.*/ - test_set_step(4); - { - chEvtUnregister(&es1, &el2); - test_assert_lock(!chEvtIsListeningI(&es1), "stuck listener"); - } - test_end_step(4); } static const testcase_t rt_test_009_001 = { - "Events registration", + "Messages Server loop", NULL, NULL, rt_test_009_001_execute }; -/** - * @page rt_test_009_002 [9.2] Event Flags dispatching - * - *

Description

- * The test dispatches three event flags and verifies that the - * associated event handlers are invoked in LSb-first order. - * - *

Test Steps

- * - [9.2.1] Three evenf flag bits are raised then chEvtDispatch() is - * invoked, the sequence of handlers calls is tested. - * . - */ - -static void rt_test_009_002_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_009_002_execute(void) { - - /* [9.2.1] Three evenf flag bits are raised then chEvtDispatch() is - invoked, the sequence of handlers calls is tested.*/ - test_set_step(1); - { - chEvtDispatch(evhndl, 7); - test_assert_sequence("ABC", "invalid sequence"); - } - test_end_step(1); -} - -static const testcase_t rt_test_009_002 = { - "Event Flags dispatching", - rt_test_009_002_setup, - NULL, - rt_test_009_002_execute -}; - -/** - * @page rt_test_009_003 [9.3] Events Flags wait using chEvtWaitOne() - * - *

Description

- * Functionality of chEvtWaitOne() is tested under various scenarios. - * - *

Test Steps

- * - [9.3.1] Setting three event flags. - * - [9.3.2] Calling chEvtWaitOne() three times, each time a single - * flag must be returned in order of priority. - * - [9.3.3] Getting current time and starting a signaler thread, the - * thread will set an event flag after 50mS. - * - [9.3.4] Calling chEvtWaitOne() then verifying that the event has - * been received after 50mS and that the event flags mask has been - * emptied. - * . - */ - -static void rt_test_009_003_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_009_003_execute(void) { - eventmask_t m; - systime_t target_time; - - /* [9.3.1] Setting three event flags.*/ - test_set_step(1); - { - chEvtAddEvents(7); - } - test_end_step(1); - - /* [9.3.2] Calling chEvtWaitOne() three times, each time a single - flag must be returned in order of priority.*/ - test_set_step(2); - { - m = chEvtWaitOne(ALL_EVENTS); - test_assert(m == 1, "single event error"); - m = chEvtWaitOne(ALL_EVENTS); - test_assert(m == 2, "single event error"); - m = chEvtWaitOne(ALL_EVENTS); - test_assert(m == 4, "single event error"); - m = chEvtGetAndClearEvents(ALL_EVENTS); - test_assert(m == 0, "stuck event"); - } - test_end_step(2); - - /* [9.3.3] Getting current time and starting a signaler thread, the - thread will set an event flag after 50mS.*/ - test_set_step(3); - { - target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50)); - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, - evt_thread3, chThdGetSelfX()); - } - test_end_step(3); - - /* [9.3.4] Calling chEvtWaitOne() then verifying that the event has - been received after 50mS and that the event flags mask has been - emptied.*/ - test_set_step(4); - { - m = chEvtWaitOne(ALL_EVENTS); - test_assert_time_window(target_time, - chTimeAddX(target_time, ALLOWED_DELAY), - "out of time window"); - test_assert(m == 1, "event flag error"); - m = chEvtGetAndClearEvents(ALL_EVENTS); - test_assert(m == 0, "stuck event"); - test_wait_threads(); - } - test_end_step(4); -} - -static const testcase_t rt_test_009_003 = { - "Events Flags wait using chEvtWaitOne()", - rt_test_009_003_setup, - NULL, - rt_test_009_003_execute -}; - -/** - * @page rt_test_009_004 [9.4] Events Flags wait using chEvtWaitAny() - * - *

Description

- * Functionality of chEvtWaitAny() is tested under various scenarios. - * - *

Test Steps

- * - [9.4.1] Setting two, non contiguous, event flags. - * - [9.4.2] Calling chEvtWaitAny() one time, the two flags must be - * returned. - * - [9.4.3] Getting current time and starting a signaler thread, the - * thread will set an event flag after 50mS. - * - [9.4.4] Calling chEvtWaitAny() then verifying that the event has - * been received after 50mS and that the event flags mask has been - * emptied. - * . - */ - -static void rt_test_009_004_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_009_004_execute(void) { - eventmask_t m; - systime_t target_time; - - /* [9.4.1] Setting two, non contiguous, event flags.*/ - test_set_step(1); - { - chEvtAddEvents(5); - } - test_end_step(1); - - /* [9.4.2] Calling chEvtWaitAny() one time, the two flags must be - returned.*/ - test_set_step(2); - { - m = chEvtWaitAny(ALL_EVENTS); - test_assert(m == 5, "unexpected pending bit"); - m = chEvtGetAndClearEvents(ALL_EVENTS); - test_assert(m == 0, "stuck event"); - } - test_end_step(2); - - /* [9.4.3] Getting current time and starting a signaler thread, the - thread will set an event flag after 50mS.*/ - test_set_step(3); - { - target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50)); - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, - evt_thread3, chThdGetSelfX()); - } - test_end_step(3); - - /* [9.4.4] Calling chEvtWaitAny() then verifying that the event has - been received after 50mS and that the event flags mask has been - emptied.*/ - test_set_step(4); - { - m = chEvtWaitAny(ALL_EVENTS); - test_assert_time_window(target_time, - chTimeAddX(target_time, ALLOWED_DELAY), - "out of time window"); - test_assert(m == 1, "event flag error"); - m = chEvtGetAndClearEvents(ALL_EVENTS); - test_assert(m == 0, "stuck event"); - test_wait_threads(); - } - test_end_step(4); -} - -static const testcase_t rt_test_009_004 = { - "Events Flags wait using chEvtWaitAny()", - rt_test_009_004_setup, - NULL, - rt_test_009_004_execute -}; - -/** - * @page rt_test_009_005 [9.5] Events Flags wait using chEvtWaitAll() - * - *

Description

- * Functionality of chEvtWaitAll() is tested under various scenarios. - * - *

Test Steps

- * - [9.5.1] Setting two, non contiguous, event flags. - * - [9.5.2] Calling chEvtWaitAll() one time, the two flags must be - * returned. - * - [9.5.3] Setting one event flag. - * - [9.5.4] Getting current time and starting a signaler thread, the - * thread will set another event flag after 50mS. - * - [9.5.5] Calling chEvtWaitAll() then verifying that both event - * flags have been received after 50mS and that the event flags mask - * has been emptied. - * . - */ - -static void rt_test_009_005_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_009_005_execute(void) { - eventmask_t m; - systime_t target_time; - - /* [9.5.1] Setting two, non contiguous, event flags.*/ - test_set_step(1); - { - chEvtAddEvents(5); - } - test_end_step(1); - - /* [9.5.2] Calling chEvtWaitAll() one time, the two flags must be - returned.*/ - test_set_step(2); - { - m = chEvtWaitAll(5); - test_assert(m == 5, "unexpected pending bit"); - m = chEvtGetAndClearEvents(ALL_EVENTS); - test_assert(m == 0, "stuck event"); - } - test_end_step(2); - - /* [9.5.3] Setting one event flag.*/ - test_set_step(3); - { - chEvtAddEvents(4); - } - test_end_step(3); - - /* [9.5.4] Getting current time and starting a signaler thread, the - thread will set another event flag after 50mS.*/ - test_set_step(4); - { - target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50)); - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, - evt_thread3, chThdGetSelfX()); - } - test_end_step(4); - - /* [9.5.5] Calling chEvtWaitAll() then verifying that both event - flags have been received after 50mS and that the event flags mask - has been emptied.*/ - test_set_step(5); - { - m = chEvtWaitAll(5); - test_assert_time_window(target_time, - chTimeAddX(target_time, ALLOWED_DELAY), - "out of time window"); - test_assert(m == 5, "event flags error"); - m = chEvtGetAndClearEvents(ALL_EVENTS); - test_assert(m == 0, "stuck event"); - test_wait_threads(); - } - test_end_step(5); -} - -static const testcase_t rt_test_009_005 = { - "Events Flags wait using chEvtWaitAll()", - rt_test_009_005_setup, - NULL, - rt_test_009_005_execute -}; - -#if (CH_CFG_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) -/** - * @page rt_test_009_006 [9.6] Events Flags wait timeouts - * - *

Description

- * Timeout functionality is tested for chEvtWaitOneTimeout(), - * chEvtWaitAnyTimeout() and chEvtWaitAllTimeout(). - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_EVENTS_TIMEOUT - * . - * - *

Test Steps

- * - [9.6.1] The functions are invoked first with TIME_IMMEDIATE - * timeout, the timeout condition is tested. - * - [9.6.2] The functions are invoked first with a 50mS timeout, the - * timeout condition is tested. - * . - */ - -static void rt_test_009_006_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_009_006_execute(void) { - eventmask_t m; - - /* [9.6.1] The functions are invoked first with TIME_IMMEDIATE - timeout, the timeout condition is tested.*/ - test_set_step(1); - { - m = chEvtWaitOneTimeout(ALL_EVENTS, TIME_IMMEDIATE); - test_assert(m == 0, "spurious event"); - m = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_IMMEDIATE); - test_assert(m == 0, "spurious event"); - m = chEvtWaitAllTimeout(ALL_EVENTS, TIME_IMMEDIATE); - test_assert(m == 0, "spurious event"); - } - test_end_step(1); - - /* [9.6.2] The functions are invoked first with a 50mS timeout, the - timeout condition is tested.*/ - test_set_step(2); - { - m = chEvtWaitOneTimeout(ALL_EVENTS, TIME_MS2I(50)); - test_assert(m == 0, "spurious event"); - m = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(50)); - test_assert(m == 0, "spurious event"); - m = chEvtWaitAllTimeout(ALL_EVENTS, TIME_MS2I(50)); - test_assert(m == 0, "spurious event"); - } - test_end_step(2); -} - -static const testcase_t rt_test_009_006 = { - "Events Flags wait timeouts", - rt_test_009_006_setup, - NULL, - rt_test_009_006_execute -}; -#endif /* CH_CFG_USE_EVENTS_TIMEOUT */ - -/** - * @page rt_test_009_007 [9.7] Broadcasting using chEvtBroadcast() - * - *

Description

- * Functionality of chEvtBroadcast() is tested. - * - *

Test Steps

- * - [9.7.1] Registering on two event sources associating them with - * flags 1 and 4. - * - [9.7.2] Getting current time and starting a broadcaster thread, - * the thread broadcast the first Event Source immediately and the - * other after 50mS. - * - [9.7.3] Calling chEvtWaitAll() then verifying that both event - * flags have been received after 50mS and that the event flags mask - * has been emptied. - * - [9.7.4] Unregistering from the Event Sources. - * . - */ - -static void rt_test_009_007_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); - chEvtObjectInit(&es1); - chEvtObjectInit(&es2); -} - -static void rt_test_009_007_execute(void) { - eventmask_t m; - event_listener_t el1, el2; - systime_t target_time; - - /* [9.7.1] Registering on two event sources associating them with - flags 1 and 4.*/ - test_set_step(1); - { - chEvtRegisterMask(&es1, &el1, 1); - chEvtRegisterMask(&es2, &el2, 4); - } - test_end_step(1); - - /* [9.7.2] Getting current time and starting a broadcaster thread, - the thread broadcast the first Event Source immediately and the - other after 50mS.*/ - test_set_step(2); - { - target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50)); - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, - evt_thread7, "A"); - } - test_end_step(2); - - /* [9.7.3] Calling chEvtWaitAll() then verifying that both event - flags have been received after 50mS and that the event flags mask - has been emptied.*/ - test_set_step(3); - { - m = chEvtWaitAll(5); - test_assert_time_window(target_time, - chTimeAddX(target_time, ALLOWED_DELAY), - "out of time window"); - m = chEvtGetAndClearEvents(ALL_EVENTS); - test_assert(m == 0, "stuck event"); - test_wait_threads(); - } - test_end_step(3); - - /* [9.7.4] Unregistering from the Event Sources.*/ - test_set_step(4); - { - chEvtUnregister(&es1, &el1); - chEvtUnregister(&es2, &el2); - test_assert(!chEvtIsListeningI(&es1), "stuck listener"); - test_assert(!chEvtIsListeningI(&es2), "stuck listener"); - } - test_end_step(4); -} - -static const testcase_t rt_test_009_007 = { - "Broadcasting using chEvtBroadcast()", - rt_test_009_007_setup, - NULL, - rt_test_009_007_execute -}; - /**************************************************************************** * Exported data. ****************************************************************************/ @@ -582,23 +118,15 @@ static const testcase_t rt_test_009_007 = { */ const testcase_t * const rt_test_sequence_009_array[] = { &rt_test_009_001, - &rt_test_009_002, - &rt_test_009_003, - &rt_test_009_004, - &rt_test_009_005, -#if (CH_CFG_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) - &rt_test_009_006, -#endif - &rt_test_009_007, NULL }; /** - * @brief Event Sources and Event Flags. + * @brief Synchronous Messages. */ const testsequence_t rt_test_sequence_009 = { - "Event Sources and Event Flags", + "Synchronous Messages", rt_test_sequence_009_array }; -#endif /* CH_CFG_USE_EVENTS */ +#endif /* CH_CFG_USE_MESSAGES */ diff --git a/test/rt/source/test/rt_test_sequence_010.c b/test/rt/source/test/rt_test_sequence_010.c index 10ac2080e..7b1052d7e 100644 --- a/test/rt/source/test/rt_test_sequence_010.c +++ b/test/rt/source/test/rt_test_sequence_010.c @@ -21,260 +21,557 @@ * @file rt_test_sequence_010.c * @brief Test Sequence 010 code. * - * @page rt_test_sequence_010 [10] Dynamic threads + * @page rt_test_sequence_010 [10] Event Sources and Event Flags * * File: @ref rt_test_sequence_010.c * *

Description

- * This module implements the test sequence for the dynamic thread - * creation APIs. + * This module implements the test sequence for the Events subsystem. * *

Conditions

* This sequence is only executed if the following preprocessor condition * evaluates to true: - * - CH_CFG_USE_DYNAMIC + * - CH_CFG_USE_EVENTS * . * *

Test Cases

* - @subpage rt_test_010_001 * - @subpage rt_test_010_002 + * - @subpage rt_test_010_003 + * - @subpage rt_test_010_004 + * - @subpage rt_test_010_005 + * - @subpage rt_test_010_006 + * - @subpage rt_test_010_007 * . */ -#if (CH_CFG_USE_DYNAMIC) || defined(__DOXYGEN__) +#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) /**************************************************************************** * Shared code. ****************************************************************************/ -#if CH_CFG_USE_HEAP -static memory_heap_t heap1; -#endif -#if CH_CFG_USE_MEMPOOLS -static memory_pool_t mp1; -#endif +static EVENTSOURCE_DECL(es1); +static EVENTSOURCE_DECL(es2); -static THD_FUNCTION(dyn_thread1, p) { +static void h1(eventid_t id) {(void)id;test_emit_token('A');} +static void h2(eventid_t id) {(void)id;test_emit_token('B');} +static void h3(eventid_t id) {(void)id;test_emit_token('C');} +static ROMCONST evhandler_t evhndl[] = {h1, h2, h3}; - test_emit_token(*(char *)p); +static THD_FUNCTION(evt_thread3, p) { + + chThdSleepMilliseconds(50); + chEvtSignal((thread_t *)p, 1); +} + +static THD_FUNCTION(evt_thread7, p) { + + (void)p; + chEvtBroadcast(&es1); + chThdSleepMilliseconds(50); + chEvtBroadcast(&es2); } /**************************************************************************** * Test cases. ****************************************************************************/ -#if (CH_CFG_USE_HEAP) || defined(__DOXYGEN__) /** - * @page rt_test_010_001 [10.1] Threads creation from Memory Heap + * @page rt_test_010_001 [10.1] Events registration * *

Description

- * Two threads are started by allocating the memory from the Memory - * Heap then a third thread is started with a huge stack - * requirement.
The test expects the first two threads to - * successfully start and the third one to fail. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_HEAP - * . + * Two event listeners are registered on an event source and then + * unregistered in the same order.
The test expects that the even + * source has listeners after the registrations and after the first + * unregistration, then, after the second unegistration, the test + * expects no more listeners. * *

Test Steps

- * - [10.1.1] Getting base priority for threads. - * - [10.1.2] Getting heap info before the test. - * - [10.1.3] Creating thread 1, it is expected to succeed. - * - [10.1.4] Creating thread 2, it is expected to succeed. - * - [10.1.5] Creating thread 3, it is expected to fail. - * - [10.1.6] Letting threads execute then checking the start order and - * freeing memory. - * - [10.1.7] Getting heap info again for verification. + * - [10.1.1] An Event Source is initialized. + * - [10.1.2] Two Event Listeners are registered on the Event Source, + * the Event Source is tested to have listeners. + * - [10.1.3] An Event Listener is unregistered, the Event Source must + * still have listeners. + * - [10.1.4] An Event Listener is unregistered, the Event Source must + * not have listeners. * . */ -static void rt_test_010_001_setup(void) { - chHeapObjectInit(&heap1, test_buffer, sizeof test_buffer); -} - static void rt_test_010_001_execute(void) { - size_t n1, total1, largest1; - size_t n2, total2, largest2; - tprio_t prio; + event_listener_t el1, el2; - /* [10.1.1] Getting base priority for threads.*/ + /* [10.1.1] An Event Source is initialized.*/ test_set_step(1); { - prio = chThdGetPriorityX(); + chEvtObjectInit(&es1); } test_end_step(1); - /* [10.1.2] Getting heap info before the test.*/ + /* [10.1.2] Two Event Listeners are registered on the Event Source, + the Event Source is tested to have listeners.*/ test_set_step(2); { - n1 = chHeapStatus(&heap1, &total1, &largest1); - test_assert(n1 == 1, "heap fragmented"); + chEvtRegisterMask(&es1, &el1, 1); + chEvtRegisterMask(&es1, &el2, 2); + test_assert_lock(chEvtIsListeningI(&es1), "no listener"); } test_end_step(2); - /* [10.1.3] Creating thread 1, it is expected to succeed.*/ + /* [10.1.3] An Event Listener is unregistered, the Event Source must + still have listeners.*/ test_set_step(3); { - threads[0] = chThdCreateFromHeap(&heap1, - THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), - "dyn1", - prio-1, dyn_thread1, "A"); - test_assert(threads[0] != NULL, "thread creation failed"); + chEvtUnregister(&es1, &el1); + test_assert_lock(chEvtIsListeningI(&es1), "no listener"); } test_end_step(3); - /* [10.1.4] Creating thread 2, it is expected to succeed.*/ + /* [10.1.4] An Event Listener is unregistered, the Event Source must + not have listeners.*/ test_set_step(4); { - threads[1] = chThdCreateFromHeap(&heap1, - THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), - "dyn2", - prio-2, dyn_thread1, "B"); - test_assert(threads[1] != NULL, "thread creation failed"); + chEvtUnregister(&es1, &el2); + test_assert_lock(!chEvtIsListeningI(&es1), "stuck listener"); } test_end_step(4); - - /* [10.1.5] Creating thread 3, it is expected to fail.*/ - test_set_step(5); - { - threads[2] = chThdCreateFromHeap(&heap1, - (((size_t)-1) >> 1U) + 1U, - "dyn3", - prio-3, dyn_thread1, "C"); - test_assert(threads[2] == NULL, "thread creation not failed"); - } - test_end_step(5); - - /* [10.1.6] Letting threads execute then checking the start order and - freeing memory.*/ - test_set_step(6); - { - test_wait_threads(); - test_assert_sequence("AB", "invalid sequence"); - } - test_end_step(6); - - /* [10.1.7] Getting heap info again for verification.*/ - test_set_step(7); - { - n2 = chHeapStatus(&heap1, &total2, &largest2); - test_assert(n1 == n2, "fragmentation changed"); - test_assert(total1 == total2, "total free space changed"); - test_assert(largest1 == largest2, "largest fragment size changed"); - } - test_end_step(7); } static const testcase_t rt_test_010_001 = { - "Threads creation from Memory Heap", - rt_test_010_001_setup, + "Events registration", + NULL, NULL, rt_test_010_001_execute }; -#endif /* CH_CFG_USE_HEAP */ -#if (CH_CFG_USE_MEMPOOLS) || defined(__DOXYGEN__) /** - * @page rt_test_010_002 [10.2] Threads creation from Memory Pool + * @page rt_test_010_002 [10.2] Event Flags dispatching * *

Description

- * Five thread creation are attempted from a pool containing only four - * elements.
The test expects the first four threads to - * successfully start and the last one to fail. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_MEMPOOLS - * . + * The test dispatches three event flags and verifies that the + * associated event handlers are invoked in LSb-first order. * *

Test Steps

- * - [10.2.1] Adding four working areas to the pool. - * - [10.2.2] Getting base priority for threads. - * - [10.2.3] Creating the five threads. - * - [10.2.4] Testing that only the fifth thread creation failed. - * - [10.2.5] Letting them run, free the memory then checking the - * execution sequence. - * - [10.2.6] Testing that the pool contains four elements again. + * - [10.2.1] Three evenf flag bits are raised then chEvtDispatch() is + * invoked, the sequence of handlers calls is tested. * . */ static void rt_test_010_002_setup(void) { - chPoolObjectInit(&mp1, THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), NULL); + chEvtGetAndClearEvents(ALL_EVENTS); } static void rt_test_010_002_execute(void) { - unsigned i; - tprio_t prio; - /* [10.2.1] Adding four working areas to the pool.*/ + /* [10.2.1] Three evenf flag bits are raised then chEvtDispatch() is + invoked, the sequence of handlers calls is tested.*/ test_set_step(1); { - for (i = 0; i < 4; i++) - chPoolFree(&mp1, wa[i]); + chEvtDispatch(evhndl, 7); + test_assert_sequence("ABC", "invalid sequence"); } test_end_step(1); - - /* [10.2.2] Getting base priority for threads.*/ - test_set_step(2); - { - prio = chThdGetPriorityX(); - } - test_end_step(2); - - /* [10.2.3] Creating the five threads.*/ - test_set_step(3); - { - threads[0] = chThdCreateFromMemoryPool(&mp1, "dyn1", prio-1, dyn_thread1, "A"); - threads[1] = chThdCreateFromMemoryPool(&mp1, "dyn2", prio-2, dyn_thread1, "B"); - threads[2] = chThdCreateFromMemoryPool(&mp1, "dyn3", prio-3, dyn_thread1, "C"); - threads[3] = chThdCreateFromMemoryPool(&mp1, "dyn4", prio-4, dyn_thread1, "D"); - threads[4] = chThdCreateFromMemoryPool(&mp1, "dyn5", prio-5, dyn_thread1, "E"); - } - test_end_step(3); - - /* [10.2.4] Testing that only the fifth thread creation failed.*/ - test_set_step(4); - { - test_assert((threads[0] != NULL) && - (threads[1] != NULL) && - (threads[2] != NULL) && - (threads[3] != NULL), - "thread creation failed"); - test_assert(threads[4] == NULL, - "thread creation not failed"); - } - test_end_step(4); - - /* [10.2.5] Letting them run, free the memory then checking the - execution sequence.*/ - test_set_step(5); - { - test_wait_threads(); - test_assert_sequence("ABCD", "invalid sequence"); - } - test_end_step(5); - - /* [10.2.6] Testing that the pool contains four elements again.*/ - test_set_step(6); - { - for (i = 0; i < 4; i++) - test_assert(chPoolAlloc(&mp1) != NULL, "pool list empty"); - test_assert(chPoolAlloc(&mp1) == NULL, "pool list not empty"); - } - test_end_step(6); } static const testcase_t rt_test_010_002 = { - "Threads creation from Memory Pool", + "Event Flags dispatching", rt_test_010_002_setup, NULL, rt_test_010_002_execute }; -#endif /* CH_CFG_USE_MEMPOOLS */ + +/** + * @page rt_test_010_003 [10.3] Events Flags wait using chEvtWaitOne() + * + *

Description

+ * Functionality of chEvtWaitOne() is tested under various scenarios. + * + *

Test Steps

+ * - [10.3.1] Setting three event flags. + * - [10.3.2] Calling chEvtWaitOne() three times, each time a single + * flag must be returned in order of priority. + * - [10.3.3] Getting current time and starting a signaler thread, the + * thread will set an event flag after 50mS. + * - [10.3.4] Calling chEvtWaitOne() then verifying that the event has + * been received after 50mS and that the event flags mask has been + * emptied. + * . + */ + +static void rt_test_010_003_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void rt_test_010_003_execute(void) { + eventmask_t m; + systime_t target_time; + + /* [10.3.1] Setting three event flags.*/ + test_set_step(1); + { + chEvtAddEvents(7); + } + test_end_step(1); + + /* [10.3.2] Calling chEvtWaitOne() three times, each time a single + flag must be returned in order of priority.*/ + test_set_step(2); + { + m = chEvtWaitOne(ALL_EVENTS); + test_assert(m == 1, "single event error"); + m = chEvtWaitOne(ALL_EVENTS); + test_assert(m == 2, "single event error"); + m = chEvtWaitOne(ALL_EVENTS); + test_assert(m == 4, "single event error"); + m = chEvtGetAndClearEvents(ALL_EVENTS); + test_assert(m == 0, "stuck event"); + } + test_end_step(2); + + /* [10.3.3] Getting current time and starting a signaler thread, the + thread will set an event flag after 50mS.*/ + test_set_step(3); + { + target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50)); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, + evt_thread3, chThdGetSelfX()); + } + test_end_step(3); + + /* [10.3.4] Calling chEvtWaitOne() then verifying that the event has + been received after 50mS and that the event flags mask has been + emptied.*/ + test_set_step(4); + { + m = chEvtWaitOne(ALL_EVENTS); + test_assert_time_window(target_time, + chTimeAddX(target_time, ALLOWED_DELAY), + "out of time window"); + test_assert(m == 1, "event flag error"); + m = chEvtGetAndClearEvents(ALL_EVENTS); + test_assert(m == 0, "stuck event"); + test_wait_threads(); + } + test_end_step(4); +} + +static const testcase_t rt_test_010_003 = { + "Events Flags wait using chEvtWaitOne()", + rt_test_010_003_setup, + NULL, + rt_test_010_003_execute +}; + +/** + * @page rt_test_010_004 [10.4] Events Flags wait using chEvtWaitAny() + * + *

Description

+ * Functionality of chEvtWaitAny() is tested under various scenarios. + * + *

Test Steps

+ * - [10.4.1] Setting two, non contiguous, event flags. + * - [10.4.2] Calling chEvtWaitAny() one time, the two flags must be + * returned. + * - [10.4.3] Getting current time and starting a signaler thread, the + * thread will set an event flag after 50mS. + * - [10.4.4] Calling chEvtWaitAny() then verifying that the event has + * been received after 50mS and that the event flags mask has been + * emptied. + * . + */ + +static void rt_test_010_004_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void rt_test_010_004_execute(void) { + eventmask_t m; + systime_t target_time; + + /* [10.4.1] Setting two, non contiguous, event flags.*/ + test_set_step(1); + { + chEvtAddEvents(5); + } + test_end_step(1); + + /* [10.4.2] Calling chEvtWaitAny() one time, the two flags must be + returned.*/ + test_set_step(2); + { + m = chEvtWaitAny(ALL_EVENTS); + test_assert(m == 5, "unexpected pending bit"); + m = chEvtGetAndClearEvents(ALL_EVENTS); + test_assert(m == 0, "stuck event"); + } + test_end_step(2); + + /* [10.4.3] Getting current time and starting a signaler thread, the + thread will set an event flag after 50mS.*/ + test_set_step(3); + { + target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50)); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, + evt_thread3, chThdGetSelfX()); + } + test_end_step(3); + + /* [10.4.4] Calling chEvtWaitAny() then verifying that the event has + been received after 50mS and that the event flags mask has been + emptied.*/ + test_set_step(4); + { + m = chEvtWaitAny(ALL_EVENTS); + test_assert_time_window(target_time, + chTimeAddX(target_time, ALLOWED_DELAY), + "out of time window"); + test_assert(m == 1, "event flag error"); + m = chEvtGetAndClearEvents(ALL_EVENTS); + test_assert(m == 0, "stuck event"); + test_wait_threads(); + } + test_end_step(4); +} + +static const testcase_t rt_test_010_004 = { + "Events Flags wait using chEvtWaitAny()", + rt_test_010_004_setup, + NULL, + rt_test_010_004_execute +}; + +/** + * @page rt_test_010_005 [10.5] Events Flags wait using chEvtWaitAll() + * + *

Description

+ * Functionality of chEvtWaitAll() is tested under various scenarios. + * + *

Test Steps

+ * - [10.5.1] Setting two, non contiguous, event flags. + * - [10.5.2] Calling chEvtWaitAll() one time, the two flags must be + * returned. + * - [10.5.3] Setting one event flag. + * - [10.5.4] Getting current time and starting a signaler thread, the + * thread will set another event flag after 50mS. + * - [10.5.5] Calling chEvtWaitAll() then verifying that both event + * flags have been received after 50mS and that the event flags mask + * has been emptied. + * . + */ + +static void rt_test_010_005_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void rt_test_010_005_execute(void) { + eventmask_t m; + systime_t target_time; + + /* [10.5.1] Setting two, non contiguous, event flags.*/ + test_set_step(1); + { + chEvtAddEvents(5); + } + test_end_step(1); + + /* [10.5.2] Calling chEvtWaitAll() one time, the two flags must be + returned.*/ + test_set_step(2); + { + m = chEvtWaitAll(5); + test_assert(m == 5, "unexpected pending bit"); + m = chEvtGetAndClearEvents(ALL_EVENTS); + test_assert(m == 0, "stuck event"); + } + test_end_step(2); + + /* [10.5.3] Setting one event flag.*/ + test_set_step(3); + { + chEvtAddEvents(4); + } + test_end_step(3); + + /* [10.5.4] Getting current time and starting a signaler thread, the + thread will set another event flag after 50mS.*/ + test_set_step(4); + { + target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50)); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, + evt_thread3, chThdGetSelfX()); + } + test_end_step(4); + + /* [10.5.5] Calling chEvtWaitAll() then verifying that both event + flags have been received after 50mS and that the event flags mask + has been emptied.*/ + test_set_step(5); + { + m = chEvtWaitAll(5); + test_assert_time_window(target_time, + chTimeAddX(target_time, ALLOWED_DELAY), + "out of time window"); + test_assert(m == 5, "event flags error"); + m = chEvtGetAndClearEvents(ALL_EVENTS); + test_assert(m == 0, "stuck event"); + test_wait_threads(); + } + test_end_step(5); +} + +static const testcase_t rt_test_010_005 = { + "Events Flags wait using chEvtWaitAll()", + rt_test_010_005_setup, + NULL, + rt_test_010_005_execute +}; + +#if (CH_CFG_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) +/** + * @page rt_test_010_006 [10.6] Events Flags wait timeouts + * + *

Description

+ * Timeout functionality is tested for chEvtWaitOneTimeout(), + * chEvtWaitAnyTimeout() and chEvtWaitAllTimeout(). + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_EVENTS_TIMEOUT + * . + * + *

Test Steps

+ * - [10.6.1] The functions are invoked first with TIME_IMMEDIATE + * timeout, the timeout condition is tested. + * - [10.6.2] The functions are invoked first with a 50mS timeout, the + * timeout condition is tested. + * . + */ + +static void rt_test_010_006_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void rt_test_010_006_execute(void) { + eventmask_t m; + + /* [10.6.1] The functions are invoked first with TIME_IMMEDIATE + timeout, the timeout condition is tested.*/ + test_set_step(1); + { + m = chEvtWaitOneTimeout(ALL_EVENTS, TIME_IMMEDIATE); + test_assert(m == 0, "spurious event"); + m = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_IMMEDIATE); + test_assert(m == 0, "spurious event"); + m = chEvtWaitAllTimeout(ALL_EVENTS, TIME_IMMEDIATE); + test_assert(m == 0, "spurious event"); + } + test_end_step(1); + + /* [10.6.2] The functions are invoked first with a 50mS timeout, the + timeout condition is tested.*/ + test_set_step(2); + { + m = chEvtWaitOneTimeout(ALL_EVENTS, TIME_MS2I(50)); + test_assert(m == 0, "spurious event"); + m = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(50)); + test_assert(m == 0, "spurious event"); + m = chEvtWaitAllTimeout(ALL_EVENTS, TIME_MS2I(50)); + test_assert(m == 0, "spurious event"); + } + test_end_step(2); +} + +static const testcase_t rt_test_010_006 = { + "Events Flags wait timeouts", + rt_test_010_006_setup, + NULL, + rt_test_010_006_execute +}; +#endif /* CH_CFG_USE_EVENTS_TIMEOUT */ + +/** + * @page rt_test_010_007 [10.7] Broadcasting using chEvtBroadcast() + * + *

Description

+ * Functionality of chEvtBroadcast() is tested. + * + *

Test Steps

+ * - [10.7.1] Registering on two event sources associating them with + * flags 1 and 4. + * - [10.7.2] Getting current time and starting a broadcaster thread, + * the thread broadcast the first Event Source immediately and the + * other after 50mS. + * - [10.7.3] Calling chEvtWaitAll() then verifying that both event + * flags have been received after 50mS and that the event flags mask + * has been emptied. + * - [10.7.4] Unregistering from the Event Sources. + * . + */ + +static void rt_test_010_007_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); + chEvtObjectInit(&es1); + chEvtObjectInit(&es2); +} + +static void rt_test_010_007_execute(void) { + eventmask_t m; + event_listener_t el1, el2; + systime_t target_time; + + /* [10.7.1] Registering on two event sources associating them with + flags 1 and 4.*/ + test_set_step(1); + { + chEvtRegisterMask(&es1, &el1, 1); + chEvtRegisterMask(&es2, &el2, 4); + } + test_end_step(1); + + /* [10.7.2] Getting current time and starting a broadcaster thread, + the thread broadcast the first Event Source immediately and the + other after 50mS.*/ + test_set_step(2); + { + target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50)); + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1, + evt_thread7, "A"); + } + test_end_step(2); + + /* [10.7.3] Calling chEvtWaitAll() then verifying that both event + flags have been received after 50mS and that the event flags mask + has been emptied.*/ + test_set_step(3); + { + m = chEvtWaitAll(5); + test_assert_time_window(target_time, + chTimeAddX(target_time, ALLOWED_DELAY), + "out of time window"); + m = chEvtGetAndClearEvents(ALL_EVENTS); + test_assert(m == 0, "stuck event"); + test_wait_threads(); + } + test_end_step(3); + + /* [10.7.4] Unregistering from the Event Sources.*/ + test_set_step(4); + { + chEvtUnregister(&es1, &el1); + chEvtUnregister(&es2, &el2); + test_assert(!chEvtIsListeningI(&es1), "stuck listener"); + test_assert(!chEvtIsListeningI(&es2), "stuck listener"); + } + test_end_step(4); +} + +static const testcase_t rt_test_010_007 = { + "Broadcasting using chEvtBroadcast()", + rt_test_010_007_setup, + NULL, + rt_test_010_007_execute +}; /**************************************************************************** * Exported data. @@ -284,21 +581,24 @@ static const testcase_t rt_test_010_002 = { * @brief Array of test cases. */ const testcase_t * const rt_test_sequence_010_array[] = { -#if (CH_CFG_USE_HEAP) || defined(__DOXYGEN__) &rt_test_010_001, -#endif -#if (CH_CFG_USE_MEMPOOLS) || defined(__DOXYGEN__) &rt_test_010_002, + &rt_test_010_003, + &rt_test_010_004, + &rt_test_010_005, +#if (CH_CFG_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) + &rt_test_010_006, #endif + &rt_test_010_007, NULL }; /** - * @brief Dynamic threads. + * @brief Event Sources and Event Flags. */ const testsequence_t rt_test_sequence_010 = { - "Dynamic threads", + "Event Sources and Event Flags", rt_test_sequence_010_array }; -#endif /* CH_CFG_USE_DYNAMIC */ +#endif /* CH_CFG_USE_EVENTS */ diff --git a/test/rt/source/test/rt_test_sequence_011.c b/test/rt/source/test/rt_test_sequence_011.c index 768e69720..8c69c285b 100644 --- a/test/rt/source/test/rt_test_sequence_011.c +++ b/test/rt/source/test/rt_test_sequence_011.c @@ -21,1018 +21,260 @@ * @file rt_test_sequence_011.c * @brief Test Sequence 011 code. * - * @page rt_test_sequence_011 [11] Benchmarks + * @page rt_test_sequence_011 [11] Dynamic threads * * File: @ref rt_test_sequence_011.c * *

Description

- * This module implements a series of system benchmarks. The benchmarks - * are useful as a stress test and as a reference when comparing - * ChibiOS/RT with similar systems.
Objective of the test sequence - * is to provide a performance index for the most critical system - * subsystems. The performance numbers allow to discover performance - * regressions between successive ChibiOS/RT releases. + * This module implements the test sequence for the dynamic thread + * creation APIs. + * + *

Conditions

+ * This sequence is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_DYNAMIC + * . * *

Test Cases

* - @subpage rt_test_011_001 * - @subpage rt_test_011_002 - * - @subpage rt_test_011_003 - * - @subpage rt_test_011_004 - * - @subpage rt_test_011_005 - * - @subpage rt_test_011_006 - * - @subpage rt_test_011_007 - * - @subpage rt_test_011_008 - * - @subpage rt_test_011_009 - * - @subpage rt_test_011_010 - * - @subpage rt_test_011_011 - * - @subpage rt_test_011_012 * . */ +#if (CH_CFG_USE_DYNAMIC) || defined(__DOXYGEN__) + /**************************************************************************** * Shared code. ****************************************************************************/ -#if CH_CFG_USE_SEMAPHORES || defined(__DOXYGEN__) -static semaphore_t sem1; +#if CH_CFG_USE_HEAP +static memory_heap_t heap1; #endif -#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) -static mutex_t mtx1; +#if CH_CFG_USE_MEMPOOLS +static memory_pool_t mp1; #endif -static void tmo(void *param) {(void)param;} +static THD_FUNCTION(dyn_thread1, p) { -#if CH_CFG_USE_MESSAGES -static THD_FUNCTION(bmk_thread1, p) { - thread_t *tp; - msg_t msg; - - (void)p; - do { - tp = chMsgWait(); - msg = chMsgGet(tp); - chMsgRelease(tp, msg); - } while (msg); -} - -NOINLINE static unsigned int msg_loop_test(thread_t *tp) { - systime_t start, end; - - uint32_t n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - (void)chMsgSend(tp, 1); - n++; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while (chVTIsSystemTimeWithinX(start, end)); - (void)chMsgSend(tp, 0); - return n; -} -#endif - -static THD_FUNCTION(bmk_thread3, p) { - - chThdExit((msg_t)p); -} - -static THD_FUNCTION(bmk_thread4, p) { - msg_t msg; - thread_t *self = chThdGetSelfX(); - - (void)p; - chSysLock(); - do { - chSchGoSleepS(CH_STATE_SUSPENDED); - msg = self->u.rdymsg; - } while (msg == MSG_OK); - chSysUnlock(); -} - -#if CH_CFG_USE_SEMAPHORES -static THD_FUNCTION(bmk_thread7, p) { - - (void)p; - while (!chThdShouldTerminateX()) - chSemWait(&sem1); -} -#endif - -static THD_FUNCTION(bmk_thread8, p) { - - do { - chThdYield(); - chThdYield(); - chThdYield(); - chThdYield(); - (*(uint32_t *)p) += 4; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while(!chThdShouldTerminateX()); + test_emit_token(*(char *)p); } /**************************************************************************** * Test cases. ****************************************************************************/ -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_HEAP) || defined(__DOXYGEN__) /** - * @page rt_test_011_001 [11.1] Messages performance #1 + * @page rt_test_011_001 [11.1] Threads creation from Memory Heap * *

Description

- * A message server thread is created with a lower priority than the - * client thread, the messages throughput per second is measured and - * the result printed on the output log. + * Two threads are started by allocating the memory from the Memory + * Heap then a third thread is started with a huge stack + * requirement.
The test expects the first two threads to + * successfully start and the third one to fail. * *

Conditions

* This test is only executed if the following preprocessor condition * evaluates to true: - * - CH_CFG_USE_MESSAGES + * - CH_CFG_USE_HEAP * . * *

Test Steps

- * - [11.1.1] The messenger thread is started at a lower priority than - * the current thread. - * - [11.1.2] The number of messages exchanged is counted in a one - * second time window. - * - [11.1.3] Score is printed. + * - [11.1.1] Getting base priority for threads. + * - [11.1.2] Getting heap info before the test. + * - [11.1.3] Creating thread 1, it is expected to succeed. + * - [11.1.4] Creating thread 2, it is expected to succeed. + * - [11.1.5] Creating thread 3, it is expected to fail. + * - [11.1.6] Letting threads execute then checking the start order and + * freeing memory. + * - [11.1.7] Getting heap info again for verification. * . */ +static void rt_test_011_001_setup(void) { + chHeapObjectInit(&heap1, test_buffer, sizeof test_buffer); +} + static void rt_test_011_001_execute(void) { - uint32_t n; + size_t n1, total1, largest1; + size_t n2, total2, largest2; + tprio_t prio; - /* [11.1.1] The messenger thread is started at a lower priority than - the current thread.*/ + /* [11.1.1] Getting base priority for threads.*/ test_set_step(1); { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, bmk_thread1, NULL); + prio = chThdGetPriorityX(); } test_end_step(1); - /* [11.1.2] The number of messages exchanged is counted in a one - second time window.*/ + /* [11.1.2] Getting heap info before the test.*/ test_set_step(2); { - n = msg_loop_test(threads[0]); - test_wait_threads(); + n1 = chHeapStatus(&heap1, &total1, &largest1); + test_assert(n1 == 1, "heap fragmented"); } test_end_step(2); - /* [11.1.3] Score is printed.*/ + /* [11.1.3] Creating thread 1, it is expected to succeed.*/ test_set_step(3); { - test_print("--- Score : "); - test_printn(n); - test_print(" msgs/S, "); - test_printn(n << 1); - test_println(" ctxswc/S"); - } - test_end_step(3); -} - -static const testcase_t rt_test_011_001 = { - "Messages performance #1", - NULL, - NULL, - rt_test_011_001_execute -}; -#endif /* CH_CFG_USE_MESSAGES */ - -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) -/** - * @page rt_test_011_002 [11.2] Messages performance #2 - * - *

Description

- * A message server thread is created with an higher priority than the - * client thread, the messages throughput per second is measured and - * the result printed on the output log. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_MESSAGES - * . - * - *

Test Steps

- * - [11.2.1] The messenger thread is started at an higher priority - * than the current thread. - * - [11.2.2] The number of messages exchanged is counted in a one - * second time window. - * - [11.2.3] Score is printed. - * . - */ - -static void rt_test_011_002_execute(void) { - uint32_t n; - - /* [11.2.1] The messenger thread is started at an higher priority - than the current thread.*/ - test_set_step(1); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, bmk_thread1, NULL); - } - test_end_step(1); - - /* [11.2.2] The number of messages exchanged is counted in a one - second time window.*/ - test_set_step(2); - { - n = msg_loop_test(threads[0]); - test_wait_threads(); - } - test_end_step(2); - - /* [11.2.3] Score is printed.*/ - test_set_step(3); - { - test_print("--- Score : "); - test_printn(n); - test_print(" msgs/S, "); - test_printn(n << 1); - test_println(" ctxswc/S"); - } - test_end_step(3); -} - -static const testcase_t rt_test_011_002 = { - "Messages performance #2", - NULL, - NULL, - rt_test_011_002_execute -}; -#endif /* CH_CFG_USE_MESSAGES */ - -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) -/** - * @page rt_test_011_003 [11.3] Messages performance #3 - * - *

Description

- * A message server thread is created with an higher priority than the - * client thread, four lower priority threads crowd the ready list, the - * messages throughput per second is measured while the ready list and - * the result printed on the output log. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_MESSAGES - * . - * - *

Test Steps

- * - [11.3.1] The messenger thread is started at an higher priority - * than the current thread. - * - [11.3.2] Four threads are started at a lower priority than the - * current thread. - * - [11.3.3] The number of messages exchanged is counted in a one - * second time window. - * - [11.3.4] Score is printed. - * . - */ - -static void rt_test_011_003_execute(void) { - uint32_t n; - - /* [11.3.1] The messenger thread is started at an higher priority - than the current thread.*/ - test_set_step(1); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, bmk_thread1, NULL); - } - test_end_step(1); - - /* [11.3.2] Four threads are started at a lower priority than the - current thread.*/ - test_set_step(2); - { - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-2, bmk_thread3, NULL); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, bmk_thread3, NULL); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-4, bmk_thread3, NULL); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-5, bmk_thread3, NULL); - } - test_end_step(2); - - /* [11.3.3] The number of messages exchanged is counted in a one - second time window.*/ - test_set_step(3); - { - n = msg_loop_test(threads[0]); - test_wait_threads(); + threads[0] = chThdCreateFromHeap(&heap1, + THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), + "dyn1", + prio-1, dyn_thread1, "A"); + test_assert(threads[0] != NULL, "thread creation failed"); } test_end_step(3); - /* [11.3.4] Score is printed.*/ + /* [11.1.4] Creating thread 2, it is expected to succeed.*/ test_set_step(4); { - test_print("--- Score : "); - test_printn(n); - test_print(" msgs/S, "); - test_printn(n << 1); - test_println(" ctxswc/S"); - } - test_end_step(4); -} - -static const testcase_t rt_test_011_003 = { - "Messages performance #3", - NULL, - NULL, - rt_test_011_003_execute -}; -#endif /* CH_CFG_USE_MESSAGES */ - -/** - * @page rt_test_011_004 [11.4] Context Switch performance - * - *

Description

- * A thread is created that just performs a @p chSchGoSleepS() into a - * loop, the thread is awakened as fast is possible by the tester - * thread.
The Context Switch performance is calculated by - * measuring the number of iterations after a second of continuous - * operations. - * - *

Test Steps

- * - [11.4.1] Starting the target thread at an higher priority level. - * - [11.4.2] Waking up the thread as fast as possible in a one second - * time window. - * - [11.4.3] Stopping the target thread. - * - [11.4.4] Score is printed. - * . - */ - -static void rt_test_011_004_execute(void) { - thread_t *tp; - uint32_t n; - - /* [11.4.1] Starting the target thread at an higher priority level.*/ - test_set_step(1); - { - tp = threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, - bmk_thread4, NULL); - } - test_end_step(1); - - /* [11.4.2] Waking up the thread as fast as possible in a one second - time window.*/ - test_set_step(2); - { - systime_t start, end; - - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - chSysLock(); - chSchWakeupS(tp, MSG_OK); - chSchWakeupS(tp, MSG_OK); - chSchWakeupS(tp, MSG_OK); - chSchWakeupS(tp, MSG_OK); - chSysUnlock(); - n += 4; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(2); - - /* [11.4.3] Stopping the target thread.*/ - test_set_step(3); - { - chSysLock(); - chSchWakeupS(tp, MSG_TIMEOUT); - chSysUnlock(); - test_wait_threads(); - } - test_end_step(3); - - /* [11.4.4] Score is printed.*/ - test_set_step(4); - { - test_print("--- Score : "); - test_printn(n * 2); - test_println(" ctxswc/S"); - } - test_end_step(4); -} - -static const testcase_t rt_test_011_004 = { - "Context Switch performance", - NULL, - NULL, - rt_test_011_004_execute -}; - -/** - * @page rt_test_011_005 [11.5] Threads performance, full cycle - * - *

Description

- * Threads are continuously created and terminated into a loop. A full - * chThdCreateStatic() / @p chThdExit() / @p chThdWait() cycle is - * performed in each iteration.
The performance is calculated by - * measuring the number of iterations after a second of continuous - * operations. - * - *

Test Steps

- * - [11.5.1] A thread is created at a lower priority level and its - * termination detected using @p chThdWait(). The operation is - * repeated continuously in a one-second time window. - * - [11.5.2] Score is printed. - * . - */ - -static void rt_test_011_005_execute(void) { - uint32_t n; - tprio_t prio = chThdGetPriorityX() - 1; - systime_t start, end; - - /* [11.5.1] A thread is created at a lower priority level and its - termination detected using @p chThdWait(). The operation is - repeated continuously in a one-second time window.*/ - test_set_step(1); - { - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - chThdWait(chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL)); - n++; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(1); - - /* [11.5.2] Score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n); - test_println(" threads/S"); - } - test_end_step(2); -} - -static const testcase_t rt_test_011_005 = { - "Threads performance, full cycle", - NULL, - NULL, - rt_test_011_005_execute -}; - -/** - * @page rt_test_011_006 [11.6] Threads performance, create/exit only - * - *

Description

- * Threads are continuously created and terminated into a loop. A - * partial @p chThdCreateStatic() / @p chThdExit() cycle is performed - * in each iteration, the @p chThdWait() is not necessary because the - * thread is created at an higher priority so there is no need to wait - * for it to terminate.
The performance is calculated by measuring - * the number of iterations after a second of continuous operations. - * - *

Test Steps

- * - [11.6.1] A thread is created at an higher priority level and let - * terminate immediately. The operation is repeated continuously in a - * one-second time window. - * - [11.6.2] Score is printed. - * . - */ - -static void rt_test_011_006_execute(void) { - uint32_t n; - tprio_t prio = chThdGetPriorityX() + 1; - systime_t start, end; - - /* [11.6.1] A thread is created at an higher priority level and let - terminate immediately. The operation is repeated continuously in a - one-second time window.*/ - test_set_step(1); - { - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { -#if CH_CFG_USE_REGISTRY - chThdRelease(chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL)); -#else - chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL); -#endif - n++; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(1); - - /* [11.6.2] Score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n); - test_println(" threads/S"); - } - test_end_step(2); -} - -static const testcase_t rt_test_011_006 = { - "Threads performance, create/exit only", - NULL, - NULL, - rt_test_011_006_execute -}; - -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) -/** - * @page rt_test_011_007 [11.7] Mass reschedule performance - * - *

Description

- * Five threads are created and atomically rescheduled by resetting the - * semaphore where they are waiting on. The operation is performed into - * a continuous loop.
The performance is calculated by measuring - * the number of iterations after a second of continuous operations. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_SEMAPHORES - * . - * - *

Test Steps

- * - [11.7.1] Five threads are created at higher priority that - * immediately enqueue on a semaphore. - * - [11.7.2] The semaphore is reset waking up the five threads. The - * operation is repeated continuously in a one-second time window. - * - [11.7.3] The five threads are terminated. - * - [11.7.4] The score is printed. - * . - */ - -static void rt_test_011_007_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_011_007_execute(void) { - uint32_t n; - - /* [11.7.1] Five threads are created at higher priority that - immediately enqueue on a semaphore.*/ - test_set_step(1); - { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+5, bmk_thread7, NULL); - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()+4, bmk_thread7, NULL); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()+3, bmk_thread7, NULL); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()+2, bmk_thread7, NULL); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()+1, bmk_thread7, NULL); - } - test_end_step(1); - - /* [11.7.2] The semaphore is reset waking up the five threads. The - operation is repeated continuously in a one-second time window.*/ - test_set_step(2); - { - systime_t start, end; - - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - chSemReset(&sem1, 0); - n++; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(2); - - /* [11.7.3] The five threads are terminated.*/ - test_set_step(3); - { - test_terminate_threads(); - chSemReset(&sem1, 0); - test_wait_threads(); - } - test_end_step(3); - - /* [11.7.4] The score is printed.*/ - test_set_step(4); - { - test_print("--- Score : "); - test_printn(n); - test_print(" reschedules/S, "); - test_printn(n * 6); - test_println(" ctxswc/S"); - } - test_end_step(4); -} - -static const testcase_t rt_test_011_007 = { - "Mass reschedule performance", - rt_test_011_007_setup, - NULL, - rt_test_011_007_execute -}; -#endif /* CH_CFG_USE_SEMAPHORES */ - -/** - * @page rt_test_011_008 [11.8] Round-Robin voluntary reschedule - * - *

Description

- * Five threads are created at equal priority, each thread just - * increases a variable and yields.
The performance is calculated - * by measuring the number of iterations after a second of continuous - * operations. - * - *

Test Steps

- * - [11.8.1] The five threads are created at lower priority. The - * threds have equal priority and start calling @p chThdYield() - * continuously. - * - [11.8.2] Waiting one second then terminating the 5 threads. - * - [11.8.3] The score is printed. - * . - */ - -static void rt_test_011_008_execute(void) { - uint32_t n; - - /* [11.8.1] The five threads are created at lower priority. The - threds have equal priority and start calling @p chThdYield() - continuously.*/ - test_set_step(1); - { - n = 0; - test_wait_tick();threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); - - threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); - threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); - threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); - threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); - } - test_end_step(1); - - /* [11.8.2] Waiting one second then terminating the 5 threads.*/ - test_set_step(2); - { - chThdSleepSeconds(1); - test_terminate_threads(); - test_wait_threads(); - } - test_end_step(2); - - /* [11.8.3] The score is printed.*/ - test_set_step(3); - { - test_print("--- Score : "); - test_printn(n); - test_println(" ctxswc/S"); - } - test_end_step(3); -} - -static const testcase_t rt_test_011_008 = { - "Round-Robin voluntary reschedule", - NULL, - NULL, - rt_test_011_008_execute -}; - -/** - * @page rt_test_011_009 [11.9] Virtual Timers set/reset performance - * - *

Description

- * A virtual timer is set and immediately reset into a continuous - * loop.
The performance is calculated by measuring the number of - * iterations after a second of continuous operations. - * - *

Test Steps

- * - [11.9.1] Two timers are set then reset without waiting for their - * counter to elapse. The operation is repeated continuously in a - * one-second time window. - * - [11.9.2] The score is printed. - * . - */ - -static void rt_test_011_009_execute(void) { - static virtual_timer_t vt1, vt2; - uint32_t n; - - /* [11.9.1] Two timers are set then reset without waiting for their - counter to elapse. The operation is repeated continuously in a - one-second time window.*/ - test_set_step(1); - { - systime_t start, end; - - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - chSysLock(); - chVTDoSetI(&vt1, 1, tmo, NULL); - chVTDoSetI(&vt2, 10000, tmo, NULL); - chVTDoResetI(&vt1); - chVTDoResetI(&vt2); - chSysUnlock(); - n++; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(1); - - /* [11.9.2] The score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n * 2); - test_println(" timers/S"); - } - test_end_step(2); -} - -static const testcase_t rt_test_011_009 = { - "Virtual Timers set/reset performance", - NULL, - NULL, - rt_test_011_009_execute -}; - -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) -/** - * @page rt_test_011_010 [11.10] Semaphores wait/signal performance - * - *

Description

- * A counting semaphore is taken/released into a continuous loop, no - * Context Switch happens because the counter is always non - * negative.
The performance is calculated by measuring the number - * of iterations after a second of continuous operations. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_SEMAPHORES - * . - * - *

Test Steps

- * - [11.10.1] A semaphore is teken and released. The operation is - * repeated continuously in a one-second time window. - * - [11.10.2] The score is printed. - * . - */ - -static void rt_test_011_010_setup(void) { - chSemObjectInit(&sem1, 1); -} - -static void rt_test_011_010_execute(void) { - uint32_t n; - - /* [11.10.1] A semaphore is teken and released. The operation is - repeated continuously in a one-second time window.*/ - test_set_step(1); - { - systime_t start, end; - - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - chSemWait(&sem1); - chSemSignal(&sem1); - chSemWait(&sem1); - chSemSignal(&sem1); - chSemWait(&sem1); - chSemSignal(&sem1); - chSemWait(&sem1); - chSemSignal(&sem1); - n++; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(1); - - /* [11.10.2] The score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n * 4); - test_println(" wait+signal/S"); - } - test_end_step(2); -} - -static const testcase_t rt_test_011_010 = { - "Semaphores wait/signal performance", - rt_test_011_010_setup, - NULL, - rt_test_011_010_execute -}; -#endif /* CH_CFG_USE_SEMAPHORES */ - -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) -/** - * @page rt_test_011_011 [11.11] Mutexes lock/unlock performance - * - *

Description

- * A mutex is locked/unlocked into a continuous loop, no Context Switch - * happens because there are no other threads asking for the mutex.
- * The performance is calculated by measuring the number of iterations - * after a second of continuous operations. - * - *

Conditions

- * This test is only executed if the following preprocessor condition - * evaluates to true: - * - CH_CFG_USE_MUTEXES - * . - * - *

Test Steps

- * - [11.11.1] A mutex is locked and unlocked. The operation is - * repeated continuously in a one-second time window. - * - [11.11.2] The score is printed. - * . - */ - -static void rt_test_011_011_setup(void) { - chMtxObjectInit(&mtx1); -} - -static void rt_test_011_011_execute(void) { - uint32_t n; - - /* [11.11.1] A mutex is locked and unlocked. The operation is - repeated continuously in a one-second time window.*/ - test_set_step(1); - { - systime_t start, end; - - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - chMtxLock(&mtx1); - chMtxUnlock(&mtx1); - chMtxLock(&mtx1); - chMtxUnlock(&mtx1); - chMtxLock(&mtx1); - chMtxUnlock(&mtx1); - chMtxLock(&mtx1); - chMtxUnlock(&mtx1); - n++; -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(1); - - /* [11.11.2] The score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n * 4); - test_println(" lock+unlock/S"); - } - test_end_step(2); -} - -static const testcase_t rt_test_011_011 = { - "Mutexes lock/unlock performance", - rt_test_011_011_setup, - NULL, - rt_test_011_011_execute -}; -#endif /* CH_CFG_USE_MUTEXES */ - -/** - * @page rt_test_011_012 [11.12] RAM Footprint - * - *

Description

- * The memory size of the various kernel objects is printed. - * - *

Test Steps

- * - [11.12.1] The size of the system area is printed. - * - [11.12.2] The size of a thread structure is printed. - * - [11.12.3] The size of a virtual timer structure is printed. - * - [11.12.4] The size of a semaphore structure is printed. - * - [11.12.5] The size of a mutex is printed. - * - [11.12.6] The size of a condition variable is printed. - * - [11.12.7] The size of an event source is printed. - * - [11.12.8] The size of an event listener is printed. - * - [11.12.9] The size of a mailbox is printed. - * . - */ - -static void rt_test_011_012_execute(void) { - - /* [11.12.1] The size of the system area is printed.*/ - test_set_step(1); - { - test_print("--- System: "); - test_printn(sizeof(ch_system_t)); - test_println(" bytes"); - } - test_end_step(1); - - /* [11.12.2] The size of a thread structure is printed.*/ - test_set_step(2); - { - test_print("--- Thread: "); - test_printn(sizeof(thread_t)); - test_println(" bytes"); - } - test_end_step(2); - - /* [11.12.3] The size of a virtual timer structure is printed.*/ - test_set_step(3); - { - test_print("--- Timer : "); - test_printn(sizeof(virtual_timer_t)); - test_println(" bytes"); - } - test_end_step(3); - - /* [11.12.4] The size of a semaphore structure is printed.*/ - test_set_step(4); - { -#if CH_CFG_USE_SEMAPHORES || defined(__DOXYGEN__) - test_print("--- Semaph: "); - test_printn(sizeof(semaphore_t)); - test_println(" bytes"); -#endif + threads[1] = chThdCreateFromHeap(&heap1, + THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), + "dyn2", + prio-2, dyn_thread1, "B"); + test_assert(threads[1] != NULL, "thread creation failed"); } test_end_step(4); - /* [11.12.5] The size of a mutex is printed.*/ + /* [11.1.5] Creating thread 3, it is expected to fail.*/ test_set_step(5); { -#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) - test_print("--- Mutex : "); - test_printn(sizeof(mutex_t)); - test_println(" bytes"); -#endif + threads[2] = chThdCreateFromHeap(&heap1, + (((size_t)-1) >> 1U) + 1U, + "dyn3", + prio-3, dyn_thread1, "C"); + test_assert(threads[2] == NULL, "thread creation not failed"); } test_end_step(5); - /* [11.12.6] The size of a condition variable is printed.*/ + /* [11.1.6] Letting threads execute then checking the start order and + freeing memory.*/ test_set_step(6); { -#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__) - test_print("--- CondV.: "); - test_printn(sizeof(condition_variable_t)); - test_println(" bytes"); -#endif + test_wait_threads(); + test_assert_sequence("AB", "invalid sequence"); } test_end_step(6); - /* [11.12.7] The size of an event source is printed.*/ + /* [11.1.7] Getting heap info again for verification.*/ test_set_step(7); { -#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__) - test_print("--- EventS: "); - test_printn(sizeof(event_source_t)); - test_println(" bytes"); -#endif + n2 = chHeapStatus(&heap1, &total2, &largest2); + test_assert(n1 == n2, "fragmentation changed"); + test_assert(total1 == total2, "total free space changed"); + test_assert(largest1 == largest2, "largest fragment size changed"); } test_end_step(7); - - /* [11.12.8] The size of an event listener is printed.*/ - test_set_step(8); - { -#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__) - test_print("--- EventL: "); - test_printn(sizeof(event_listener_t)); - test_println(" bytes"); -#endif - } - test_end_step(8); - - /* [11.12.9] The size of a mailbox is printed.*/ - test_set_step(9); - { -#if CH_CFG_USE_MAILBOXES || defined(__DOXYGEN__) - test_print("--- MailB.: "); - test_printn(sizeof(mailbox_t)); - test_println(" bytes"); -#endif - } - test_end_step(9); } -static const testcase_t rt_test_011_012 = { - "RAM Footprint", +static const testcase_t rt_test_011_001 = { + "Threads creation from Memory Heap", + rt_test_011_001_setup, NULL, - NULL, - rt_test_011_012_execute + rt_test_011_001_execute }; +#endif /* CH_CFG_USE_HEAP */ + +#if (CH_CFG_USE_MEMPOOLS) || defined(__DOXYGEN__) +/** + * @page rt_test_011_002 [11.2] Threads creation from Memory Pool + * + *

Description

+ * Five thread creation are attempted from a pool containing only four + * elements.
The test expects the first four threads to + * successfully start and the last one to fail. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_MEMPOOLS + * . + * + *

Test Steps

+ * - [11.2.1] Adding four working areas to the pool. + * - [11.2.2] Getting base priority for threads. + * - [11.2.3] Creating the five threads. + * - [11.2.4] Testing that only the fifth thread creation failed. + * - [11.2.5] Letting them run, free the memory then checking the + * execution sequence. + * - [11.2.6] Testing that the pool contains four elements again. + * . + */ + +static void rt_test_011_002_setup(void) { + chPoolObjectInit(&mp1, THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), NULL); +} + +static void rt_test_011_002_execute(void) { + unsigned i; + tprio_t prio; + + /* [11.2.1] Adding four working areas to the pool.*/ + test_set_step(1); + { + for (i = 0; i < 4; i++) + chPoolFree(&mp1, wa[i]); + } + test_end_step(1); + + /* [11.2.2] Getting base priority for threads.*/ + test_set_step(2); + { + prio = chThdGetPriorityX(); + } + test_end_step(2); + + /* [11.2.3] Creating the five threads.*/ + test_set_step(3); + { + threads[0] = chThdCreateFromMemoryPool(&mp1, "dyn1", prio-1, dyn_thread1, "A"); + threads[1] = chThdCreateFromMemoryPool(&mp1, "dyn2", prio-2, dyn_thread1, "B"); + threads[2] = chThdCreateFromMemoryPool(&mp1, "dyn3", prio-3, dyn_thread1, "C"); + threads[3] = chThdCreateFromMemoryPool(&mp1, "dyn4", prio-4, dyn_thread1, "D"); + threads[4] = chThdCreateFromMemoryPool(&mp1, "dyn5", prio-5, dyn_thread1, "E"); + } + test_end_step(3); + + /* [11.2.4] Testing that only the fifth thread creation failed.*/ + test_set_step(4); + { + test_assert((threads[0] != NULL) && + (threads[1] != NULL) && + (threads[2] != NULL) && + (threads[3] != NULL), + "thread creation failed"); + test_assert(threads[4] == NULL, + "thread creation not failed"); + } + test_end_step(4); + + /* [11.2.5] Letting them run, free the memory then checking the + execution sequence.*/ + test_set_step(5); + { + test_wait_threads(); + test_assert_sequence("ABCD", "invalid sequence"); + } + test_end_step(5); + + /* [11.2.6] Testing that the pool contains four elements again.*/ + test_set_step(6); + { + for (i = 0; i < 4; i++) + test_assert(chPoolAlloc(&mp1) != NULL, "pool list empty"); + test_assert(chPoolAlloc(&mp1) == NULL, "pool list not empty"); + } + test_end_step(6); +} + +static const testcase_t rt_test_011_002 = { + "Threads creation from Memory Pool", + rt_test_011_002_setup, + NULL, + rt_test_011_002_execute +}; +#endif /* CH_CFG_USE_MEMPOOLS */ /**************************************************************************** * Exported data. @@ -1042,37 +284,21 @@ static const testcase_t rt_test_011_012 = { * @brief Array of test cases. */ const testcase_t * const rt_test_sequence_011_array[] = { -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_HEAP) || defined(__DOXYGEN__) &rt_test_011_001, #endif -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MEMPOOLS) || defined(__DOXYGEN__) &rt_test_011_002, #endif -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) - &rt_test_011_003, -#endif - &rt_test_011_004, - &rt_test_011_005, - &rt_test_011_006, -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) - &rt_test_011_007, -#endif - &rt_test_011_008, - &rt_test_011_009, -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) - &rt_test_011_010, -#endif -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) - &rt_test_011_011, -#endif - &rt_test_011_012, NULL }; /** - * @brief Benchmarks. + * @brief Dynamic threads. */ const testsequence_t rt_test_sequence_011 = { - "Benchmarks", + "Dynamic threads", rt_test_sequence_011_array }; + +#endif /* CH_CFG_USE_DYNAMIC */ diff --git a/test/rt/source/test/rt_test_sequence_012.c b/test/rt/source/test/rt_test_sequence_012.c new file mode 100644 index 000000000..0c0e770f4 --- /dev/null +++ b/test/rt/source/test/rt_test_sequence_012.c @@ -0,0 +1,1078 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "hal.h" +#include "rt_test_root.h" + +/** + * @file rt_test_sequence_012.c + * @brief Test Sequence 012 code. + * + * @page rt_test_sequence_012 [12] Benchmarks + * + * File: @ref rt_test_sequence_012.c + * + *

Description

+ * This module implements a series of system benchmarks. The benchmarks + * are useful as a stress test and as a reference when comparing + * ChibiOS/RT with similar systems.
Objective of the test sequence + * is to provide a performance index for the most critical system + * subsystems. The performance numbers allow to discover performance + * regressions between successive ChibiOS/RT releases. + * + *

Test Cases

+ * - @subpage rt_test_012_001 + * - @subpage rt_test_012_002 + * - @subpage rt_test_012_003 + * - @subpage rt_test_012_004 + * - @subpage rt_test_012_005 + * - @subpage rt_test_012_006 + * - @subpage rt_test_012_007 + * - @subpage rt_test_012_008 + * - @subpage rt_test_012_009 + * - @subpage rt_test_012_010 + * - @subpage rt_test_012_011 + * - @subpage rt_test_012_012 + * . + */ + +/**************************************************************************** + * Shared code. + ****************************************************************************/ + +#if CH_CFG_USE_SEMAPHORES || defined(__DOXYGEN__) +static semaphore_t sem1; +#endif +#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) +static mutex_t mtx1; +#endif + +static void tmo(void *param) {(void)param;} + +#if CH_CFG_USE_MESSAGES +static THD_FUNCTION(bmk_thread1, p) { + thread_t *tp; + msg_t msg; + + (void)p; + do { + tp = chMsgWait(); + msg = chMsgGet(tp); + chMsgRelease(tp, msg); + } while (msg); +} + +NOINLINE static unsigned int msg_loop_test(thread_t *tp) { + systime_t start, end; + + uint32_t n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + (void)chMsgSend(tp, 1); + n++; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); + (void)chMsgSend(tp, 0); + return n; +} +#endif + +static THD_FUNCTION(bmk_thread3, p) { + + chThdExit((msg_t)p); +} + +static THD_FUNCTION(bmk_thread4, p) { + msg_t msg; + thread_t *self = chThdGetSelfX(); + + (void)p; + chSysLock(); + do { + chSchGoSleepS(CH_STATE_SUSPENDED); + msg = self->u.rdymsg; + } while (msg == MSG_OK); + chSysUnlock(); +} + +#if CH_CFG_USE_SEMAPHORES +static THD_FUNCTION(bmk_thread7, p) { + + (void)p; + while (!chThdShouldTerminateX()) + chSemWait(&sem1); +} +#endif + +static THD_FUNCTION(bmk_thread8, p) { + + do { + chThdYield(); + chThdYield(); + chThdYield(); + chThdYield(); + (*(uint32_t *)p) += 4; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while(!chThdShouldTerminateX()); +} + +/**************************************************************************** + * Test cases. + ****************************************************************************/ + +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +/** + * @page rt_test_012_001 [12.1] Messages performance #1 + * + *

Description

+ * A message server thread is created with a lower priority than the + * client thread, the messages throughput per second is measured and + * the result printed on the output log. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_MESSAGES + * . + * + *

Test Steps

+ * - [12.1.1] The messenger thread is started at a lower priority than + * the current thread. + * - [12.1.2] The number of messages exchanged is counted in a one + * second time window. + * - [12.1.3] Score is printed. + * . + */ + +static void rt_test_012_001_execute(void) { + uint32_t n; + + /* [12.1.1] The messenger thread is started at a lower priority than + the current thread.*/ + test_set_step(1); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, bmk_thread1, NULL); + } + test_end_step(1); + + /* [12.1.2] The number of messages exchanged is counted in a one + second time window.*/ + test_set_step(2); + { + n = msg_loop_test(threads[0]); + test_wait_threads(); + } + test_end_step(2); + + /* [12.1.3] Score is printed.*/ + test_set_step(3); + { + test_print("--- Score : "); + test_printn(n); + test_print(" msgs/S, "); + test_printn(n << 1); + test_println(" ctxswc/S"); + } + test_end_step(3); +} + +static const testcase_t rt_test_012_001 = { + "Messages performance #1", + NULL, + NULL, + rt_test_012_001_execute +}; +#endif /* CH_CFG_USE_MESSAGES */ + +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +/** + * @page rt_test_012_002 [12.2] Messages performance #2 + * + *

Description

+ * A message server thread is created with an higher priority than the + * client thread, the messages throughput per second is measured and + * the result printed on the output log. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_MESSAGES + * . + * + *

Test Steps

+ * - [12.2.1] The messenger thread is started at an higher priority + * than the current thread. + * - [12.2.2] The number of messages exchanged is counted in a one + * second time window. + * - [12.2.3] Score is printed. + * . + */ + +static void rt_test_012_002_execute(void) { + uint32_t n; + + /* [12.2.1] The messenger thread is started at an higher priority + than the current thread.*/ + test_set_step(1); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, bmk_thread1, NULL); + } + test_end_step(1); + + /* [12.2.2] The number of messages exchanged is counted in a one + second time window.*/ + test_set_step(2); + { + n = msg_loop_test(threads[0]); + test_wait_threads(); + } + test_end_step(2); + + /* [12.2.3] Score is printed.*/ + test_set_step(3); + { + test_print("--- Score : "); + test_printn(n); + test_print(" msgs/S, "); + test_printn(n << 1); + test_println(" ctxswc/S"); + } + test_end_step(3); +} + +static const testcase_t rt_test_012_002 = { + "Messages performance #2", + NULL, + NULL, + rt_test_012_002_execute +}; +#endif /* CH_CFG_USE_MESSAGES */ + +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +/** + * @page rt_test_012_003 [12.3] Messages performance #3 + * + *

Description

+ * A message server thread is created with an higher priority than the + * client thread, four lower priority threads crowd the ready list, the + * messages throughput per second is measured while the ready list and + * the result printed on the output log. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_MESSAGES + * . + * + *

Test Steps

+ * - [12.3.1] The messenger thread is started at an higher priority + * than the current thread. + * - [12.3.2] Four threads are started at a lower priority than the + * current thread. + * - [12.3.3] The number of messages exchanged is counted in a one + * second time window. + * - [12.3.4] Score is printed. + * . + */ + +static void rt_test_012_003_execute(void) { + uint32_t n; + + /* [12.3.1] The messenger thread is started at an higher priority + than the current thread.*/ + test_set_step(1); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, bmk_thread1, NULL); + } + test_end_step(1); + + /* [12.3.2] Four threads are started at a lower priority than the + current thread.*/ + test_set_step(2); + { + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-2, bmk_thread3, NULL); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, bmk_thread3, NULL); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-4, bmk_thread3, NULL); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-5, bmk_thread3, NULL); + } + test_end_step(2); + + /* [12.3.3] The number of messages exchanged is counted in a one + second time window.*/ + test_set_step(3); + { + n = msg_loop_test(threads[0]); + test_wait_threads(); + } + test_end_step(3); + + /* [12.3.4] Score is printed.*/ + test_set_step(4); + { + test_print("--- Score : "); + test_printn(n); + test_print(" msgs/S, "); + test_printn(n << 1); + test_println(" ctxswc/S"); + } + test_end_step(4); +} + +static const testcase_t rt_test_012_003 = { + "Messages performance #3", + NULL, + NULL, + rt_test_012_003_execute +}; +#endif /* CH_CFG_USE_MESSAGES */ + +/** + * @page rt_test_012_004 [12.4] Context Switch performance + * + *

Description

+ * A thread is created that just performs a @p chSchGoSleepS() into a + * loop, the thread is awakened as fast is possible by the tester + * thread.
The Context Switch performance is calculated by + * measuring the number of iterations after a second of continuous + * operations. + * + *

Test Steps

+ * - [12.4.1] Starting the target thread at an higher priority level. + * - [12.4.2] Waking up the thread as fast as possible in a one second + * time window. + * - [12.4.3] Stopping the target thread. + * - [12.4.4] Score is printed. + * . + */ + +static void rt_test_012_004_execute(void) { + thread_t *tp; + uint32_t n; + + /* [12.4.1] Starting the target thread at an higher priority level.*/ + test_set_step(1); + { + tp = threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, + bmk_thread4, NULL); + } + test_end_step(1); + + /* [12.4.2] Waking up the thread as fast as possible in a one second + time window.*/ + test_set_step(2); + { + systime_t start, end; + + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + chSysLock(); + chSchWakeupS(tp, MSG_OK); + chSchWakeupS(tp, MSG_OK); + chSchWakeupS(tp, MSG_OK); + chSchWakeupS(tp, MSG_OK); + chSysUnlock(); + n += 4; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(2); + + /* [12.4.3] Stopping the target thread.*/ + test_set_step(3); + { + chSysLock(); + chSchWakeupS(tp, MSG_TIMEOUT); + chSysUnlock(); + test_wait_threads(); + } + test_end_step(3); + + /* [12.4.4] Score is printed.*/ + test_set_step(4); + { + test_print("--- Score : "); + test_printn(n * 2); + test_println(" ctxswc/S"); + } + test_end_step(4); +} + +static const testcase_t rt_test_012_004 = { + "Context Switch performance", + NULL, + NULL, + rt_test_012_004_execute +}; + +/** + * @page rt_test_012_005 [12.5] Threads performance, full cycle + * + *

Description

+ * Threads are continuously created and terminated into a loop. A full + * chThdCreateStatic() / @p chThdExit() / @p chThdWait() cycle is + * performed in each iteration.
The performance is calculated by + * measuring the number of iterations after a second of continuous + * operations. + * + *

Test Steps

+ * - [12.5.1] A thread is created at a lower priority level and its + * termination detected using @p chThdWait(). The operation is + * repeated continuously in a one-second time window. + * - [12.5.2] Score is printed. + * . + */ + +static void rt_test_012_005_execute(void) { + uint32_t n; + tprio_t prio = chThdGetPriorityX() - 1; + systime_t start, end; + + /* [12.5.1] A thread is created at a lower priority level and its + termination detected using @p chThdWait(). The operation is + repeated continuously in a one-second time window.*/ + test_set_step(1); + { + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + chThdWait(chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL)); + n++; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(1); + + /* [12.5.2] Score is printed.*/ + test_set_step(2); + { + test_print("--- Score : "); + test_printn(n); + test_println(" threads/S"); + } + test_end_step(2); +} + +static const testcase_t rt_test_012_005 = { + "Threads performance, full cycle", + NULL, + NULL, + rt_test_012_005_execute +}; + +/** + * @page rt_test_012_006 [12.6] Threads performance, create/exit only + * + *

Description

+ * Threads are continuously created and terminated into a loop. A + * partial @p chThdCreateStatic() / @p chThdExit() cycle is performed + * in each iteration, the @p chThdWait() is not necessary because the + * thread is created at an higher priority so there is no need to wait + * for it to terminate.
The performance is calculated by measuring + * the number of iterations after a second of continuous operations. + * + *

Test Steps

+ * - [12.6.1] A thread is created at an higher priority level and let + * terminate immediately. The operation is repeated continuously in a + * one-second time window. + * - [12.6.2] Score is printed. + * . + */ + +static void rt_test_012_006_execute(void) { + uint32_t n; + tprio_t prio = chThdGetPriorityX() + 1; + systime_t start, end; + + /* [12.6.1] A thread is created at an higher priority level and let + terminate immediately. The operation is repeated continuously in a + one-second time window.*/ + test_set_step(1); + { + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { +#if CH_CFG_USE_REGISTRY + chThdRelease(chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL)); +#else + chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL); +#endif + n++; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(1); + + /* [12.6.2] Score is printed.*/ + test_set_step(2); + { + test_print("--- Score : "); + test_printn(n); + test_println(" threads/S"); + } + test_end_step(2); +} + +static const testcase_t rt_test_012_006 = { + "Threads performance, create/exit only", + NULL, + NULL, + rt_test_012_006_execute +}; + +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) +/** + * @page rt_test_012_007 [12.7] Mass reschedule performance + * + *

Description

+ * Five threads are created and atomically rescheduled by resetting the + * semaphore where they are waiting on. The operation is performed into + * a continuous loop.
The performance is calculated by measuring + * the number of iterations after a second of continuous operations. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_SEMAPHORES + * . + * + *

Test Steps

+ * - [12.7.1] Five threads are created at higher priority that + * immediately enqueue on a semaphore. + * - [12.7.2] The semaphore is reset waking up the five threads. The + * operation is repeated continuously in a one-second time window. + * - [12.7.3] The five threads are terminated. + * - [12.7.4] The score is printed. + * . + */ + +static void rt_test_012_007_setup(void) { + chSemObjectInit(&sem1, 0); +} + +static void rt_test_012_007_execute(void) { + uint32_t n; + + /* [12.7.1] Five threads are created at higher priority that + immediately enqueue on a semaphore.*/ + test_set_step(1); + { + threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+5, bmk_thread7, NULL); + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()+4, bmk_thread7, NULL); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()+3, bmk_thread7, NULL); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()+2, bmk_thread7, NULL); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()+1, bmk_thread7, NULL); + } + test_end_step(1); + + /* [12.7.2] The semaphore is reset waking up the five threads. The + operation is repeated continuously in a one-second time window.*/ + test_set_step(2); + { + systime_t start, end; + + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + chSemReset(&sem1, 0); + n++; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(2); + + /* [12.7.3] The five threads are terminated.*/ + test_set_step(3); + { + test_terminate_threads(); + chSemReset(&sem1, 0); + test_wait_threads(); + } + test_end_step(3); + + /* [12.7.4] The score is printed.*/ + test_set_step(4); + { + test_print("--- Score : "); + test_printn(n); + test_print(" reschedules/S, "); + test_printn(n * 6); + test_println(" ctxswc/S"); + } + test_end_step(4); +} + +static const testcase_t rt_test_012_007 = { + "Mass reschedule performance", + rt_test_012_007_setup, + NULL, + rt_test_012_007_execute +}; +#endif /* CH_CFG_USE_SEMAPHORES */ + +/** + * @page rt_test_012_008 [12.8] Round-Robin voluntary reschedule + * + *

Description

+ * Five threads are created at equal priority, each thread just + * increases a variable and yields.
The performance is calculated + * by measuring the number of iterations after a second of continuous + * operations. + * + *

Test Steps

+ * - [12.8.1] The five threads are created at lower priority. The + * threds have equal priority and start calling @p chThdYield() + * continuously. + * - [12.8.2] Waiting one second then terminating the 5 threads. + * - [12.8.3] The score is printed. + * . + */ + +static void rt_test_012_008_execute(void) { + uint32_t n; + + /* [12.8.1] The five threads are created at lower priority. The + threds have equal priority and start calling @p chThdYield() + continuously.*/ + test_set_step(1); + { + n = 0; + test_wait_tick();threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); + + threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); + threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); + threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); + threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n); + } + test_end_step(1); + + /* [12.8.2] Waiting one second then terminating the 5 threads.*/ + test_set_step(2); + { + chThdSleepSeconds(1); + test_terminate_threads(); + test_wait_threads(); + } + test_end_step(2); + + /* [12.8.3] The score is printed.*/ + test_set_step(3); + { + test_print("--- Score : "); + test_printn(n); + test_println(" ctxswc/S"); + } + test_end_step(3); +} + +static const testcase_t rt_test_012_008 = { + "Round-Robin voluntary reschedule", + NULL, + NULL, + rt_test_012_008_execute +}; + +/** + * @page rt_test_012_009 [12.9] Virtual Timers set/reset performance + * + *

Description

+ * A virtual timer is set and immediately reset into a continuous + * loop.
The performance is calculated by measuring the number of + * iterations after a second of continuous operations. + * + *

Test Steps

+ * - [12.9.1] Two timers are set then reset without waiting for their + * counter to elapse. The operation is repeated continuously in a + * one-second time window. + * - [12.9.2] The score is printed. + * . + */ + +static void rt_test_012_009_execute(void) { + static virtual_timer_t vt1, vt2; + uint32_t n; + + /* [12.9.1] Two timers are set then reset without waiting for their + counter to elapse. The operation is repeated continuously in a + one-second time window.*/ + test_set_step(1); + { + systime_t start, end; + + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + chSysLock(); + chVTDoSetI(&vt1, 1, tmo, NULL); + chVTDoSetI(&vt2, 10000, tmo, NULL); + chVTDoResetI(&vt1); + chVTDoResetI(&vt2); + chSysUnlock(); + n++; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(1); + + /* [12.9.2] The score is printed.*/ + test_set_step(2); + { + test_print("--- Score : "); + test_printn(n * 2); + test_println(" timers/S"); + } + test_end_step(2); +} + +static const testcase_t rt_test_012_009 = { + "Virtual Timers set/reset performance", + NULL, + NULL, + rt_test_012_009_execute +}; + +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) +/** + * @page rt_test_012_010 [12.10] Semaphores wait/signal performance + * + *

Description

+ * A counting semaphore is taken/released into a continuous loop, no + * Context Switch happens because the counter is always non + * negative.
The performance is calculated by measuring the number + * of iterations after a second of continuous operations. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_SEMAPHORES + * . + * + *

Test Steps

+ * - [12.10.1] A semaphore is teken and released. The operation is + * repeated continuously in a one-second time window. + * - [12.10.2] The score is printed. + * . + */ + +static void rt_test_012_010_setup(void) { + chSemObjectInit(&sem1, 1); +} + +static void rt_test_012_010_execute(void) { + uint32_t n; + + /* [12.10.1] A semaphore is teken and released. The operation is + repeated continuously in a one-second time window.*/ + test_set_step(1); + { + systime_t start, end; + + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + chSemWait(&sem1); + chSemSignal(&sem1); + chSemWait(&sem1); + chSemSignal(&sem1); + chSemWait(&sem1); + chSemSignal(&sem1); + chSemWait(&sem1); + chSemSignal(&sem1); + n++; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(1); + + /* [12.10.2] The score is printed.*/ + test_set_step(2); + { + test_print("--- Score : "); + test_printn(n * 4); + test_println(" wait+signal/S"); + } + test_end_step(2); +} + +static const testcase_t rt_test_012_010 = { + "Semaphores wait/signal performance", + rt_test_012_010_setup, + NULL, + rt_test_012_010_execute +}; +#endif /* CH_CFG_USE_SEMAPHORES */ + +#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) +/** + * @page rt_test_012_011 [12.11] Mutexes lock/unlock performance + * + *

Description

+ * A mutex is locked/unlocked into a continuous loop, no Context Switch + * happens because there are no other threads asking for the mutex.
+ * The performance is calculated by measuring the number of iterations + * after a second of continuous operations. + * + *

Conditions

+ * This test is only executed if the following preprocessor condition + * evaluates to true: + * - CH_CFG_USE_MUTEXES + * . + * + *

Test Steps

+ * - [12.11.1] A mutex is locked and unlocked. The operation is + * repeated continuously in a one-second time window. + * - [12.11.2] The score is printed. + * . + */ + +static void rt_test_012_011_setup(void) { + chMtxObjectInit(&mtx1); +} + +static void rt_test_012_011_execute(void) { + uint32_t n; + + /* [12.11.1] A mutex is locked and unlocked. The operation is + repeated continuously in a one-second time window.*/ + test_set_step(1); + { + systime_t start, end; + + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + chMtxLock(&mtx1); + chMtxUnlock(&mtx1); + chMtxLock(&mtx1); + chMtxUnlock(&mtx1); + chMtxLock(&mtx1); + chMtxUnlock(&mtx1); + chMtxLock(&mtx1); + chMtxUnlock(&mtx1); + n++; +#if defined(SIMULATOR) + _sim_check_for_interrupts(); +#endif + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(1); + + /* [12.11.2] The score is printed.*/ + test_set_step(2); + { + test_print("--- Score : "); + test_printn(n * 4); + test_println(" lock+unlock/S"); + } + test_end_step(2); +} + +static const testcase_t rt_test_012_011 = { + "Mutexes lock/unlock performance", + rt_test_012_011_setup, + NULL, + rt_test_012_011_execute +}; +#endif /* CH_CFG_USE_MUTEXES */ + +/** + * @page rt_test_012_012 [12.12] RAM Footprint + * + *

Description

+ * The memory size of the various kernel objects is printed. + * + *

Test Steps

+ * - [12.12.1] The size of the system area is printed. + * - [12.12.2] The size of a thread structure is printed. + * - [12.12.3] The size of a virtual timer structure is printed. + * - [12.12.4] The size of a semaphore structure is printed. + * - [12.12.5] The size of a mutex is printed. + * - [12.12.6] The size of a condition variable is printed. + * - [12.12.7] The size of an event source is printed. + * - [12.12.8] The size of an event listener is printed. + * - [12.12.9] The size of a mailbox is printed. + * . + */ + +static void rt_test_012_012_execute(void) { + + /* [12.12.1] The size of the system area is printed.*/ + test_set_step(1); + { + test_print("--- System: "); + test_printn(sizeof(ch_system_t)); + test_println(" bytes"); + } + test_end_step(1); + + /* [12.12.2] The size of a thread structure is printed.*/ + test_set_step(2); + { + test_print("--- Thread: "); + test_printn(sizeof(thread_t)); + test_println(" bytes"); + } + test_end_step(2); + + /* [12.12.3] The size of a virtual timer structure is printed.*/ + test_set_step(3); + { + test_print("--- Timer : "); + test_printn(sizeof(virtual_timer_t)); + test_println(" bytes"); + } + test_end_step(3); + + /* [12.12.4] The size of a semaphore structure is printed.*/ + test_set_step(4); + { +#if CH_CFG_USE_SEMAPHORES || defined(__DOXYGEN__) + test_print("--- Semaph: "); + test_printn(sizeof(semaphore_t)); + test_println(" bytes"); +#endif + } + test_end_step(4); + + /* [12.12.5] The size of a mutex is printed.*/ + test_set_step(5); + { +#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) + test_print("--- Mutex : "); + test_printn(sizeof(mutex_t)); + test_println(" bytes"); +#endif + } + test_end_step(5); + + /* [12.12.6] The size of a condition variable is printed.*/ + test_set_step(6); + { +#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__) + test_print("--- CondV.: "); + test_printn(sizeof(condition_variable_t)); + test_println(" bytes"); +#endif + } + test_end_step(6); + + /* [12.12.7] The size of an event source is printed.*/ + test_set_step(7); + { +#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__) + test_print("--- EventS: "); + test_printn(sizeof(event_source_t)); + test_println(" bytes"); +#endif + } + test_end_step(7); + + /* [12.12.8] The size of an event listener is printed.*/ + test_set_step(8); + { +#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__) + test_print("--- EventL: "); + test_printn(sizeof(event_listener_t)); + test_println(" bytes"); +#endif + } + test_end_step(8); + + /* [12.12.9] The size of a mailbox is printed.*/ + test_set_step(9); + { +#if CH_CFG_USE_MAILBOXES || defined(__DOXYGEN__) + test_print("--- MailB.: "); + test_printn(sizeof(mailbox_t)); + test_println(" bytes"); +#endif + } + test_end_step(9); +} + +static const testcase_t rt_test_012_012 = { + "RAM Footprint", + NULL, + NULL, + rt_test_012_012_execute +}; + +/**************************************************************************** + * Exported data. + ****************************************************************************/ + +/** + * @brief Array of test cases. + */ +const testcase_t * const rt_test_sequence_012_array[] = { +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) + &rt_test_012_001, +#endif +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) + &rt_test_012_002, +#endif +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) + &rt_test_012_003, +#endif + &rt_test_012_004, + &rt_test_012_005, + &rt_test_012_006, +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) + &rt_test_012_007, +#endif + &rt_test_012_008, + &rt_test_012_009, +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) + &rt_test_012_010, +#endif +#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) + &rt_test_012_011, +#endif + &rt_test_012_012, + NULL +}; + +/** + * @brief Benchmarks. + */ +const testsequence_t rt_test_sequence_012 = { + "Benchmarks", + rt_test_sequence_012_array +}; diff --git a/test/rt/source/test/rt_test_sequence_012.h b/test/rt/source/test/rt_test_sequence_012.h new file mode 100644 index 000000000..9402969a7 --- /dev/null +++ b/test/rt/source/test/rt_test_sequence_012.h @@ -0,0 +1,27 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file rt_test_sequence_012.h + * @brief Test Sequence 012 header. + */ + +#ifndef RT_TEST_SEQUENCE_012_H +#define RT_TEST_SEQUENCE_012_H + +extern const testsequence_t rt_test_sequence_012; + +#endif /* RT_TEST_SEQUENCE_012_H */