From 00a58754675a8f1b982621c2fd5b5877fbfb1d16 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Sun, 24 Nov 2019 16:05:48 +0000 Subject: [PATCH] Fixed bugs #1060 and bug #1061. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@13192 27425a3e-05d8-49a3-a47f-9c15f0e5edd8 --- os/hal/lib/fallback/I2C/hal_i2c_lld.c | 16 +- os/hal/lib/fallback/I2C/hal_i2c_lld.h | 4 +- os/hal/osal/os-less/ARMCMx/osal.h | 3 +- os/hal/templates/osal/osal.h | 3 +- os/nil/include/ch.h | 18 +- os/nil/src/ch.c | 19 + os/rt/include/chtime.h | 4 +- readme.txt | 4 + test/nil/configuration.xml | 113 +- test/nil/nil_test.mk | 3 +- test/nil/source/test/nil_test_root.c | 12 +- test/nil/source/test/nil_test_root.h | 1 + test/nil/source/test/nil_test_sequence_002.c | 116 +- test/nil/source/test/nil_test_sequence_003.c | 288 ++--- test/nil/source/test/nil_test_sequence_004.c | 246 +++- test/nil/source/test/nil_test_sequence_005.c | 614 ++-------- test/nil/source/test/nil_test_sequence_006.c | 586 +++++++++- test/nil/source/test/nil_test_sequence_007.c | 618 +--------- test/nil/source/test/nil_test_sequence_008.c | 662 +++++++++++ test/nil/source/test/nil_test_sequence_008.h | 27 + test/rt/configuration.xml | 116 +- test/rt/rt_test.mk | 3 +- test/rt/source/test/rt_test_root.c | 14 +- test/rt/source/test/rt_test_root.h | 1 + test/rt/source/test/rt_test_sequence_001.c | 3 + test/rt/source/test/rt_test_sequence_002.c | 46 +- test/rt/source/test/rt_test_sequence_003.c | 305 +---- test/rt/source/test/rt_test_sequence_004.c | 322 +++++- test/rt/source/test/rt_test_sequence_005.c | 463 +------- test/rt/source/test/rt_test_sequence_006.c | 1033 ++++------------- test/rt/source/test/rt_test_sequence_007.c | 1071 ++++++++++++++++- test/rt/source/test/rt_test_sequence_008.c | 524 +-------- test/rt/source/test/rt_test_sequence_009.c | 639 ++++++++--- test/rt/source/test/rt_test_sequence_010.c | 1078 +++--------------- test/rt/source/test/rt_test_sequence_011.c | 1078 ++++++++++++++++++ test/rt/source/test/rt_test_sequence_011.h | 27 + 36 files changed, 5406 insertions(+), 4674 deletions(-) create mode 100644 test/nil/source/test/nil_test_sequence_008.c create mode 100644 test/nil/source/test/nil_test_sequence_008.h create mode 100644 test/rt/source/test/rt_test_sequence_011.c create mode 100644 test/rt/source/test/rt_test_sequence_011.h diff --git a/os/hal/lib/fallback/I2C/hal_i2c_lld.c b/os/hal/lib/fallback/I2C/hal_i2c_lld.c index 1482d52f5..b3674750a 100644 --- a/os/hal/lib/fallback/I2C/hal_i2c_lld.c +++ b/os/hal/lib/fallback/I2C/hal_i2c_lld.c @@ -94,7 +94,7 @@ static inline msg_t i2c_check_arbitration(I2CDriver *i2cp) { static inline msg_t i2c_check_timeout(I2CDriver *i2cp) { - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), i2cp->start, i2cp->end)) { + if (!osalTimeIsInRangeX(osalOsGetSystemTimeX(), i2cp->start, i2cp->end)) { i2c_write_stop(i2cp); return MSG_TIMEOUT; } @@ -105,7 +105,7 @@ static inline msg_t i2c_check_timeout(I2CDriver *i2cp) { static msg_t i2c_wait_clock(I2CDriver *i2cp) { while (palReadLine(i2cp->config->scl) == PAL_LOW) { - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), i2cp->start, i2cp->end)) { + if (!osalTimeIsInRangeX(osalOsGetSystemTimeX(), i2cp->start, i2cp->end)) { return MSG_TIMEOUT; } i2c_delay(i2cp); @@ -347,18 +347,18 @@ void i2c_lld_stop(I2CDriver *i2cp) { */ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { + sysinterval_t timeout) { /* Setting timeout fields.*/ i2cp->start = osalOsGetSystemTimeX(); i2cp->end = i2cp->start; if (timeout != TIME_INFINITE) { - i2cp->end += timeout; + i2cp->end = osalTimeAddX(i2cp->start, timeout); } CHECK_ERROR(i2c_write_start(i2cp)); - /* Sending anddress and mode.*/ + /* Sending address and mode.*/ CHECK_ERROR(i2c_write_header(i2cp, addr, true)); do { @@ -399,19 +399,19 @@ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, const uint8_t *txbuf, size_t txbytes, uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { + sysinterval_t timeout) { /* Setting timeout fields.*/ i2cp->start = osalOsGetSystemTimeX(); i2cp->end = i2cp->start; if (timeout != TIME_INFINITE) { - i2cp->end += timeout; + i2cp->end = osalTimeAddX(i2cp->start, timeout); } /* send start condition */ CHECK_ERROR(i2c_write_start(i2cp)); - /* Sending anddress and mode.*/ + /* Sending address and mode.*/ CHECK_ERROR(i2c_write_header(i2cp, addr, false)); do { diff --git a/os/hal/lib/fallback/I2C/hal_i2c_lld.h b/os/hal/lib/fallback/I2C/hal_i2c_lld.h index 21a7faabf..c398aa0c4 100644 --- a/os/hal/lib/fallback/I2C/hal_i2c_lld.h +++ b/os/hal/lib/fallback/I2C/hal_i2c_lld.h @@ -217,10 +217,10 @@ extern "C" { msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, const uint8_t *txbuf, size_t txbytes, uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); + sysinterval_t timeout); msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); + sysinterval_t timeout); #ifdef __cplusplus } #endif diff --git a/os/hal/osal/os-less/ARMCMx/osal.h b/os/hal/osal/os-less/ARMCMx/osal.h index 5cbd6630b..cb6195b06 100644 --- a/os/hal/osal/os-less/ARMCMx/osal.h +++ b/os/hal/osal/os-less/ARMCMx/osal.h @@ -703,7 +703,8 @@ static inline bool osalTimeIsInRangeX(systime_t time, systime_t start, systime_t end) { - return (bool)((time - start) < (end - start)); + return (bool)((systime_t)((systime_t)time - (systime_t)start) <= + (systime_t)((systime_t)end - (systime_t)start - (systime_t)1)); } /** diff --git a/os/hal/templates/osal/osal.h b/os/hal/templates/osal/osal.h index c0523db28..93e97f25f 100644 --- a/os/hal/templates/osal/osal.h +++ b/os/hal/templates/osal/osal.h @@ -639,7 +639,8 @@ static inline bool osalTimeIsInRangeX(systime_t time, systime_t start, systime_t end) { - return (bool)((time - start) < (end - start)); + return (bool)((systime_t)((systime_t)time - (systime_t)start) <= + (systime_t)((systime_t)end - (systime_t)start - (systime_t)1)); } /** diff --git a/os/nil/include/ch.h b/os/nil/include/ch.h index 798fecd86..a41c804e0 100644 --- a/os/nil/include/ch.h +++ b/os/nil/include/ch.h @@ -1278,23 +1278,6 @@ struct nil_system { #define chTimeDiffX(start, end) \ ((sysinterval_t)((systime_t)((systime_t)(end) - (systime_t)(start)))) -/** - * @brief Checks if the specified time is within the specified time range. - * @note When start==end then the function returns always true because the - * whole time range is specified. - * - * @param[in] time the time to be verified - * @param[in] start the start of the time window (inclusive) - * @param[in] end the end of the time window (non inclusive) - * @retval true current time within the specified time window. - * @retval false current time not within the specified time window. - * - * @xclass - */ -#define chTimeIsInRangeX(time, start, end) \ - ((bool)((systime_t)((systime_t)(time) - (systime_t)(start)) < \ - (systime_t)((systime_t)(end) - (systime_t)(start)))) - /** * @brief Function parameters check. * @details If the condition check fails then the kernel panics and halts. @@ -1392,6 +1375,7 @@ extern "C" { void chSchDoReschedule(void); void chSchRescheduleS(void); msg_t chSchGoSleepTimeoutS(tstate_t newstate, sysinterval_t timeout); + bool chTimeIsInRangeX(systime_t time, systime_t start, systime_t end); thread_t *chThdCreateI(const thread_descriptor_t *tdp); thread_t *chThdCreate(const thread_descriptor_t *tdp); void chThdExit(msg_t msg); diff --git a/os/nil/src/ch.c b/os/nil/src/ch.c index ea945793c..7cda68d5f 100644 --- a/os/nil/src/ch.c +++ b/os/nil/src/ch.c @@ -722,6 +722,25 @@ msg_t chSchGoSleepTimeoutS(tstate_t newstate, sysinterval_t timeout) { } } +/** + * @brief Checks if the specified time is within the specified time range. + * @note When start==end then the function returns always true because the + * whole time range is specified. + * + * @param[in] time the time to be verified + * @param[in] start the start of the time window (inclusive) + * @param[in] end the end of the time window (non inclusive) + * @retval true current time within the specified time window. + * @retval false current time not within the specified time window. + * + * @xclass + */ +bool chTimeIsInRangeX(systime_t time, systime_t start, systime_t end) { + + return (bool)((systime_t)((systime_t)(time) - (systime_t)(start)) <= + (systime_t)((systime_t)(end) - (systime_t)(start) - (systime_t)1)); +} + /** * @brief Creates a new thread into a static memory area. * @details The new thread is initialized and make ready to execute. diff --git a/os/rt/include/chtime.h b/os/rt/include/chtime.h index 608abe70e..f1034c57d 100644 --- a/os/rt/include/chtime.h +++ b/os/rt/include/chtime.h @@ -481,8 +481,8 @@ static inline bool chTimeIsInRangeX(systime_t time, systime_t start, systime_t end) { - return (bool)((systime_t)((systime_t)time - (systime_t)start) < - (systime_t)((systime_t)end - (systime_t)start)); + return (bool)((systime_t)((systime_t)time - (systime_t)start) <= + (systime_t)((systime_t)end - (systime_t)start - (systime_t)1)); } /** @} */ diff --git a/readme.txt b/readme.txt index b5029545b..11e4b3a2d 100644 --- a/readme.txt +++ b/readme.txt @@ -140,6 +140,10 @@ - HAL: Added a new interface for range-finder devices (used by EX). - HAL: Added mcuconf.h updater tool for STM32F407 (backported to 19.1.1). - NIL: Integrated NIL 4.0. +- FIX: Fixed I2C fallback driver broken (bug #1061) + (not yet)(backported to 19.1.4)(backported to 18.2.3). +- FIX: Fixed bug in chVTGetSystemTimeX() (bug #1060) + (not yet)(backported to 19.1.4)(backported to 18.2.3). - FIX: Fixed STM32 ADC1 sample time macros (bug #1059) (backported to 19.1.4)(backported to 18.2.3). - FIX: Fixed STM32 ADCv1 error callback disabled on some devices (bug #1058) diff --git a/test/nil/configuration.xml b/test/nil/configuration.xml index 49d70836b..92334ab50 100644 --- a/test/nil/configuration.xml +++ b/test/nil/configuration.xml @@ -279,10 +279,10 @@ test_println("");]]> Internal Tests - Threads Functionality. + Time and Intervals Functionality. - This sequence tests the ChibiOS/NIL functionalities related to threading. + This sequence tests the ChibiOS/NIL functionalities related to time and intervals management. @@ -328,6 +328,115 @@ while (time == chVTGetSystemTimeX()) { + + + Time ranges functionality. + + + The functionality of the API @p chTimeIsInRangeX() is tested. + + + + + + + + + + + + + + + + + + + Checking case where start == end, it must always evaluate as in range. + + + + + + + + + + + Checking boundaries for start < end. + + + + + + + + + + + Checking boundaries for start > end. + + + + + + + + + + + + + + + Internal Tests + + + Threads Functionality. + + + This sequence tests the ChibiOS/NIL functionalities related to threading. + + + + + + + + Thread Sleep functionality. diff --git a/test/nil/nil_test.mk b/test/nil/nil_test.mk index 63cf566d5..463a25d0d 100644 --- a/test/nil/nil_test.mk +++ b/test/nil/nil_test.mk @@ -6,7 +6,8 @@ TESTSRC += ${CHIBIOS}/test/nil/source/test/nil_test_root.c \ ${CHIBIOS}/test/nil/source/test/nil_test_sequence_004.c \ ${CHIBIOS}/test/nil/source/test/nil_test_sequence_005.c \ ${CHIBIOS}/test/nil/source/test/nil_test_sequence_006.c \ - ${CHIBIOS}/test/nil/source/test/nil_test_sequence_007.c + ${CHIBIOS}/test/nil/source/test/nil_test_sequence_007.c \ + ${CHIBIOS}/test/nil/source/test/nil_test_sequence_008.c # Required include directories TESTINC += ${CHIBIOS}/test/nil/source/test diff --git a/test/nil/source/test/nil_test_root.c b/test/nil/source/test/nil_test_root.c index b0139d2c6..7697d8dff 100644 --- a/test/nil/source/test/nil_test_root.c +++ b/test/nil/source/test/nil_test_root.c @@ -28,6 +28,7 @@ * - @subpage nil_test_sequence_005 * - @subpage nil_test_sequence_006 * - @subpage nil_test_sequence_007 + * - @subpage nil_test_sequence_008 * . */ @@ -51,17 +52,18 @@ const testsequence_t * const nil_test_suite_array[] = { &nil_test_sequence_001, &nil_test_sequence_002, -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) &nil_test_sequence_003, -#endif +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) &nil_test_sequence_004, -#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) - &nil_test_sequence_005, #endif -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) + &nil_test_sequence_005, +#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) &nil_test_sequence_006, #endif +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) &nil_test_sequence_007, +#endif + &nil_test_sequence_008, NULL }; diff --git a/test/nil/source/test/nil_test_root.h b/test/nil/source/test/nil_test_root.h index d0eb22d3a..709b473d4 100644 --- a/test/nil/source/test/nil_test_root.h +++ b/test/nil/source/test/nil_test_root.h @@ -31,6 +31,7 @@ #include "nil_test_sequence_005.h" #include "nil_test_sequence_006.h" #include "nil_test_sequence_007.h" +#include "nil_test_sequence_008.h" #if !defined(__DOXYGEN__) diff --git a/test/nil/source/test/nil_test_sequence_002.c b/test/nil/source/test/nil_test_sequence_002.c index 8579501a6..0a8188d88 100644 --- a/test/nil/source/test/nil_test_sequence_002.c +++ b/test/nil/source/test/nil_test_sequence_002.c @@ -21,13 +21,13 @@ * @file nil_test_sequence_002.c * @brief Test Sequence 002 code. * - * @page nil_test_sequence_002 [2] Threads Functionality + * @page nil_test_sequence_002 [2] Time and Intervals Functionality * * File: @ref nil_test_sequence_002.c * *

Description

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

Test Cases

* - @subpage nil_test_002_001 @@ -78,97 +78,73 @@ static const testcase_t nil_test_002_001 = { }; /** - * @page nil_test_002_002 [2.2] Thread Sleep functionality + * @page nil_test_002_002 [2.2] Time ranges functionality * *

Description

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

Test Steps

- * - [2.2.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. - * - [2.2.2] The current system time is read then a sleep is performed - * for 100000 microseconds and on exit the system time is verified - * again. - * - [2.2.3] The current system time is read then a sleep is performed - * for 100 milliseconds and on exit the system time is verified - * again. - * - [2.2.4] The current system time is read then a sleep is performed - * for 1 second and on exit the system time is verified again. - * - [2.2.5] Function chThdSleepUntil() is tested with a timeline of - * "now" + 100 ticks. + * - [2.2.1] Checking case where start == end, it must always evaluate + * as in range. + * - [2.2.2] Checking boundaries for start < end. + * - [2.2.3] Checking boundaries for start > end. * . */ static void nil_test_002_002_execute(void) { - systime_t time; - /* [2.2.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.*/ + /* [2.2.1] Checking case where start == end, it must always evaluate + as in range.*/ test_set_step(1); { - time = chVTGetSystemTimeX(); - chThdSleep(100); - test_assert_time_window(chTimeAddX(time, 100), - chTimeAddX(time, 100 + 1), - "out of time window"); + bool b; + + b = chTimeIsInRangeX((systime_t)0, (systime_t)0, (systime_t)0); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)-1, (systime_t)0, (systime_t)0); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)0, (systime_t)-1, (systime_t)-1); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)-1, (systime_t)-1, (systime_t)-1); + test_assert(b == true, "not in range"); } test_end_step(1); - /* [2.2.2] The current system time is read then a sleep is performed - for 100000 microseconds and on exit the system time is verified - again.*/ + /* [2.2.2] Checking boundaries for start < end.*/ test_set_step(2); { - time = chVTGetSystemTimeX(); - chThdSleepMicroseconds(100000); - test_assert_time_window(chTimeAddX(time, TIME_US2I(100000)), - chTimeAddX(time, TIME_US2I(100000) + 1), - "out of time window"); + bool b; + + b = chTimeIsInRangeX((systime_t)10, (systime_t)10, (systime_t)100); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)9, (systime_t)10, (systime_t)100); + test_assert(b == false, "in range"); + b = chTimeIsInRangeX((systime_t)99, (systime_t)10, (systime_t)100); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)100, (systime_t)10, (systime_t)100); + test_assert(b == false, "in range"); } test_end_step(2); - /* [2.2.3] The current system time is read then a sleep is performed - for 100 milliseconds and on exit the system time is verified - again.*/ + /* [2.2.3] Checking boundaries for start > end.*/ test_set_step(3); { - time = chVTGetSystemTimeX(); - chThdSleepMilliseconds(100); - test_assert_time_window(chTimeAddX(time, TIME_MS2I(100)), - chTimeAddX(time, TIME_MS2I(100) + 1), - "out of time window"); + bool b; + + b = chTimeIsInRangeX((systime_t)100, (systime_t)100, (systime_t)10); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)99, (systime_t)100, (systime_t)10); + test_assert(b == false, "in range"); + b = chTimeIsInRangeX((systime_t)9, (systime_t)100, (systime_t)10); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)10, (systime_t)100, (systime_t)10); + test_assert(b == false, "in range"); } test_end_step(3); - - /* [2.2.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) + 1), - "out of time window"); - } - test_end_step(4); - - /* [2.2.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 + 1), - "out of time window"); - } - test_end_step(5); } static const testcase_t nil_test_002_002 = { - "Thread Sleep functionality", + "Time ranges functionality", NULL, NULL, nil_test_002_002_execute @@ -188,9 +164,9 @@ const testcase_t * const nil_test_sequence_002_array[] = { }; /** - * @brief Threads Functionality. + * @brief Time and Intervals Functionality. */ const testsequence_t nil_test_sequence_002 = { - "Threads Functionality", + "Time and Intervals Functionality", nil_test_sequence_002_array }; diff --git a/test/nil/source/test/nil_test_sequence_003.c b/test/nil/source/test/nil_test_sequence_003.c index 0a80a4ae0..147132e1b 100644 --- a/test/nil/source/test/nil_test_sequence_003.c +++ b/test/nil/source/test/nil_test_sequence_003.c @@ -21,268 +21,126 @@ * @file nil_test_sequence_003.c * @brief Test Sequence 003 code. * - * @page nil_test_sequence_003 [3] Semaphores + * @page nil_test_sequence_003 [3] Threads Functionality * * File: @ref nil_test_sequence_003.c * *

Description

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

Conditions

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

Test Cases

* - @subpage nil_test_003_001 - * - @subpage nil_test_003_002 - * - @subpage nil_test_003_003 * . */ -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) - /**************************************************************************** * Shared code. ****************************************************************************/ #include "ch.h" -static thread_t *tp1; -static bool terminate; -static semaphore_t sem1, sem2; - -/* - * Signaler thread. - */ -static THD_FUNCTION(signaler, arg) { - - (void)arg; - - /* Initializing global resources.*/ - terminate = false; - chSemObjectInit(&sem1, 0); - chSemObjectInit(&sem2, 0); - - while (!terminate) { - chSysLock(); - if (chSemGetCounterI(&sem1) < 0) - chSemSignalI(&sem1); - chSemResetI(&sem2, 0); - chSchRescheduleS(); - chSysUnlock(); - - chThdSleepMilliseconds(250); - } -} - /**************************************************************************** * Test cases. ****************************************************************************/ /** - * @page nil_test_003_001 [3.1] Semaphore primitives, no state change + * @page nil_test_003_001 [3.1] Thread Sleep functionality * *

Description

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

Test Steps

- * - [3.1.1] The function chSemWait() is invoked, after return the - * counter and the returned message are tested. - * - [3.1.2] The function chSemSignal() is invoked, after return the - * counter is tested. - * - [3.1.3] The function chSemReset() is invoked, after return the - * counter is tested. + * - [3.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. + * - [3.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. + * - [3.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. + * - [3.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. + * - [3.1.5] Function chThdSleepUntil() is tested with a timeline of + * "now" + 100 ticks. * . */ -static void nil_test_003_001_setup(void) { - chSemObjectInit(&sem1, 1); -} - -static void nil_test_003_001_teardown(void) { - chSemReset(&sem1, 0); -} - static void nil_test_003_001_execute(void) { + systime_t time; - /* [3.1.1] The function chSemWait() is invoked, after return the - counter and the returned message are tested.*/ + /* [3.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); { - msg_t msg; - - msg = chSemWait(&sem1); - test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value"); - test_assert(MSG_OK == msg, "wrong returned message"); + time = chVTGetSystemTimeX(); + chThdSleep(100); + test_assert_time_window(chTimeAddX(time, 100), + chTimeAddX(time, 100 + 1), + "out of time window"); } test_end_step(1); - /* [3.1.2] The function chSemSignal() is invoked, after return the - counter is tested.*/ + /* [3.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); { - chSemSignal(&sem1); - test_assert_lock(chSemGetCounterI(&sem1) == 1, "wrong counter value"); + time = chVTGetSystemTimeX(); + chThdSleepMicroseconds(100000); + test_assert_time_window(chTimeAddX(time, TIME_US2I(100000)), + chTimeAddX(time, TIME_US2I(100000) + 1), + "out of time window"); } test_end_step(2); - /* [3.1.3] The function chSemReset() is invoked, after return the - counter is tested.*/ + /* [3.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); { - chSemReset(&sem1, 2); - test_assert_lock(chSemGetCounterI(&sem1) == 2, "wrong counter value"); + time = chVTGetSystemTimeX(); + chThdSleepMilliseconds(100); + test_assert_time_window(chTimeAddX(time, TIME_MS2I(100)), + chTimeAddX(time, TIME_MS2I(100) + 1), + "out of time window"); } test_end_step(3); + + /* [3.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) + 1), + "out of time window"); + } + test_end_step(4); + + /* [3.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 + 1), + "out of time window"); + } + test_end_step(5); } static const testcase_t nil_test_003_001 = { - "Semaphore primitives, no state change", - nil_test_003_001_setup, - nil_test_003_001_teardown, + "Thread Sleep functionality", + NULL, + NULL, nil_test_003_001_execute }; -/** - * @page nil_test_003_002 [3.2] Semaphore primitives, with state change - * - *

Description

- * Wait, Signal and Reset primitives are tested. The testing thread - * triggers a state change. - * - *

Test Steps

- * - [3.2.1] The function chSemWait() is invoked, after return the - * counter and the returned message are tested. The semaphore is - * signaled by another thread. - * - [3.2.2] The function chSemWait() is invoked, after return the - * counter and the returned message are tested. The semaphore is - * reset by another thread. - * . - */ - -static void nil_test_003_002_setup(void) { - thread_descriptor_t td = { - .name = "signaler", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() - 1, - .funcp = signaler, - .arg = NULL - }; - tp1 = chThdCreate(&td); -} - -static void nil_test_003_002_teardown(void) { - terminate = true; - chThdWait(tp1); -} - -static void nil_test_003_002_execute(void) { - - /* [3.2.1] The function chSemWait() is invoked, after return the - counter and the returned message are tested. The semaphore is - signaled by another thread.*/ - 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); - - /* [3.2.2] The function chSemWait() is invoked, after return the - counter and the returned message are tested. The semaphore is - reset by another thread.*/ - test_set_step(2); - { - msg_t msg; - - msg = chSemWait(&sem2); - test_assert_lock(chSemGetCounterI(&sem2) == 0,"wrong counter value"); - test_assert(MSG_RESET == msg, "wrong returned message"); - } - test_end_step(2); -} - -static const testcase_t nil_test_003_002 = { - "Semaphore primitives, with state change", - nil_test_003_002_setup, - nil_test_003_002_teardown, - nil_test_003_002_execute -}; - -/** - * @page nil_test_003_003 [3.3] Semaphores timeout - * - *

Description

- * Timeout on semaphores is tested. - * - *

Test Steps

- * - [3.3.1] The function chSemWaitTimeout() is invoked a first time, - * after return the system time, the counter and the returned message - * are tested. - * - [3.3.2] The function chSemWaitTimeout() is invoked again, after - * return the system time, the counter and the returned message are - * tested. - * . - */ - -static void nil_test_003_003_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void nil_test_003_003_teardown(void) { - chSemReset(&sem1, 0); -} - -static void nil_test_003_003_execute(void) { - systime_t time; - msg_t msg; - - /* [3.3.1] The function chSemWaitTimeout() is invoked a first time, - after return the system time, the counter and the returned message - are tested.*/ - test_set_step(1); - { - time = chVTGetSystemTimeX(); - msg = chSemWaitTimeout(&sem1, TIME_MS2I(1000)); - test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), - chTimeAddX(time, TIME_MS2I(1000) + 1), - "out of time window"); - test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value"); - test_assert(MSG_TIMEOUT == msg, "wrong timeout message"); - } - test_end_step(1); - - /* [3.3.2] The function chSemWaitTimeout() is invoked again, after - return the system time, the counter and the returned message are - tested.*/ - test_set_step(2); - { - time = chVTGetSystemTimeX(); - msg = chSemWaitTimeout(&sem1, TIME_MS2I(1000)); - test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), - chTimeAddX(time, TIME_MS2I(1000) + 1), - "out of time window"); - test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value"); - test_assert(MSG_TIMEOUT == msg, "wrong timeout message"); - } - test_end_step(2); -} - -static const testcase_t nil_test_003_003 = { - "Semaphores timeout", - nil_test_003_003_setup, - nil_test_003_003_teardown, - nil_test_003_003_execute -}; - /**************************************************************************** * Exported data. ****************************************************************************/ @@ -292,17 +150,13 @@ static const testcase_t nil_test_003_003 = { */ const testcase_t * const nil_test_sequence_003_array[] = { &nil_test_003_001, - &nil_test_003_002, - &nil_test_003_003, NULL }; /** - * @brief Semaphores. + * @brief Threads Functionality. */ const testsequence_t nil_test_sequence_003 = { - "Semaphores", + "Threads Functionality", nil_test_sequence_003_array }; - -#endif /* CH_CFG_USE_SEMAPHORES */ diff --git a/test/nil/source/test/nil_test_sequence_004.c b/test/nil/source/test/nil_test_sequence_004.c index 7869b0f77..3f2d4f4cd 100644 --- a/test/nil/source/test/nil_test_sequence_004.c +++ b/test/nil/source/test/nil_test_sequence_004.c @@ -21,40 +21,59 @@ * @file nil_test_sequence_004.c * @brief Test Sequence 004 code. * - * @page nil_test_sequence_004 [4] Suspend/Resume + * @page nil_test_sequence_004 [4] Semaphores * * File: @ref nil_test_sequence_004.c * *

Description

* This sequence tests the ChibiOS/NIL functionalities related to - * threads suspend/resume. + * counter semaphores. + * + *

Conditions

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

Test Cases

* - @subpage nil_test_004_001 + * - @subpage nil_test_004_002 + * - @subpage nil_test_004_003 * . */ +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) + /**************************************************************************** * Shared code. ****************************************************************************/ +#include "ch.h" + static thread_t *tp1; static bool terminate; -static thread_reference_t tr1; +static semaphore_t sem1, sem2; /* - * Resumer thread. + * Signaler thread. */ -static THD_FUNCTION(resumer, arg) { +static THD_FUNCTION(signaler, arg) { (void)arg; /* Initializing global resources.*/ terminate = false; - tr1 = NULL; + chSemObjectInit(&sem1, 0); + chSemObjectInit(&sem2, 0); while (!terminate) { - chThdResume(&tr1, MSG_OK); + chSysLock(); + if (chSemGetCounterI(&sem1) < 0) + chSemSignalI(&sem1); + chSemResetI(&sem2, 0); + chSchRescheduleS(); + chSysUnlock(); + chThdSleepMilliseconds(250); } } @@ -64,81 +83,204 @@ static THD_FUNCTION(resumer, arg) { ****************************************************************************/ /** - * @page nil_test_004_001 [4.1] Suspend and Resume functionality + * @page nil_test_004_001 [4.1] Semaphore primitives, no state change * *

Description

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

Test Steps

- * - [4.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. - * - [4.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. + * - [4.1.1] The function chSemWait() is invoked, after return the + * counter and the returned message are tested. + * - [4.1.2] The function chSemSignal() is invoked, after return the + * counter is tested. + * - [4.1.3] The function chSemReset() is invoked, after return the + * counter is tested. * . */ static void nil_test_004_001_setup(void) { + chSemObjectInit(&sem1, 1); +} + +static void nil_test_004_001_teardown(void) { + chSemReset(&sem1, 0); +} + +static void nil_test_004_001_execute(void) { + + /* [4.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); + + /* [4.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); + + /* [4.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 nil_test_004_001 = { + "Semaphore primitives, no state change", + nil_test_004_001_setup, + nil_test_004_001_teardown, + nil_test_004_001_execute +}; + +/** + * @page nil_test_004_002 [4.2] Semaphore primitives, with state change + * + *

Description

+ * Wait, Signal and Reset primitives are tested. The testing thread + * triggers a state change. + * + *

Test Steps

+ * - [4.2.1] The function chSemWait() is invoked, after return the + * counter and the returned message are tested. The semaphore is + * signaled by another thread. + * - [4.2.2] The function chSemWait() is invoked, after return the + * counter and the returned message are tested. The semaphore is + * reset by another thread. + * . + */ + +static void nil_test_004_002_setup(void) { thread_descriptor_t td = { - .name = "resumer", + .name = "signaler", .wbase = wa_common, .wend = THD_WORKING_AREA_END(wa_common), .prio = chThdGetPriorityX() - 1, - .funcp = resumer, + .funcp = signaler, .arg = NULL }; tp1 = chThdCreate(&td); } -static void nil_test_004_001_teardown(void) { +static void nil_test_004_002_teardown(void) { terminate = true; chThdWait(tp1); } -static void nil_test_004_001_execute(void) { - systime_t time; - msg_t msg; +static void nil_test_004_002_execute(void) { - /* [4.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.*/ + /* [4.2.1] The function chSemWait() is invoked, after return the + counter and the returned message are tested. The semaphore is + signaled by another thread.*/ test_set_step(1); { - chSysLock(); - msg = chThdSuspendTimeoutS(&tr1, TIME_INFINITE); - chSysUnlock(); - test_assert(NULL == tr1, "not NULL"); - test_assert(MSG_OK == msg,"wrong returned message"); + 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); - /* [4.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.*/ + /* [4.2.2] The function chSemWait() is invoked, after return the + counter and the returned message are tested. The semaphore is + reset by another thread.*/ test_set_step(2); { - thread_reference_t tr = NULL; + msg_t msg; - chSysLock(); - time = chVTGetSystemTimeX(); - msg = chThdSuspendTimeoutS(&tr, TIME_MS2I(1000)); - chSysUnlock(); - test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), - chTimeAddX(time, TIME_MS2I(1000) + 1), - "out of time window"); - test_assert(NULL == tr, "not NULL"); - test_assert(MSG_TIMEOUT == msg, "wrong returned message"); + msg = chSemWait(&sem2); + test_assert_lock(chSemGetCounterI(&sem2) == 0,"wrong counter value"); + test_assert(MSG_RESET == msg, "wrong returned message"); } test_end_step(2); } -static const testcase_t nil_test_004_001 = { - "Suspend and Resume functionality", - nil_test_004_001_setup, - nil_test_004_001_teardown, - nil_test_004_001_execute +static const testcase_t nil_test_004_002 = { + "Semaphore primitives, with state change", + nil_test_004_002_setup, + nil_test_004_002_teardown, + nil_test_004_002_execute +}; + +/** + * @page nil_test_004_003 [4.3] Semaphores timeout + * + *

Description

+ * Timeout on semaphores is tested. + * + *

Test Steps

+ * - [4.3.1] The function chSemWaitTimeout() is invoked a first time, + * after return the system time, the counter and the returned message + * are tested. + * - [4.3.2] The function chSemWaitTimeout() is invoked again, after + * return the system time, the counter and the returned message are + * tested. + * . + */ + +static void nil_test_004_003_setup(void) { + chSemObjectInit(&sem1, 0); +} + +static void nil_test_004_003_teardown(void) { + chSemReset(&sem1, 0); +} + +static void nil_test_004_003_execute(void) { + systime_t time; + msg_t msg; + + /* [4.3.1] The function chSemWaitTimeout() is invoked a first time, + after return the system time, the counter and the returned message + are tested.*/ + test_set_step(1); + { + time = chVTGetSystemTimeX(); + msg = chSemWaitTimeout(&sem1, TIME_MS2I(1000)); + test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), + chTimeAddX(time, TIME_MS2I(1000) + 1), + "out of time window"); + test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value"); + test_assert(MSG_TIMEOUT == msg, "wrong timeout message"); + } + test_end_step(1); + + /* [4.3.2] The function chSemWaitTimeout() is invoked again, after + return the system time, the counter and the returned message are + tested.*/ + test_set_step(2); + { + time = chVTGetSystemTimeX(); + msg = chSemWaitTimeout(&sem1, TIME_MS2I(1000)); + test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), + chTimeAddX(time, TIME_MS2I(1000) + 1), + "out of time window"); + test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value"); + test_assert(MSG_TIMEOUT == msg, "wrong timeout message"); + } + test_end_step(2); +} + +static const testcase_t nil_test_004_003 = { + "Semaphores timeout", + nil_test_004_003_setup, + nil_test_004_003_teardown, + nil_test_004_003_execute }; /**************************************************************************** @@ -150,13 +292,17 @@ static const testcase_t nil_test_004_001 = { */ const testcase_t * const nil_test_sequence_004_array[] = { &nil_test_004_001, + &nil_test_004_002, + &nil_test_004_003, NULL }; /** - * @brief Suspend/Resume. + * @brief Semaphores. */ const testsequence_t nil_test_sequence_004 = { - "Suspend/Resume", + "Semaphores", nil_test_sequence_004_array }; + +#endif /* CH_CFG_USE_SEMAPHORES */ diff --git a/test/nil/source/test/nil_test_sequence_005.c b/test/nil/source/test/nil_test_sequence_005.c index 15c7a7543..91974388e 100644 --- a/test/nil/source/test/nil_test_sequence_005.c +++ b/test/nil/source/test/nil_test_sequence_005.c @@ -21,62 +21,42 @@ * @file nil_test_sequence_005.c * @brief Test Sequence 005 code. * - * @page nil_test_sequence_005 [5] Event Sources and Event Flags + * @page nil_test_sequence_005 [5] Suspend/Resume * * File: @ref nil_test_sequence_005.c * *

Description

- * 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_EVENTS - * . + * This sequence tests the ChibiOS/NIL functionalities related to + * threads suspend/resume. * *

Test Cases

* - @subpage nil_test_005_001 - * - @subpage nil_test_005_002 - * - @subpage nil_test_005_003 - * - @subpage nil_test_005_004 - * - @subpage nil_test_005_005 - * - @subpage nil_test_005_006 - * - @subpage nil_test_005_007 * . */ -#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) - /**************************************************************************** * Shared code. ****************************************************************************/ -static EVENTSOURCE_DECL(es1); -static EVENTSOURCE_DECL(es2); - -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 thread_t *tp1; +static bool terminate; +static thread_reference_t tr1; /* - * Direct events thread. + * Resumer thread. */ -static THD_FUNCTION(evtthd1, p) { +static THD_FUNCTION(resumer, arg) { - chThdSleepMilliseconds(50); - chEvtSignal((thread_t *)p, 1); -} + (void)arg; -/* - * Broadcaster thread. - */ -static THD_FUNCTION(evtthd2, p) { + /* Initializing global resources.*/ + terminate = false; + tr1 = NULL; - (void)p; - chEvtBroadcast(&es1); - chThdSleepMilliseconds(50); - chEvtBroadcast(&es2); + while (!terminate) { + chThdResume(&tr1, MSG_OK); + chThdSleepMilliseconds(250); + } } /**************************************************************************** @@ -84,525 +64,83 @@ static THD_FUNCTION(evtthd2, p) { ****************************************************************************/ /** - * @page nil_test_005_001 [5.1] Events registration + * @page nil_test_005_001 [5.1] Suspend and Resume functionality * *

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. + * The functionality of chThdSuspendTimeoutS() and chThdResumeI() is + * tested. * *

Test Steps

- * - [5.1.1] An Event Source is initialized. - * - [5.1.2] Two Event Listeners are registered on the Event Source, - * the Event Source is tested to have listeners. - * - [5.1.3] An Event Listener is unregistered, the Event Source must - * still have listeners. - * - [5.1.4] An Event Listener is unregistered, the Event Source must - * not have listeners. + * - [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. * . */ -static void nil_test_005_001_execute(void) { - event_listener_t el1, el2; +static void nil_test_005_001_setup(void) { + thread_descriptor_t td = { + .name = "resumer", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() - 1, + .funcp = resumer, + .arg = NULL + }; + tp1 = chThdCreate(&td); +} - /* [5.1.1] An Event Source is initialized.*/ +static void nil_test_005_001_teardown(void) { + terminate = true; + chThdWait(tp1); +} + +static void nil_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.*/ test_set_step(1); { - chEvtObjectInit(&es1); + chSysLock(); + msg = chThdSuspendTimeoutS(&tr1, TIME_INFINITE); + chSysUnlock(); + test_assert(NULL == tr1, "not NULL"); + test_assert(MSG_OK == msg,"wrong returned message"); } test_end_step(1); - /* [5.1.2] Two Event Listeners are registered on the Event Source, - the Event Source is tested to have listeners.*/ + /* [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.*/ test_set_step(2); { - chEvtRegisterMask(&es1, &el1, 1); - chEvtRegisterMask(&es1, &el2, 2); - test_assert_lock(chEvtIsListeningI(&es1), "no listener"); + thread_reference_t tr = NULL; + + chSysLock(); + time = chVTGetSystemTimeX(); + msg = chThdSuspendTimeoutS(&tr, TIME_MS2I(1000)); + chSysUnlock(); + test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), + chTimeAddX(time, TIME_MS2I(1000) + 1), + "out of time window"); + test_assert(NULL == tr, "not NULL"); + test_assert(MSG_TIMEOUT == msg, "wrong returned message"); } test_end_step(2); - - /* [5.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); - - /* [5.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 nil_test_005_001 = { - "Events registration", - NULL, - NULL, + "Suspend and Resume functionality", + nil_test_005_001_setup, + nil_test_005_001_teardown, nil_test_005_001_execute }; -/** - * @page nil_test_005_002 [5.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

- * - [5.2.1] Three evenf flag bits are raised then chEvtDispatch() is - * invoked, the sequence of handlers calls is tested. - * . - */ - -static void nil_test_005_002_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void nil_test_005_002_execute(void) { - - /* [5.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 nil_test_005_002 = { - "Event Flags dispatching", - nil_test_005_002_setup, - NULL, - nil_test_005_002_execute -}; - -/** - * @page nil_test_005_003 [5.3] Events Flags wait using chEvtWaitOne() - * - *

Description

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

Test Steps

- * - [5.3.1] Setting three event flags. - * - [5.3.2] Calling chEvtWaitOne() three times, each time a single - * flag must be returned in order of priority. - * - [5.3.3] Getting current time and starting a signaler thread, the - * thread will set an event flag after 50mS. - * - [5.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 nil_test_005_003_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void nil_test_005_003_execute(void) { - eventmask_t m; - systime_t target_time; - thread_t *tp; - - /* [5.3.1] Setting three event flags.*/ - test_set_step(1); - { - chEvtAddEvents(7); - } - test_end_step(1); - - /* [5.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); - - /* [5.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)); - thread_descriptor_t td = { - .name = "event1", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() + 1, - .funcp = evtthd1, - .arg = chThdGetSelfX() - }; - tp = chThdCreate(&td); - } - test_end_step(3); - - /* [5.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"); - chThdWait(tp); - } - test_end_step(4); -} - -static const testcase_t nil_test_005_003 = { - "Events Flags wait using chEvtWaitOne()", - nil_test_005_003_setup, - NULL, - nil_test_005_003_execute -}; - -/** - * @page nil_test_005_004 [5.4] Events Flags wait using chEvtWaitAny() - * - *

Description

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

Test Steps

- * - [5.4.1] Setting two, non contiguous, event flags. - * - [5.4.2] Calling chEvtWaitAny() one time, the two flags must be - * returned. - * - [5.4.3] Getting current time and starting a signaler thread, the - * thread will set an event flag after 50mS. - * - [5.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 nil_test_005_004_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void nil_test_005_004_execute(void) { - eventmask_t m; - systime_t target_time; - thread_t *tp; - - /* [5.4.1] Setting two, non contiguous, event flags.*/ - test_set_step(1); - { - chEvtAddEvents(5); - } - test_end_step(1); - - /* [5.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); - - /* [5.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)); - thread_descriptor_t td = { - .name = "event1", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() + 1, - .funcp = evtthd1, - .arg = chThdGetSelfX() - }; - tp = chThdCreate(&td); - } - test_end_step(3); - - /* [5.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"); - chThdWait(tp); - } - test_end_step(4); -} - -static const testcase_t nil_test_005_004 = { - "Events Flags wait using chEvtWaitAny()", - nil_test_005_004_setup, - NULL, - nil_test_005_004_execute -}; - -/** - * @page nil_test_005_005 [5.5] Events Flags wait using chEvtWaitAll() - * - *

Description

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

Test Steps

- * - [5.5.1] Setting two, non contiguous, event flags. - * - [5.5.2] Calling chEvtWaitAll() one time, the two flags must be - * returned. - * - [5.5.3] Setting one event flag. - * - [5.5.4] Getting current time and starting a signaler thread, the - * thread will set another event flag after 50mS. - * - [5.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 nil_test_005_005_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void nil_test_005_005_execute(void) { - eventmask_t m; - systime_t target_time; - thread_t *tp; - - /* [5.5.1] Setting two, non contiguous, event flags.*/ - test_set_step(1); - { - chEvtAddEvents(5); - } - test_end_step(1); - - /* [5.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); - - /* [5.5.3] Setting one event flag.*/ - test_set_step(3); - { - chEvtAddEvents(4); - } - test_end_step(3); - - /* [5.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)); - thread_descriptor_t td = { - .name = "event1", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() + 1, - .funcp = evtthd1, - .arg = chThdGetSelfX() - }; - tp = chThdCreate(&td); - } - test_end_step(4); - - /* [5.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"); - chThdWait(tp); - } - test_end_step(5); -} - -static const testcase_t nil_test_005_005 = { - "Events Flags wait using chEvtWaitAll()", - nil_test_005_005_setup, - NULL, - nil_test_005_005_execute -}; - -/** - * @page nil_test_005_006 [5.6] Events Flags wait timeouts - * - *

Description

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

Test Steps

- * - [5.6.1] The functions are invoked first with TIME_IMMEDIATE - * timeout, the timeout condition is tested. - * - [5.6.2] The functions are invoked first with a 50mS timeout, the - * timeout condition is tested. - * . - */ - -static void nil_test_005_006_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void nil_test_005_006_execute(void) { - eventmask_t m; - - /* [5.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); - - /* [5.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 nil_test_005_006 = { - "Events Flags wait timeouts", - nil_test_005_006_setup, - NULL, - nil_test_005_006_execute -}; - -/** - * @page nil_test_005_007 [5.7] Broadcasting using chEvtBroadcast() - * - *

Description

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

Test Steps

- * - [5.7.1] Registering on two event sources associating them with - * flags 1 and 4. - * - [5.7.2] Getting current time and starting a broadcaster thread, - * the thread broadcast the first Event Source immediately and the - * other after 50mS. - * - [5.7.3] Calling chEvtWaitAll() then verifying that both event - * flags have been received after 50mS and that the event flags mask - * has been emptied. - * - [5.7.4] Unregistering from the Event Sources. - * . - */ - -static void nil_test_005_007_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); - chEvtObjectInit(&es1); - chEvtObjectInit(&es2); -} - -static void nil_test_005_007_execute(void) { - eventmask_t m; - event_listener_t el1, el2; - systime_t target_time; - thread_t *tp; - - /* [5.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); - - /* [5.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)); - thread_descriptor_t td = { - .name = "event2", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() + 1, - .funcp = evtthd2, - .arg = NULL - }; - tp = chThdCreate(&td); - } - test_end_step(2); - - /* [5.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"); - chThdWait(tp); - } - test_end_step(3); - - /* [5.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 nil_test_005_007 = { - "Broadcasting using chEvtBroadcast()", - nil_test_005_007_setup, - NULL, - nil_test_005_007_execute -}; - /**************************************************************************** * Exported data. ****************************************************************************/ @@ -612,21 +150,13 @@ static const testcase_t nil_test_005_007 = { */ const testcase_t * const nil_test_sequence_005_array[] = { &nil_test_005_001, - &nil_test_005_002, - &nil_test_005_003, - &nil_test_005_004, - &nil_test_005_005, - &nil_test_005_006, - &nil_test_005_007, NULL }; /** - * @brief Event Sources and Event Flags. + * @brief Suspend/Resume. */ const testsequence_t nil_test_sequence_005 = { - "Event Sources and Event Flags", + "Suspend/Resume", nil_test_sequence_005_array }; - -#endif /* CH_CFG_USE_EVENTS */ diff --git a/test/nil/source/test/nil_test_sequence_006.c b/test/nil/source/test/nil_test_sequence_006.c index 023d56fc9..b12990fd3 100644 --- a/test/nil/source/test/nil_test_sequence_006.c +++ b/test/nil/source/test/nil_test_sequence_006.c @@ -21,40 +21,62 @@ * @file nil_test_sequence_006.c * @brief Test Sequence 006 code. * - * @page nil_test_sequence_006 [6] Synchronous Messages + * @page nil_test_sequence_006 [6] Event Sources and Event Flags * * File: @ref nil_test_sequence_006.c * *

Description

- * This module implements the test sequence for the Synchronous - * Messages subsystem. + * 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_MESSAGES + * - CH_CFG_USE_EVENTS * . * *

Test Cases

* - @subpage nil_test_006_001 + * - @subpage nil_test_006_002 + * - @subpage nil_test_006_003 + * - @subpage nil_test_006_004 + * - @subpage nil_test_006_005 + * - @subpage nil_test_006_006 + * - @subpage nil_test_006_007 * . */ -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) /**************************************************************************** * Shared code. ****************************************************************************/ -/* - * Messager thread. - */ -static THD_FUNCTION(messenger, p) { +static EVENTSOURCE_DECL(es1); +static EVENTSOURCE_DECL(es2); - chMsgSend(p, 'A'); - chMsgSend(p, 'B'); - chMsgSend(p, 'C'); - chMsgSend(p, 'D'); +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}; + +/* + * Direct events thread. + */ +static THD_FUNCTION(evtthd1, p) { + + chThdSleepMilliseconds(50); + chEvtSignal((thread_t *)p, 1); +} + +/* + * Broadcaster thread. + */ +static THD_FUNCTION(evtthd2, p) { + + (void)p; + chEvtBroadcast(&es1); + chThdSleepMilliseconds(50); + chEvtBroadcast(&es2); } /**************************************************************************** @@ -62,61 +84,523 @@ static THD_FUNCTION(messenger, p) { ****************************************************************************/ /** - * @page nil_test_006_001 [6.1] Messages Server loop + * @page nil_test_006_001 [6.1] Events registration * *

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. + * 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

- * - [6.1.1] Starting the messenger thread. - * - [6.1.2] Waiting for four messages then testing the receive order. + * - [6.1.1] An Event Source is initialized. + * - [6.1.2] Two Event Listeners are registered on the Event Source, + * the Event Source is tested to have listeners. + * - [6.1.3] An Event Listener is unregistered, the Event Source must + * still have listeners. + * - [6.1.4] An Event Listener is unregistered, the Event Source must + * not have listeners. * . */ static void nil_test_006_001_execute(void) { - thread_t *tp, *tp1; - msg_t msg; + event_listener_t el1, el2; - /* [6.1.1] Starting the messenger thread.*/ + /* [6.1.1] An Event Source is initialized.*/ test_set_step(1); { - thread_descriptor_t td = { - .name = "messenger", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() - 1, - .funcp = messenger, - .arg = chThdGetSelfX() - }; - tp1 = chThdCreate(&td); + chEvtObjectInit(&es1); } test_end_step(1); - /* [6.1.2] Waiting for four messages then testing the receive - order.*/ + /* [6.1.2] Two Event Listeners are registered on the Event Source, + the Event Source is tested to have listeners.*/ test_set_step(2); { - unsigned i; + chEvtRegisterMask(&es1, &el1, 1); + chEvtRegisterMask(&es1, &el2, 2); + test_assert_lock(chEvtIsListeningI(&es1), "no listener"); + } + test_end_step(2); - for (i = 0; i < 4; i++) { - tp = chMsgWait(); - msg = chMsgGet(tp); - chMsgRelease(tp, msg); - test_emit_token(msg); - } - chThdWait(tp1); - test_assert_sequence("ABCD", "invalid sequence"); + /* [6.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); + + /* [6.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 nil_test_006_001 = { + "Events registration", + NULL, + NULL, + nil_test_006_001_execute +}; + +/** + * @page nil_test_006_002 [6.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

+ * - [6.2.1] Three evenf flag bits are raised then chEvtDispatch() is + * invoked, the sequence of handlers calls is tested. + * . + */ + +static void nil_test_006_002_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void nil_test_006_002_execute(void) { + + /* [6.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 nil_test_006_002 = { + "Event Flags dispatching", + nil_test_006_002_setup, + NULL, + nil_test_006_002_execute +}; + +/** + * @page nil_test_006_003 [6.3] Events Flags wait using chEvtWaitOne() + * + *

Description

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

Test Steps

+ * - [6.3.1] Setting three event flags. + * - [6.3.2] Calling chEvtWaitOne() three times, each time a single + * flag must be returned in order of priority. + * - [6.3.3] Getting current time and starting a signaler thread, the + * thread will set an event flag after 50mS. + * - [6.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 nil_test_006_003_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void nil_test_006_003_execute(void) { + eventmask_t m; + systime_t target_time; + thread_t *tp; + + /* [6.3.1] Setting three event flags.*/ + test_set_step(1); + { + chEvtAddEvents(7); + } + test_end_step(1); + + /* [6.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); + + /* [6.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)); + thread_descriptor_t td = { + .name = "event1", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() + 1, + .funcp = evtthd1, + .arg = chThdGetSelfX() + }; + tp = chThdCreate(&td); + } + test_end_step(3); + + /* [6.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"); + chThdWait(tp); + } + test_end_step(4); +} + +static const testcase_t nil_test_006_003 = { + "Events Flags wait using chEvtWaitOne()", + nil_test_006_003_setup, + NULL, + nil_test_006_003_execute +}; + +/** + * @page nil_test_006_004 [6.4] Events Flags wait using chEvtWaitAny() + * + *

Description

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

Test Steps

+ * - [6.4.1] Setting two, non contiguous, event flags. + * - [6.4.2] Calling chEvtWaitAny() one time, the two flags must be + * returned. + * - [6.4.3] Getting current time and starting a signaler thread, the + * thread will set an event flag after 50mS. + * - [6.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 nil_test_006_004_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void nil_test_006_004_execute(void) { + eventmask_t m; + systime_t target_time; + thread_t *tp; + + /* [6.4.1] Setting two, non contiguous, event flags.*/ + test_set_step(1); + { + chEvtAddEvents(5); + } + test_end_step(1); + + /* [6.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); + + /* [6.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)); + thread_descriptor_t td = { + .name = "event1", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() + 1, + .funcp = evtthd1, + .arg = chThdGetSelfX() + }; + tp = chThdCreate(&td); + } + test_end_step(3); + + /* [6.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"); + chThdWait(tp); + } + test_end_step(4); +} + +static const testcase_t nil_test_006_004 = { + "Events Flags wait using chEvtWaitAny()", + nil_test_006_004_setup, + NULL, + nil_test_006_004_execute +}; + +/** + * @page nil_test_006_005 [6.5] Events Flags wait using chEvtWaitAll() + * + *

Description

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

Test Steps

+ * - [6.5.1] Setting two, non contiguous, event flags. + * - [6.5.2] Calling chEvtWaitAll() one time, the two flags must be + * returned. + * - [6.5.3] Setting one event flag. + * - [6.5.4] Getting current time and starting a signaler thread, the + * thread will set another event flag after 50mS. + * - [6.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 nil_test_006_005_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void nil_test_006_005_execute(void) { + eventmask_t m; + systime_t target_time; + thread_t *tp; + + /* [6.5.1] Setting two, non contiguous, event flags.*/ + test_set_step(1); + { + chEvtAddEvents(5); + } + test_end_step(1); + + /* [6.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); + + /* [6.5.3] Setting one event flag.*/ + test_set_step(3); + { + chEvtAddEvents(4); + } + test_end_step(3); + + /* [6.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)); + thread_descriptor_t td = { + .name = "event1", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() + 1, + .funcp = evtthd1, + .arg = chThdGetSelfX() + }; + tp = chThdCreate(&td); + } + test_end_step(4); + + /* [6.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"); + chThdWait(tp); + } + test_end_step(5); +} + +static const testcase_t nil_test_006_005 = { + "Events Flags wait using chEvtWaitAll()", + nil_test_006_005_setup, + NULL, + nil_test_006_005_execute +}; + +/** + * @page nil_test_006_006 [6.6] Events Flags wait timeouts + * + *

Description

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

Test Steps

+ * - [6.6.1] The functions are invoked first with TIME_IMMEDIATE + * timeout, the timeout condition is tested. + * - [6.6.2] The functions are invoked first with a 50mS timeout, the + * timeout condition is tested. + * . + */ + +static void nil_test_006_006_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); +} + +static void nil_test_006_006_execute(void) { + eventmask_t m; + + /* [6.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); + + /* [6.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 nil_test_006_001 = { - "Messages Server loop", +static const testcase_t nil_test_006_006 = { + "Events Flags wait timeouts", + nil_test_006_006_setup, NULL, + nil_test_006_006_execute +}; + +/** + * @page nil_test_006_007 [6.7] Broadcasting using chEvtBroadcast() + * + *

Description

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

Test Steps

+ * - [6.7.1] Registering on two event sources associating them with + * flags 1 and 4. + * - [6.7.2] Getting current time and starting a broadcaster thread, + * the thread broadcast the first Event Source immediately and the + * other after 50mS. + * - [6.7.3] Calling chEvtWaitAll() then verifying that both event + * flags have been received after 50mS and that the event flags mask + * has been emptied. + * - [6.7.4] Unregistering from the Event Sources. + * . + */ + +static void nil_test_006_007_setup(void) { + chEvtGetAndClearEvents(ALL_EVENTS); + chEvtObjectInit(&es1); + chEvtObjectInit(&es2); +} + +static void nil_test_006_007_execute(void) { + eventmask_t m; + event_listener_t el1, el2; + systime_t target_time; + thread_t *tp; + + /* [6.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); + + /* [6.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)); + thread_descriptor_t td = { + .name = "event2", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() + 1, + .funcp = evtthd2, + .arg = NULL + }; + tp = chThdCreate(&td); + } + test_end_step(2); + + /* [6.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"); + chThdWait(tp); + } + test_end_step(3); + + /* [6.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 nil_test_006_007 = { + "Broadcasting using chEvtBroadcast()", + nil_test_006_007_setup, NULL, - nil_test_006_001_execute + nil_test_006_007_execute }; /**************************************************************************** @@ -128,15 +612,21 @@ static const testcase_t nil_test_006_001 = { */ const testcase_t * const nil_test_sequence_006_array[] = { &nil_test_006_001, + &nil_test_006_002, + &nil_test_006_003, + &nil_test_006_004, + &nil_test_006_005, + &nil_test_006_006, + &nil_test_006_007, NULL }; /** - * @brief Synchronous Messages. + * @brief Event Sources and Event Flags. */ const testsequence_t nil_test_sequence_006 = { - "Synchronous Messages", + "Event Sources and Event Flags", nil_test_sequence_006_array }; -#endif /* CH_CFG_USE_MESSAGES */ +#endif /* CH_CFG_USE_EVENTS */ diff --git a/test/nil/source/test/nil_test_sequence_007.c b/test/nil/source/test/nil_test_sequence_007.c index 212068987..b8d7de1c3 100644 --- a/test/nil/source/test/nil_test_sequence_007.c +++ b/test/nil/source/test/nil_test_sequence_007.c @@ -21,613 +21,103 @@ * @file nil_test_sequence_007.c * @brief Test Sequence 007 code. * - * @page nil_test_sequence_007 [7] Benchmarks + * @page nil_test_sequence_007 [7] Synchronous Messages * * File: @ref nil_test_sequence_007.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 Synchronous + * Messages subsystem. + * + *

Conditions

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

Test Cases

* - @subpage nil_test_007_001 - * - @subpage nil_test_007_002 - * - @subpage nil_test_007_003 - * - @subpage nil_test_007_004 - * - @subpage nil_test_007_005 - * - @subpage nil_test_007_006 - * - @subpage nil_test_007_007 * . */ +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) + /**************************************************************************** * 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 +/* + * Messager thread. + */ +static THD_FUNCTION(messenger, 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(NIL_STATE_SUSPENDED); - msg = self->u1.msg; - } while (msg == MSG_OK); - chSysUnlock(); + chMsgSend(p, 'A'); + chMsgSend(p, 'B'); + chMsgSend(p, 'C'); + chMsgSend(p, 'D'); } /**************************************************************************** * Test cases. ****************************************************************************/ -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) /** - * @page nil_test_007_001 [7.1] Messages performance #1 + * @page nil_test_007_001 [7.1] Messages Server loop * *

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 - * . + * 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

- * - [7.1.1] The messenger thread is started at a lower priority than - * the current thread. - * - [7.1.2] The number of messages exchanged is counted in a one - * second time window. - * - [7.1.3] Score is printed. + * - [7.1.1] Starting the messenger thread. + * - [7.1.2] Waiting for four messages then testing the receive order. * . */ static void nil_test_007_001_execute(void) { - uint32_t n; - thread_t *tp; + thread_t *tp, *tp1; + msg_t msg; - /* [7.1.1] The messenger thread is started at a lower priority than - the current thread.*/ + /* [7.1.1] Starting the messenger thread.*/ test_set_step(1); { thread_descriptor_t td = { .name = "messenger", .wbase = wa_common, .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() + 1, - .funcp = bmk_thread1, - .arg = NULL + .prio = chThdGetPriorityX() - 1, + .funcp = messenger, + .arg = chThdGetSelfX() }; - tp = chThdCreate(&td); + tp1 = chThdCreate(&td); } test_end_step(1); - /* [7.1.2] The number of messages exchanged is counted in a one - second time window.*/ + /* [7.1.2] Waiting for four messages then testing the receive + order.*/ test_set_step(2); { - n = msg_loop_test(tp); - chThdWait(tp); + unsigned i; + + for (i = 0; i < 4; i++) { + tp = chMsgWait(); + msg = chMsgGet(tp); + chMsgRelease(tp, msg); + test_emit_token(msg); + } + chThdWait(tp1); + test_assert_sequence("ABCD", "invalid sequence"); } test_end_step(2); - - /* [7.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 nil_test_007_001 = { - "Messages performance #1", + "Messages Server loop", NULL, NULL, nil_test_007_001_execute }; -#endif /* CH_CFG_USE_MESSAGES */ - -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) -/** - * @page nil_test_007_002 [7.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

- * - [7.2.1] The messenger thread is started at an higher priority than - * the current thread. - * - [7.2.2] The number of messages exchanged is counted in a one - * second time window. - * - [7.2.3] Score is printed. - * . - */ - -static void nil_test_007_002_execute(void) { - uint32_t n; - thread_t *tp; - - /* [7.2.1] The messenger thread is started at an higher priority than - the current thread.*/ - test_set_step(1); - { - thread_descriptor_t td = { - .name = "messenger", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() - 1, - .funcp = bmk_thread1, - .arg = NULL - }; - tp = chThdCreate(&td); - } - test_end_step(1); - - /* [7.2.2] The number of messages exchanged is counted in a one - second time window.*/ - test_set_step(2); - { - n = msg_loop_test(tp); - chThdWait(tp); - } - test_end_step(2); - - /* [7.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 nil_test_007_002 = { - "Messages performance #2", - NULL, - NULL, - nil_test_007_002_execute -}; -#endif /* CH_CFG_USE_MESSAGES */ - -/** - * @page nil_test_007_003 [7.3] 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

- * - [7.3.1] Starting the target thread at an higher priority level. - * - [7.3.2] Waking up the thread as fast as possible in a one second - * time window. - * - [7.3.3] Stopping the target thread. - * - [7.3.4] Score is printed. - * . - */ - -static void nil_test_007_003_execute(void) { - thread_t *tp; - uint32_t n; - - /* [7.3.1] Starting the target thread at an higher priority level.*/ - test_set_step(1); - { - thread_descriptor_t td = { - .name = "messenger", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() - 1, - .funcp = bmk_thread4, - .arg = NULL - }; - tp = chThdCreate(&td); - } - test_end_step(1); - - /* [7.3.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; - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(2); - - /* [7.3.3] Stopping the target thread.*/ - test_set_step(3); - { - chSysLock(); - chSchWakeupS(tp, MSG_TIMEOUT); - chSysUnlock(); - chThdWait(tp); - } - test_end_step(3); - - /* [7.3.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 nil_test_007_003 = { - "Context Switch performance", - NULL, - NULL, - nil_test_007_003_execute -}; - -/** - * @page nil_test_007_004 [7.4] 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

- * - [7.4.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. - * - [7.4.2] Score is printed. - * . - */ - -static void nil_test_007_004_execute(void) { - uint32_t n; - - /* [7.4.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); - { - systime_t start, end; - thread_descriptor_t td = { - .name = "messenger", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() + 1, - .funcp = bmk_thread3, - .arg = NULL - }; - - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - chThdWait(chThdCreate(&td)); - n++; - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(1); - - /* [7.4.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 nil_test_007_004 = { - "Threads performance, full cycle", - NULL, - NULL, - nil_test_007_004_execute -}; - -/** - * @page nil_test_007_005 [7.5] 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

- * - [7.5.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. - * - [7.5.2] Score is printed. - * . - */ - -static void nil_test_007_005_execute(void) { - uint32_t n; - - /* [7.5.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); - { - systime_t start, end; - thread_descriptor_t td = { - .name = "messenger", - .wbase = wa_common, - .wend = THD_WORKING_AREA_END(wa_common), - .prio = chThdGetPriorityX() - 1, - .funcp = bmk_thread3, - .arg = NULL - }; - - n = 0; - start = test_wait_tick(); - end = chTimeAddX(start, TIME_MS2I(1000)); - do { - chThdWait(chThdCreate(&td)); - n++; - } while (chVTIsSystemTimeWithinX(start, end)); - } - test_end_step(1); - - /* [7.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 nil_test_007_005 = { - "Threads performance, create/exit only", - NULL, - NULL, - nil_test_007_005_execute -}; - -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) -/** - * @page nil_test_007_006 [7.6] 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

- * - [7.6.1] A semaphore is teken and released. The operation is - * repeated continuously in a one-second time window. - * - [7.6.2] The score is printed. - * . - */ - -static void nil_test_007_006_setup(void) { - chSemObjectInit(&sem1, 1); -} - -static void nil_test_007_006_execute(void) { - uint32_t n; - - /* [7.6.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); - - /* [7.6.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 nil_test_007_006 = { - "Semaphores wait/signal performance", - nil_test_007_006_setup, - NULL, - nil_test_007_006_execute -}; -#endif /* CH_CFG_USE_SEMAPHORES */ - -/** - * @page nil_test_007_007 [7.7] RAM Footprint - * - *

Description

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

Test Steps

- * - [7.7.1] The size of the system area is printed. - * - [7.7.2] The size of a thread structure is printed. - * - [7.7.3] The size of a semaphore structure is printed. - * - [7.7.4] The size of an event source is printed. - * - [7.7.5] The size of an event listener is printed. - * - [7.7.6] The size of a mailbox is printed. - * . - */ - -static void nil_test_007_007_execute(void) { - - /* [7.7.1] The size of the system area is printed.*/ - test_set_step(1); - { - test_print("--- System: "); - test_printn(sizeof(nil_system_t)); - test_println(" bytes"); - } - test_end_step(1); - - /* [7.7.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); - - /* [7.7.3] The size of a semaphore structure is printed.*/ - test_set_step(3); - { -#if CH_CFG_USE_SEMAPHORES || defined(__DOXYGEN__) - test_print("--- Semaph: "); - test_printn(sizeof(semaphore_t)); - test_println(" bytes"); -#endif - } - test_end_step(3); - - /* [7.7.4] The size of an event source is printed.*/ - test_set_step(4); - { -#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__) - test_print("--- EventS: "); - test_printn(sizeof(event_source_t)); - test_println(" bytes"); -#endif - } - test_end_step(4); - - /* [7.7.5] The size of an event listener is printed.*/ - test_set_step(5); - { -#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__) - test_print("--- EventL: "); - test_printn(sizeof(event_listener_t)); - test_println(" bytes"); -#endif - } - test_end_step(5); - - /* [7.7.6] The size of a mailbox is printed.*/ - test_set_step(6); - { -#if CH_CFG_USE_MAILBOXES || defined(__DOXYGEN__) - test_print("--- MailB.: "); - test_printn(sizeof(mailbox_t)); - test_println(" bytes"); -#endif - } - test_end_step(6); -} - -static const testcase_t nil_test_007_007 = { - "RAM Footprint", - NULL, - NULL, - nil_test_007_007_execute -}; /**************************************************************************** * Exported data. @@ -637,26 +127,16 @@ static const testcase_t nil_test_007_007 = { * @brief Array of test cases. */ const testcase_t * const nil_test_sequence_007_array[] = { -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) &nil_test_007_001, -#endif -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) - &nil_test_007_002, -#endif - &nil_test_007_003, - &nil_test_007_004, - &nil_test_007_005, -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) - &nil_test_007_006, -#endif - &nil_test_007_007, NULL }; /** - * @brief Benchmarks. + * @brief Synchronous Messages. */ const testsequence_t nil_test_sequence_007 = { - "Benchmarks", + "Synchronous Messages", nil_test_sequence_007_array }; + +#endif /* CH_CFG_USE_MESSAGES */ diff --git a/test/nil/source/test/nil_test_sequence_008.c b/test/nil/source/test/nil_test_sequence_008.c new file mode 100644 index 000000000..52e807bb2 --- /dev/null +++ b/test/nil/source/test/nil_test_sequence_008.c @@ -0,0 +1,662 @@ +/* + 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 "nil_test_root.h" + +/** + * @file nil_test_sequence_008.c + * @brief Test Sequence 008 code. + * + * @page nil_test_sequence_008 [8] Benchmarks + * + * File: @ref nil_test_sequence_008.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 nil_test_008_001 + * - @subpage nil_test_008_002 + * - @subpage nil_test_008_003 + * - @subpage nil_test_008_004 + * - @subpage nil_test_008_005 + * - @subpage nil_test_008_006 + * - @subpage nil_test_008_007 + * . + */ + +/**************************************************************************** + * 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 + +#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(NIL_STATE_SUSPENDED); + msg = self->u1.msg; + } while (msg == MSG_OK); + chSysUnlock(); +} + +/**************************************************************************** + * Test cases. + ****************************************************************************/ + +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +/** + * @page nil_test_008_001 [8.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

+ * - [8.1.1] The messenger thread is started at a lower priority than + * the current thread. + * - [8.1.2] The number of messages exchanged is counted in a one + * second time window. + * - [8.1.3] Score is printed. + * . + */ + +static void nil_test_008_001_execute(void) { + uint32_t n; + thread_t *tp; + + /* [8.1.1] The messenger thread is started at a lower priority than + the current thread.*/ + test_set_step(1); + { + thread_descriptor_t td = { + .name = "messenger", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() + 1, + .funcp = bmk_thread1, + .arg = NULL + }; + tp = chThdCreate(&td); + } + test_end_step(1); + + /* [8.1.2] The number of messages exchanged is counted in a one + second time window.*/ + test_set_step(2); + { + n = msg_loop_test(tp); + chThdWait(tp); + } + test_end_step(2); + + /* [8.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 nil_test_008_001 = { + "Messages performance #1", + NULL, + NULL, + nil_test_008_001_execute +}; +#endif /* CH_CFG_USE_MESSAGES */ + +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +/** + * @page nil_test_008_002 [8.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

+ * - [8.2.1] The messenger thread is started at an higher priority than + * the current thread. + * - [8.2.2] The number of messages exchanged is counted in a one + * second time window. + * - [8.2.3] Score is printed. + * . + */ + +static void nil_test_008_002_execute(void) { + uint32_t n; + thread_t *tp; + + /* [8.2.1] The messenger thread is started at an higher priority than + the current thread.*/ + test_set_step(1); + { + thread_descriptor_t td = { + .name = "messenger", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() - 1, + .funcp = bmk_thread1, + .arg = NULL + }; + tp = chThdCreate(&td); + } + test_end_step(1); + + /* [8.2.2] The number of messages exchanged is counted in a one + second time window.*/ + test_set_step(2); + { + n = msg_loop_test(tp); + chThdWait(tp); + } + test_end_step(2); + + /* [8.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 nil_test_008_002 = { + "Messages performance #2", + NULL, + NULL, + nil_test_008_002_execute +}; +#endif /* CH_CFG_USE_MESSAGES */ + +/** + * @page nil_test_008_003 [8.3] 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

+ * - [8.3.1] Starting the target thread at an higher priority level. + * - [8.3.2] Waking up the thread as fast as possible in a one second + * time window. + * - [8.3.3] Stopping the target thread. + * - [8.3.4] Score is printed. + * . + */ + +static void nil_test_008_003_execute(void) { + thread_t *tp; + uint32_t n; + + /* [8.3.1] Starting the target thread at an higher priority level.*/ + test_set_step(1); + { + thread_descriptor_t td = { + .name = "messenger", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() - 1, + .funcp = bmk_thread4, + .arg = NULL + }; + tp = chThdCreate(&td); + } + test_end_step(1); + + /* [8.3.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; + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(2); + + /* [8.3.3] Stopping the target thread.*/ + test_set_step(3); + { + chSysLock(); + chSchWakeupS(tp, MSG_TIMEOUT); + chSysUnlock(); + chThdWait(tp); + } + test_end_step(3); + + /* [8.3.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 nil_test_008_003 = { + "Context Switch performance", + NULL, + NULL, + nil_test_008_003_execute +}; + +/** + * @page nil_test_008_004 [8.4] 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

+ * - [8.4.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. + * - [8.4.2] Score is printed. + * . + */ + +static void nil_test_008_004_execute(void) { + uint32_t n; + + /* [8.4.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); + { + systime_t start, end; + thread_descriptor_t td = { + .name = "messenger", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() + 1, + .funcp = bmk_thread3, + .arg = NULL + }; + + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + chThdWait(chThdCreate(&td)); + n++; + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(1); + + /* [8.4.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 nil_test_008_004 = { + "Threads performance, full cycle", + NULL, + NULL, + nil_test_008_004_execute +}; + +/** + * @page nil_test_008_005 [8.5] 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

+ * - [8.5.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. + * - [8.5.2] Score is printed. + * . + */ + +static void nil_test_008_005_execute(void) { + uint32_t n; + + /* [8.5.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); + { + systime_t start, end; + thread_descriptor_t td = { + .name = "messenger", + .wbase = wa_common, + .wend = THD_WORKING_AREA_END(wa_common), + .prio = chThdGetPriorityX() - 1, + .funcp = bmk_thread3, + .arg = NULL + }; + + n = 0; + start = test_wait_tick(); + end = chTimeAddX(start, TIME_MS2I(1000)); + do { + chThdWait(chThdCreate(&td)); + n++; + } while (chVTIsSystemTimeWithinX(start, end)); + } + test_end_step(1); + + /* [8.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 nil_test_008_005 = { + "Threads performance, create/exit only", + NULL, + NULL, + nil_test_008_005_execute +}; + +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) +/** + * @page nil_test_008_006 [8.6] 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

+ * - [8.6.1] A semaphore is teken and released. The operation is + * repeated continuously in a one-second time window. + * - [8.6.2] The score is printed. + * . + */ + +static void nil_test_008_006_setup(void) { + chSemObjectInit(&sem1, 1); +} + +static void nil_test_008_006_execute(void) { + uint32_t n; + + /* [8.6.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); + + /* [8.6.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 nil_test_008_006 = { + "Semaphores wait/signal performance", + nil_test_008_006_setup, + NULL, + nil_test_008_006_execute +}; +#endif /* CH_CFG_USE_SEMAPHORES */ + +/** + * @page nil_test_008_007 [8.7] RAM Footprint + * + *

Description

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

Test Steps

+ * - [8.7.1] The size of the system area is printed. + * - [8.7.2] The size of a thread structure is printed. + * - [8.7.3] The size of a semaphore structure is printed. + * - [8.7.4] The size of an event source is printed. + * - [8.7.5] The size of an event listener is printed. + * - [8.7.6] The size of a mailbox is printed. + * . + */ + +static void nil_test_008_007_execute(void) { + + /* [8.7.1] The size of the system area is printed.*/ + test_set_step(1); + { + test_print("--- System: "); + test_printn(sizeof(nil_system_t)); + test_println(" bytes"); + } + test_end_step(1); + + /* [8.7.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); + + /* [8.7.3] The size of a semaphore structure is printed.*/ + test_set_step(3); + { +#if CH_CFG_USE_SEMAPHORES || defined(__DOXYGEN__) + test_print("--- Semaph: "); + test_printn(sizeof(semaphore_t)); + test_println(" bytes"); +#endif + } + test_end_step(3); + + /* [8.7.4] The size of an event source is printed.*/ + test_set_step(4); + { +#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__) + test_print("--- EventS: "); + test_printn(sizeof(event_source_t)); + test_println(" bytes"); +#endif + } + test_end_step(4); + + /* [8.7.5] The size of an event listener is printed.*/ + test_set_step(5); + { +#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__) + test_print("--- EventL: "); + test_printn(sizeof(event_listener_t)); + test_println(" bytes"); +#endif + } + test_end_step(5); + + /* [8.7.6] The size of a mailbox is printed.*/ + test_set_step(6); + { +#if CH_CFG_USE_MAILBOXES || defined(__DOXYGEN__) + test_print("--- MailB.: "); + test_printn(sizeof(mailbox_t)); + test_println(" bytes"); +#endif + } + test_end_step(6); +} + +static const testcase_t nil_test_008_007 = { + "RAM Footprint", + NULL, + NULL, + nil_test_008_007_execute +}; + +/**************************************************************************** + * Exported data. + ****************************************************************************/ + +/** + * @brief Array of test cases. + */ +const testcase_t * const nil_test_sequence_008_array[] = { +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) + &nil_test_008_001, +#endif +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) + &nil_test_008_002, +#endif + &nil_test_008_003, + &nil_test_008_004, + &nil_test_008_005, +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) + &nil_test_008_006, +#endif + &nil_test_008_007, + NULL +}; + +/** + * @brief Benchmarks. + */ +const testsequence_t nil_test_sequence_008 = { + "Benchmarks", + nil_test_sequence_008_array +}; diff --git a/test/nil/source/test/nil_test_sequence_008.h b/test/nil/source/test/nil_test_sequence_008.h new file mode 100644 index 000000000..5acb591af --- /dev/null +++ b/test/nil/source/test/nil_test_sequence_008.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 nil_test_sequence_008.h + * @brief Test Sequence 008 header. + */ + +#ifndef NIL_TEST_SEQUENCE_008_H +#define NIL_TEST_SEQUENCE_008_H + +extern const testsequence_t nil_test_sequence_008; + +#endif /* NIL_TEST_SEQUENCE_008_H */ diff --git a/test/rt/configuration.xml b/test/rt/configuration.xml index e2692edb6..a7051a75d 100644 --- a/test/rt/configuration.xml +++ b/test/rt/configuration.xml @@ -637,6 +637,25 @@ chSysEnable();]]>
+ + + + + Internal Tests + + + Time and Intervals Functionality. + + + This sequence tests the ChibiOS/NIL functionalities related to time and intervals management. + + + + + + + + System Tick Counter functionality. @@ -667,16 +686,103 @@ chSysEnable();]]> - + + + Time ranges functionality. + + + The functionality of the API @p chTimeIsInRangeX() is tested. + + + + + + + + + + + + + + + + + + + Checking case where start == end, it must always evaluate as in range. + + + + + + + + + + + Checking boundaries for start < end. + + + + + + + + + + + Checking boundaries for start > end. + + + + + + + + + + diff --git a/test/rt/rt_test.mk b/test/rt/rt_test.mk index f38edeb7b..bba0a09fc 100644 --- a/test/rt/rt_test.mk +++ b/test/rt/rt_test.mk @@ -9,7 +9,8 @@ TESTSRC += ${CHIBIOS}/test/rt/source/test/rt_test_root.c \ ${CHIBIOS}/test/rt/source/test/rt_test_sequence_007.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_010.c \ + ${CHIBIOS}/test/rt/source/test/rt_test_sequence_011.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 e03d0f1c8..4a8f4e1f8 100644 --- a/test/rt/source/test/rt_test_root.c +++ b/test/rt/source/test/rt_test_root.c @@ -31,6 +31,7 @@ * - @subpage rt_test_sequence_008 * - @subpage rt_test_sequence_009 * - @subpage rt_test_sequence_010 + * - @subpage rt_test_sequence_011 * . */ @@ -56,22 +57,23 @@ const testsequence_t * const rt_test_suite_array[] = { &rt_test_sequence_002, &rt_test_sequence_003, &rt_test_sequence_004, -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) &rt_test_sequence_005, -#endif -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) &rt_test_sequence_006, #endif -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) &rt_test_sequence_007, #endif -#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) &rt_test_sequence_008, #endif -#if (CH_CFG_USE_DYNAMIC) || defined(__DOXYGEN__) +#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) &rt_test_sequence_009, #endif +#if (CH_CFG_USE_DYNAMIC) || defined(__DOXYGEN__) &rt_test_sequence_010, +#endif + &rt_test_sequence_011, NULL }; diff --git a/test/rt/source/test/rt_test_root.h b/test/rt/source/test/rt_test_root.h index 6da777b22..963a4a45c 100644 --- a/test/rt/source/test/rt_test_root.h +++ b/test/rt/source/test/rt_test_root.h @@ -34,6 +34,7 @@ #include "rt_test_sequence_008.h" #include "rt_test_sequence_009.h" #include "rt_test_sequence_010.h" +#include "rt_test_sequence_011.h" #if !defined(__DOXYGEN__) diff --git a/test/rt/source/test/rt_test_sequence_001.c b/test/rt/source/test/rt_test_sequence_001.c index 518291739..dc066a048 100644 --- a/test/rt/source/test/rt_test_sequence_001.c +++ b/test/rt/source/test/rt_test_sequence_001.c @@ -94,6 +94,7 @@ static void rt_test_001_001_execute(void) { test_println(""); #endif } + test_end_step(1); } static const testcase_t rt_test_001_001 = { @@ -135,6 +136,7 @@ static void rt_test_001_002_execute(void) { test_printn(CH_KERNEL_PATCH); test_println(""); } + test_end_step(1); } static const testcase_t rt_test_001_002 = { @@ -257,6 +259,7 @@ static void rt_test_001_003_execute(void) { test_printn(CH_DBG_THREADS_PROFILING); test_println(""); } + test_end_step(1); } static const testcase_t rt_test_001_003 = { diff --git a/test/rt/source/test/rt_test_sequence_002.c b/test/rt/source/test/rt_test_sequence_002.c index dba480fd0..51542002c 100644 --- a/test/rt/source/test/rt_test_sequence_002.c +++ b/test/rt/source/test/rt_test_sequence_002.c @@ -36,7 +36,6 @@ * - @subpage rt_test_002_001 * - @subpage rt_test_002_002 * - @subpage rt_test_002_003 - * - @subpage rt_test_002_004 * . */ @@ -91,6 +90,7 @@ static void rt_test_002_001_execute(void) { chSysUnlock(); test_assert(result == false, "ready list check failed"); } + test_end_step(1); /* [2.1.2] Testing Virtual Timers List integrity.*/ test_set_step(2); @@ -100,6 +100,7 @@ static void rt_test_002_001_execute(void) { chSysUnlock(); test_assert(result == false, "virtual timers list check failed"); } + test_end_step(2); /* [2.1.3] Testing Registry List integrity.*/ test_set_step(3); @@ -109,6 +110,7 @@ static void rt_test_002_001_execute(void) { chSysUnlock(); test_assert(result == false, "registry list check failed"); } + test_end_step(3); /* [2.1.4] Testing Port-defined integrity.*/ test_set_step(4); @@ -118,6 +120,7 @@ static void rt_test_002_001_execute(void) { chSysUnlock(); test_assert(result == false, "port layer check failed"); } + test_end_step(4); } static const testcase_t rt_test_002_001 = { @@ -155,6 +158,7 @@ static void rt_test_002_002_execute(void) { sts = chSysGetStatusAndLockX(); chSysRestoreStatusX(sts); } + test_end_step(1); /* [2.2.2] Testing chSysGetStatusAndLockX() and chSysRestoreStatusX(), reentrant case.*/ @@ -165,6 +169,7 @@ static void rt_test_002_002_execute(void) { chSysRestoreStatusX(sts); chSysUnlock(); } + test_end_step(2); /* [2.2.3] Testing chSysUnconditionalLock().*/ test_set_step(3); @@ -173,6 +178,7 @@ static void rt_test_002_002_execute(void) { chSysUnconditionalLock(); chSysUnlock(); } + test_end_step(3); /* [2.2.4] Testing chSysUnconditionalUnlock().*/ test_set_step(4); @@ -181,6 +187,7 @@ static void rt_test_002_002_execute(void) { chSysUnconditionalUnlock(); chSysUnconditionalUnlock(); } + test_end_step(4); /* [2.2.5] Testing from ISR context using a virtual timer.*/ test_set_step(5); @@ -191,6 +198,7 @@ static void rt_test_002_002_execute(void) { test_assert(chVTIsArmed(&vt) == false, "timer still armed"); } + test_end_step(5); } static const testcase_t rt_test_002_002 = { @@ -222,6 +230,7 @@ static void rt_test_002_003_execute(void) { chSysSuspend(); chSysEnable(); } + test_end_step(1); } static const testcase_t rt_test_002_003 = { @@ -231,40 +240,6 @@ static const testcase_t rt_test_002_003 = { rt_test_002_003_execute }; -/** - * @page rt_test_002_004 [2.4] System Tick Counter functionality - * - *

Description

- * The functionality of the API @p chVTGetSystemTimeX() is tested. - * - *

Test Steps

- * - [2.4.1] A System Tick Counter increment is expected, the test - * simply hangs if it does not happen. - * . - */ - -static void rt_test_002_004_execute(void) { - - /* [2.4.1] A System Tick Counter increment is expected, the test - simply hangs if it does not happen.*/ - test_set_step(1); - { - systime_t time = chVTGetSystemTimeX(); - while (time == chVTGetSystemTimeX()) { -#if defined(SIMULATOR) - _sim_check_for_interrupts(); -#endif - } - } -} - -static const testcase_t rt_test_002_004 = { - "System Tick Counter functionality", - NULL, - NULL, - rt_test_002_004_execute -}; - /**************************************************************************** * Exported data. ****************************************************************************/ @@ -276,7 +251,6 @@ const testcase_t * const rt_test_sequence_002_array[] = { &rt_test_002_001, &rt_test_002_002, &rt_test_002_003, - &rt_test_002_004, NULL }; diff --git a/test/rt/source/test/rt_test_sequence_003.c b/test/rt/source/test/rt_test_sequence_003.c index 8598b12ce..98404f55c 100644 --- a/test/rt/source/test/rt_test_sequence_003.c +++ b/test/rt/source/test/rt_test_sequence_003.c @@ -21,19 +21,17 @@ * @file rt_test_sequence_003.c * @brief Test Sequence 003 code. * - * @page rt_test_sequence_003 [3] Threads Functionality + * @page rt_test_sequence_003 [3] Time and Intervals Functionality * * File: @ref rt_test_sequence_003.c * *

Description

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

Test Cases

* - @subpage rt_test_003_001 * - @subpage rt_test_003_002 - * - @subpage rt_test_003_003 - * - @subpage rt_test_003_004 * . */ @@ -41,290 +39,117 @@ * Shared code. ****************************************************************************/ -static THD_FUNCTION(thread, p) { - - test_emit_token(*(char *)p); -} +#include "ch.h" /**************************************************************************** * Test cases. ****************************************************************************/ /** - * @page rt_test_003_001 [3.1] Thread Sleep functionality + * @page rt_test_003_001 [3.1] System Tick Counter functionality * *

Description

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

Test Steps

- * - [3.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. - * - [3.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. - * - [3.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. - * - [3.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. - * - [3.1.5] Function chThdSleepUntil() is tested with a timeline of - * "now" + 100 ticks. + * - [3.1.1] A System Tick Counter increment is expected, the test + * simply hangs if it does not happen. * . */ static void rt_test_003_001_execute(void) { - systime_t time; - /* [3.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.*/ + /* [3.1.1] A System Tick Counter increment is expected, the test + simply hangs if it does not happen.*/ 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"); - } - - /* [3.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"); - } - - /* [3.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"); - } - - /* [3.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"); - } - - /* [3.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"); + systime_t time = chVTGetSystemTimeX(); + while (time == chVTGetSystemTimeX()) { + } } + test_end_step(1); } static const testcase_t rt_test_003_001 = { - "Thread Sleep functionality", + "System Tick Counter functionality", NULL, NULL, rt_test_003_001_execute }; /** - * @page rt_test_003_002 [3.2] Ready List functionality, threads priority order + * @page rt_test_003_002 [3.2] Time ranges functionality * *

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. + * The functionality of the API @p chTimeIsInRangeX() is tested. * *

Test Steps

- * - [3.2.1] Creating 5 threads with increasing priority, execution - * sequence is tested. - * - [3.2.2] Creating 5 threads with decreasing priority, execution - * sequence is tested. - * - [3.2.3] Creating 5 threads with pseudo-random priority, execution - * sequence is tested. + * - [3.2.1] Checking case where start == end, it must always evaluate + * as in range. + * - [3.2.2] Checking boundaries for start < end. + * - [3.2.3] Checking boundaries for start > end. * . */ static void rt_test_003_002_execute(void) { - /* [3.2.1] Creating 5 threads with increasing priority, execution - sequence is tested.*/ + /* [3.2.1] Checking case where start == end, it must always evaluate + as in range.*/ 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"); - } + bool b; - /* [3.2.2] Creating 5 threads with decreasing priority, execution - sequence is tested.*/ + b = chTimeIsInRangeX((systime_t)0, (systime_t)0, (systime_t)0); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)-1, (systime_t)0, (systime_t)0); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)0, (systime_t)-1, (systime_t)-1); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)-1, (systime_t)-1, (systime_t)-1); + test_assert(b == true, "not in range"); + } + test_end_step(1); + + /* [3.2.2] Checking boundaries for start < end.*/ 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"); - } + bool b; - /* [3.2.3] Creating 5 threads with pseudo-random priority, execution - sequence is tested.*/ + b = chTimeIsInRangeX((systime_t)10, (systime_t)10, (systime_t)100); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)9, (systime_t)10, (systime_t)100); + test_assert(b == false, "in range"); + b = chTimeIsInRangeX((systime_t)99, (systime_t)10, (systime_t)100); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)100, (systime_t)10, (systime_t)100); + test_assert(b == false, "in range"); + } + test_end_step(2); + + /* [3.2.3] Checking boundaries for start > end.*/ 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"); + bool b; + + b = chTimeIsInRangeX((systime_t)100, (systime_t)100, (systime_t)10); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)99, (systime_t)100, (systime_t)10); + test_assert(b == false, "in range"); + b = chTimeIsInRangeX((systime_t)9, (systime_t)100, (systime_t)10); + test_assert(b == true, "not in range"); + b = chTimeIsInRangeX((systime_t)10, (systime_t)100, (systime_t)10); + test_assert(b == false, "in range"); } + test_end_step(3); } static const testcase_t rt_test_003_002 = { - "Ready List functionality, threads priority order", + "Time ranges functionality", NULL, NULL, rt_test_003_002_execute }; -/** - * @page rt_test_003_003 [3.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

- * - [3.3.1] Thread priority is increased by one then a check is - * performed. - * - [3.3.2] Thread priority is returned to the previous value then a - * check is performed. - * . - */ - -static void rt_test_003_003_execute(void) { - tprio_t prio, p1; - - /* [3.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"); - } - - /* [3.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"); - } -} - -static const testcase_t rt_test_003_003 = { - "Priority change test", - NULL, - NULL, - rt_test_003_003_execute -}; - -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) -/** - * @page rt_test_003_004 [3.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

- * - [3.4.1] Simulating a priority boost situation (prio > realprio). - * - [3.4.2] Raising thread priority above original priority but below - * the boosted level. - * - [3.4.3] Raising thread priority above the boosted level. - * - [3.4.4] Restoring original conditions. - * . - */ - -static void rt_test_003_004_execute(void) { - tprio_t prio, p1; - - /* [3.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"); - } - - /* [3.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"); - } - - /* [3.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"); - } - - /* [3.4.4] Restoring original conditions.*/ - test_set_step(4); - { - chSysLock(); - chThdGetSelfX()->prio = prio; - chThdGetSelfX()->realprio = prio; - chSysUnlock(); - } -} - -static const testcase_t rt_test_003_004 = { - "Priority change test with Priority Inheritance", - NULL, - NULL, - rt_test_003_004_execute -}; -#endif /* CH_CFG_USE_MUTEXES */ - /**************************************************************************** * Exported data. ****************************************************************************/ @@ -335,17 +160,13 @@ static const testcase_t rt_test_003_004 = { const testcase_t * const rt_test_sequence_003_array[] = { &rt_test_003_001, &rt_test_003_002, - &rt_test_003_003, -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) - &rt_test_003_004, -#endif NULL }; /** - * @brief Threads Functionality. + * @brief Time and Intervals Functionality. */ const testsequence_t rt_test_sequence_003 = { - "Threads Functionality", + "Time and Intervals Functionality", rt_test_sequence_003_array }; diff --git a/test/rt/source/test/rt_test_sequence_004.c b/test/rt/source/test/rt_test_sequence_004.c index 7ad2ab59a..167494ad0 100644 --- a/test/rt/source/test/rt_test_sequence_004.c +++ b/test/rt/source/test/rt_test_sequence_004.c @@ -21,16 +21,19 @@ * @file rt_test_sequence_004.c * @brief Test Sequence 004 code. * - * @page rt_test_sequence_004 [4] Suspend/Resume + * @page rt_test_sequence_004 [4] Threads Functionality * * File: @ref rt_test_sequence_004.c * *

Description

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

Test Cases

* - @subpage rt_test_004_001 + * - @subpage rt_test_004_002 + * - @subpage rt_test_004_003 + * - @subpage rt_test_004_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,68 +51,294 @@ static THD_FUNCTION(thread1, p) { ****************************************************************************/ /** - * @page rt_test_004_001 [4.1] Suspend and Resume functionality + * @page rt_test_004_001 [4.1] Thread Sleep functionality * *

Description

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

Test Steps

- * - [4.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. - * - [4.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. + * - [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. * . */ -static void rt_test_004_001_setup(void) { - tr1 = NULL; -} - static void rt_test_004_001_execute(void) { systime_t time; - msg_t msg; - /* [4.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.*/ + /* [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.*/ 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); - /* [4.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.*/ + /* [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); { - 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); + + /* [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 = { - "Suspend and Resume functionality", - rt_test_004_001_setup, + "Thread Sleep 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. ****************************************************************************/ @@ -125,13 +348,18 @@ static const testcase_t rt_test_004_001 = { */ 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 Suspend/Resume. + * @brief Threads Functionality. */ const testsequence_t rt_test_sequence_004 = { - "Suspend/Resume", + "Threads 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 29cebb994..6576d5700 100644 --- a/test/rt/source/test/rt_test_sequence_005.c +++ b/test/rt/source/test/rt_test_sequence_005.c @@ -21,66 +21,32 @@ * @file rt_test_sequence_005.c * @brief Test Sequence 005 code. * - * @page rt_test_sequence_005 [5] Counter Semaphores + * @page rt_test_sequence_005 [5] Suspend/Resume * * File: @ref rt_test_sequence_005.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_005_001 - * - @subpage rt_test_005_002 - * - @subpage rt_test_005_003 - * - @subpage rt_test_005_004 - * - @subpage rt_test_005_005 - * - @subpage rt_test_005_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,404 +54,68 @@ static THD_FUNCTION(thread4, p) { ****************************************************************************/ /** - * @page rt_test_005_001 [5.1] Semaphore primitives, no state change + * @page rt_test_005_001 [5.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

- * - [5.1.1] The function chSemWait() is invoked, after return the - * counter and the returned message are tested. - * - [5.1.2] The function chSemSignal() is invoked, after return the - * counter is tested. - * - [5.1.3] The function chSemReset() is invoked, after return the - * counter is tested. + * - [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. * . */ static void rt_test_005_001_setup(void) { - chSemObjectInit(&sem1, 1); -} - -static void rt_test_005_001_teardown(void) { - chSemReset(&sem1, 0); + tr1 = NULL; } static void rt_test_005_001_execute(void) { - - /* [5.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"); - } - - /* [5.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"); - } - - /* [5.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"); - } -} - -static const testcase_t rt_test_005_001 = { - "Semaphore primitives, no state change", - rt_test_005_001_setup, - rt_test_005_001_teardown, - rt_test_005_001_execute -}; - -/** - * @page rt_test_005_002 [5.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

- * - [5.2.1] Five threads are created with mixed priority levels (not - * increasing nor decreasing). Threads enqueue on a semaphore - * initialized to zero. - * - [5.2.2] The semaphore is signaled 5 times. The thread activation - * sequence is tested. - * . - */ - -static void rt_test_005_002_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_005_002_execute(void) { - - /* [5.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"); - } - - /* [5.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 - } -} - -static const testcase_t rt_test_005_002 = { - "Semaphore enqueuing test", - rt_test_005_002_setup, - NULL, - rt_test_005_002_execute -}; - -/** - * @page rt_test_005_003 [5.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

- * - [5.3.1] Testing special case TIME_IMMEDIATE. - * - [5.3.2] Testing non-timeout condition. - * - [5.3.3] Testing timeout condition. - * . - */ - -static void rt_test_005_003_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_005_003_execute(void) { - unsigned i; - systime_t target_time; + systime_t time; msg_t msg; - /* [5.3.1] Testing special case TIME_IMMEDIATE.*/ + /* [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.*/ 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"); - } - - /* [5.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(1); - /* [5.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"); - } -} - -static const testcase_t rt_test_005_003 = { - "Semaphore timeout test", - rt_test_005_003_setup, - NULL, - rt_test_005_003_execute -}; - -/** - * @page rt_test_005_004 [5.4] Testing chSemAddCounterI() functionality - * - *

Description

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

Test Steps

- * - [5.4.1] A thread is created, it goes to wait on the semaphore. - * - [5.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_005_004_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_005_004_execute(void) { - - /* [5.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"); - } - - /* [5.4.2] The semaphore counter is increased by two, it is then - tested to be one, the thread must have completed.*/ + /* [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.*/ 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_005_004 = { - "Testing chSemAddCounterI() functionality", - rt_test_005_004_setup, +static const testcase_t rt_test_005_001 = { + "Suspend and Resume functionality", + rt_test_005_001_setup, NULL, - rt_test_005_004_execute -}; - -/** - * @page rt_test_005_005 [5.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

- * - [5.5.1] An higher priority thread is created that performs - * non-atomical wait and signal operations on a semaphore. - * - [5.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. - * - [5.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_005_005_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_005_005_teardown(void) { - test_wait_threads(); -} - -static void rt_test_005_005_execute(void) { - - /* [5.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); - } - - /* [5.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"); - } - - /* [5.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"); - } -} - -static const testcase_t rt_test_005_005 = { - "Testing chSemWaitSignal() functionality", - rt_test_005_005_setup, - rt_test_005_005_teardown, - rt_test_005_005_execute -}; - -/** - * @page rt_test_005_006 [5.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

- * - [5.6.1] Creating a binary semaphore in "taken" state, the state is - * checked. - * - [5.6.2] Resetting the binary semaphore in "taken" state, the state - * must not change. - * - [5.6.3] Starting a signaler thread at a lower priority. - * - [5.6.4] Waiting for the binary semaphore to be signaled, the - * semaphore is expected to be taken. - * - [5.6.5] Signaling the binary semaphore, checking the binary - * semaphore state to be "not taken" and the underlying counter - * semaphore counter to be one. - * - [5.6.6] Signaling the binary semaphore again, the internal state - * must not change from "not taken". - * . - */ - -static void rt_test_005_006_teardown(void) { - test_wait_threads(); -} - -static void rt_test_005_006_execute(void) { - binary_semaphore_t bsem; - msg_t msg; - - /* [5.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"); - } - - /* [5.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"); - } - - /* [5.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); - } - - /* [5.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"); - } - - /* [5.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"); - } - - /* [5.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"); - } -} - -static const testcase_t rt_test_005_006 = { - "Testing Binary Semaphores special case", - NULL, - rt_test_005_006_teardown, - rt_test_005_006_execute + rt_test_005_001_execute }; /**************************************************************************** @@ -497,20 +127,13 @@ static const testcase_t rt_test_005_006 = { */ const testcase_t * const rt_test_sequence_005_array[] = { &rt_test_005_001, - &rt_test_005_002, - &rt_test_005_003, - &rt_test_005_004, - &rt_test_005_005, - &rt_test_005_006, NULL }; /** - * @brief Counter Semaphores. + * @brief Suspend/Resume. */ const testsequence_t rt_test_sequence_005 = { - "Counter Semaphores", + "Suspend/Resume", rt_test_sequence_005_array }; - -#endif /* CH_CFG_USE_SEMAPHORES */ diff --git a/test/rt/source/test/rt_test_sequence_006.c b/test/rt/source/test/rt_test_sequence_006.c index 3694755c1..cf4db61b7 100644 --- a/test/rt/source/test/rt_test_sequence_006.c +++ b/test/rt/source/test/rt_test_sequence_006.c @@ -21,18 +21,18 @@ * @file rt_test_sequence_006.c * @brief Test Sequence 006 code. * - * @page rt_test_sequence_006 [6] Mutexes, Condition Variables and Priority Inheritance + * @page rt_test_sequence_006 [6] Counter Semaphores * * File: @ref rt_test_sequence_006.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,996 +42,470 @@ * - @subpage rt_test_006_004 * - @subpage rt_test_006_005 * - @subpage rt_test_006_006 - * - @subpage rt_test_006_007 - * - @subpage rt_test_006_008 - * - @subpage rt_test_006_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_006_001 [6.1] Priority enqueuing test + * @page rt_test_006_001 [6.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

- * - [6.1.1] Getting the initial priority. - * - [6.1.2] Locking the mutex. - * - [6.1.3] Five threads are created that try to lock and unlock the - * mutex then terminate. The threads are created in ascending - * priority order. - * - [6.1.4] Unlocking the mutex, the threads will wakeup in priority - * order because the mutext queue is an ordered one. + * - [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. * . */ static void rt_test_006_001_setup(void) { - chMtxObjectInit(&m1); + chSemObjectInit(&sem1, 1); +} + +static void rt_test_006_001_teardown(void) { + chSemReset(&sem1, 0); } static void rt_test_006_001_execute(void) { - tprio_t prio; - /* [6.1.1] Getting the initial priority.*/ + /* [6.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; - /* [6.1.2] Locking the mutex.*/ + 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); { - chMtxLock(&m1); + chSemSignal(&sem1); + test_assert_lock(chSemGetCounterI(&sem1) == 1, "wrong counter value"); } + test_end_step(2); - /* [6.1.3] Five threads are created that try to lock and unlock the - mutex then terminate. The threads are created in ascending - priority order.*/ + /* [6.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"); - } - - /* [6.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"); + chSemReset(&sem1, 2); + test_assert_lock(chSemGetCounterI(&sem1) == 2, "wrong counter value"); } + test_end_step(3); } static const testcase_t rt_test_006_001 = { - "Priority enqueuing test", + "Semaphore primitives, no state change", rt_test_006_001_setup, - NULL, + rt_test_006_001_teardown, rt_test_006_001_execute }; -#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) /** - * @page rt_test_006_002 [6.2] Priority inheritance, simple case + * @page rt_test_006_002 [6.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

- * - [6.2.1] Getting the system time for test duration measurement. - * - [6.2.2] The three contenders threads are created and let run - * atomically, the goals sequence is tested, the threads must - * complete in priority order. - * - [6.2.3] Testing that all threads completed within the specified - * time windows (100mS...100mS+ALLOWED_DELAY). + * - [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) { - chMtxObjectInit(&m1); + chSemObjectInit(&sem1, 0); } static void rt_test_006_002_execute(void) { - systime_t time; - /* [6.2.1] Getting the system time for test duration measurement.*/ + /* [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); { - 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); - /* [6.2.2] The three contenders threads are created and let run - atomically, the goals sequence is tested, the threads must - complete in priority order.*/ + /* [6.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"); - } - - /* [6.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"); +#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 = { - "Priority inheritance, simple case", + "Semaphore enqueuing test", rt_test_006_002_setup, NULL, rt_test_006_002_execute }; -#endif /* CH_DBG_THREADS_PROFILING */ -#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) /** - * @page rt_test_006_003 [6.3] Priority inheritance, complex case + * @page rt_test_006_003 [6.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

- * - [6.3.1] Getting the system time for test duration measurement. - * - [6.3.2] The five contenders threads are created and let run - * atomically, the goals sequence is tested, the threads must - * complete in priority order. - * - [6.3.3] Testing that all threads completed within the specified - * time windows (110mS...110mS+ALLOWED_DELAY). + * - [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) { - chMtxObjectInit(&m1); /* Mutex B.*/ - chMtxObjectInit(&m2); /* Mutex A.*/ + chSemObjectInit(&sem1, 0); } static void rt_test_006_003_execute(void) { - systime_t time; + unsigned i; + systime_t target_time; + msg_t msg; - /* [6.3.1] Getting the system time for test duration measurement.*/ + /* [6.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); - /* [6.3.2] The five contenders threads are created and let run - atomically, the goals sequence is tested, the threads must - complete in priority order.*/ + /* [6.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); - /* [6.3.3] Testing that all threads completed within the specified - time windows (110mS...110mS+ALLOWED_DELAY).*/ + /* [6.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_006_003 = { - "Priority inheritance, complex case", + "Semaphore timeout test", rt_test_006_003_setup, NULL, rt_test_006_003_execute }; -#endif /* CH_DBG_THREADS_PROFILING */ /** - * @page rt_test_006_004 [6.4] Priority return verification + * @page rt_test_006_004 [6.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

- * - [6.4.1] Getting current thread priority P(0) and assigning to the - * threads A and B priorities +1 and +2. - * - [6.4.2] Spawning threads A and B at priorities P(A) and P(B). - * - [6.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. - * - [6.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. - * - [6.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. - * - [6.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. - * - [6.4.7] Unlocking M2, the priority should fall back to P(A). - * - [6.4.8] Unlocking M1, the priority should fall back to P(0). + * - [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) { - chMtxObjectInit(&m1); - chMtxObjectInit(&m2); -} - -static void rt_test_006_004_teardown(void) { - test_wait_threads(); + chSemObjectInit(&sem1, 0); } static void rt_test_006_004_execute(void) { - tprio_t p, pa, pb; - /* [6.4.1] Getting current thread priority P(0) and assigning to the - threads A and B priorities +1 and +2.*/ + /* [6.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); - /* [6.4.2] Spawning threads A and B at priorities P(A) and P(B).*/ + /* [6.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"); - } - - /* [6.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"); - } - - /* [6.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"); - } - - /* [6.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"); - } - - /* [6.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"); - } - - /* [6.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"); - } - - /* [6.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"); + 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); } static const testcase_t rt_test_006_004 = { - "Priority return verification", + "Testing chSemAddCounterI() functionality", rt_test_006_004_setup, - rt_test_006_004_teardown, + NULL, rt_test_006_004_execute }; -#if (!CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) /** - * @page rt_test_006_005 [6.5] Repeated locks, non recursive scenario + * @page rt_test_006_005 [6.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

- * - [6.5.1] Getting current thread priority for later checks. - * - [6.5.2] Locking the mutex first time, it must be possible because - * it is not owned. - * - [6.5.3] Locking the mutex second time, it must fail because it is - * already owned. - * - [6.5.4] Unlocking the mutex then it must not be owned anymore and - * the queue must be empty. - * - [6.5.5] Testing that priority has not changed after operations. - * - [6.5.6] Testing chMtxUnlockAll() behavior. - * - [6.5.7] Testing that priority has not changed after operations. + * - [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) { - chMtxObjectInit(&m1); + chSemObjectInit(&sem1, 0); +} + +static void rt_test_006_005_teardown(void) { + test_wait_threads(); } static void rt_test_006_005_execute(void) { - bool b; - tprio_t prio; - /* [6.5.1] Getting current thread priority for later checks.*/ + /* [6.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); - /* [6.5.2] Locking the mutex first time, it must be possible because - it is not owned.*/ + /* [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); { - 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); - /* [6.5.3] Locking the mutex second time, it must fail because it is - already owned.*/ + /* [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); { - b = chMtxTryLock(&m1); - test_assert(!b, "not locked"); - } - - /* [6.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"); - } - - /* [6.5.5] Testing that priority has not changed after operations.*/ - test_set_step(5); - { - test_assert(chThdGetPriorityX() == prio, "wrong priority level"); - } - - /* [6.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"); - } - - /* [6.5.7] Testing that priority has not changed after operations.*/ - test_set_step(7); - { - test_assert(chThdGetPriorityX() == prio, "wrong priority level"); + 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 = { - "Repeated locks, non recursive scenario", + "Testing chSemWaitSignal() functionality", rt_test_006_005_setup, - NULL, + rt_test_006_005_teardown, rt_test_006_005_execute }; -#endif /* !CH_CFG_USE_MUTEXES_RECURSIVE */ -#if (CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) /** - * @page rt_test_006_006 [6.6] Repeated locks using, recursive scenario + * @page rt_test_006_006 [6.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

- * - [6.6.1] Getting current thread priority for later checks. - * - [6.6.2] Locking the mutex first time, it must be possible because - * it is not owned. - * - [6.6.3] Locking the mutex second time, it must be possible because - * it is recursive. - * - [6.6.4] Unlocking the mutex then it must be still owned because - * recursivity. - * - [6.6.5] Unlocking the mutex then it must not be owned anymore and - * the queue must be empty. - * - [6.6.6] Testing that priority has not changed after operations. - * - [6.6.7] Testing consecutive chMtxTryLock()/chMtxTryLockS() calls - * and a final chMtxUnlockAllS(). - * - [6.6.8] Testing consecutive chMtxLock()/chMtxLockS() calls and a - * final chMtxUnlockAll(). - * - [6.6.9] Testing that priority has not changed after operations. + * - [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_setup(void) { - chMtxObjectInit(&m1); +static void rt_test_006_006_teardown(void) { + test_wait_threads(); } static void rt_test_006_006_execute(void) { - bool b; - tprio_t prio; + binary_semaphore_t bsem; + msg_t msg; - /* [6.6.1] Getting current thread priority for later checks.*/ + /* [6.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); - /* [6.6.2] Locking the mutex first time, it must be possible because - it is not owned.*/ + /* [6.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); - /* [6.6.3] Locking the mutex second time, it must be possible because - it is recursive.*/ + /* [6.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); - /* [6.6.4] Unlocking the mutex then it must be still owned because - recursivity.*/ + /* [6.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); - /* [6.6.5] Unlocking the mutex then it must not be owned anymore and - the queue must be empty.*/ + /* [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); { - 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); - /* [6.6.6] Testing that priority has not changed after operations.*/ + /* [6.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"); - } - - /* [6.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"); - } - - /* [6.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"); - } - - /* [6.6.9] Testing that priority has not changed after operations.*/ - test_set_step(9); - { - 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); } static const testcase_t rt_test_006_006 = { - "Repeated locks using, recursive scenario", - rt_test_006_006_setup, + "Testing Binary Semaphores special case", NULL, + rt_test_006_006_teardown, rt_test_006_006_execute }; -#endif /* CH_CFG_USE_MUTEXES_RECURSIVE */ - -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) -/** - * @page rt_test_006_007 [6.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

- * - [6.7.1] Starting the five threads with increasing priority, the - * threads will queue on the condition variable. - * - [6.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_006_007_setup(void) { - chCondObjectInit(&c1); - chMtxObjectInit(&m1); -} - -static void rt_test_006_007_execute(void) { - - /* [6.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"); - } - - /* [6.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"); - } -} - -static const testcase_t rt_test_006_007 = { - "Condition Variable signal test", - rt_test_006_007_setup, - NULL, - rt_test_006_007_execute -}; -#endif /* CH_CFG_USE_CONDVARS */ - -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) -/** - * @page rt_test_006_008 [6.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

- * - [6.8.1] Starting the five threads with increasing priority, the - * threads will queue on the condition variable. - * - [6.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_006_008_setup(void) { - chCondObjectInit(&c1); - chMtxObjectInit(&m1); -} - -static void rt_test_006_008_execute(void) { - - /* [6.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"); - } - - /* [6.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"); - } -} - -static const testcase_t rt_test_006_008 = { - "Condition Variable broadcast test", - rt_test_006_008_setup, - NULL, - rt_test_006_008_execute -}; -#endif /* CH_CFG_USE_CONDVARS */ - -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) -/** - * @page rt_test_006_009 [6.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

- * - [6.9.1] Reading current base priority. - * - [6.9.2] Thread A is created at priority P(+1), it locks M2, locks - * M1 and goes to wait on C1. - * - [6.9.3] Thread C is created at priority P(+2), it enqueues on M1 - * and boosts TA priority at P(+2). - * - [6.9.4] Thread B is created at priority P(+3), it enqueues on M2 - * and boosts TA priority at P(+3). - * - [6.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. - * - [6.9.6] Signaling C1: TC wakes up, unlocks M1 and completes. - * - [6.9.7] Checking the order of operations. - * . - */ - -static void rt_test_006_009_setup(void) { - chCondObjectInit(&c1); - chMtxObjectInit(&m1); - chMtxObjectInit(&m2); -} - -static void rt_test_006_009_execute(void) { - tprio_t prio; - - /* [6.9.1] Reading current base priority.*/ - test_set_step(1); - { - prio = chThdGetPriorityX(); - } - - /* [6.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"); - } - - /* [6.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"); - } - - /* [6.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"); - } - - /* [6.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); - } - - /* [6.9.6] Signaling C1: TC wakes up, unlocks M1 and completes.*/ - test_set_step(6); - { - chCondSignal(&c1); - } - - /* [6.9.7] Checking the order of operations.*/ - test_set_step(7); - { - test_wait_threads(); - test_assert_sequence("ABC", "invalid sequence"); - } -} - -static const testcase_t rt_test_006_009 = { - "Condition Variable priority boost test", - rt_test_006_009_setup, - NULL, - rt_test_006_009_execute -}; -#endif /* CH_CFG_USE_CONDVARS */ /**************************************************************************** * Exported data. @@ -1042,37 +516,20 @@ static const testcase_t rt_test_006_009 = { */ const testcase_t * const rt_test_sequence_006_array[] = { &rt_test_006_001, -#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) &rt_test_006_002, -#endif -#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) &rt_test_006_003, -#endif &rt_test_006_004, -#if (!CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) &rt_test_006_005, -#endif -#if (CH_CFG_USE_MUTEXES_RECURSIVE) || defined(__DOXYGEN__) &rt_test_006_006, -#endif -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) - &rt_test_006_007, -#endif -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) - &rt_test_006_008, -#endif -#if (CH_CFG_USE_CONDVARS) || defined(__DOXYGEN__) - &rt_test_006_009, -#endif NULL }; /** - * @brief Mutexes, Condition Variables and Priority Inheritance. + * @brief Counter Semaphores. */ const testsequence_t rt_test_sequence_006 = { - "Mutexes, Condition Variables and Priority Inheritance", + "Counter Semaphores", rt_test_sequence_006_array }; -#endif /* CH_CFG_USE_MUTEXES */ +#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 62a9df95b..aa717c9bf 100644 --- a/test/rt/source/test/rt_test_sequence_007.c +++ b/test/rt/source/test/rt_test_sequence_007.c @@ -21,92 +21,1063 @@ * @file rt_test_sequence_007.c * @brief Test Sequence 007 code. * - * @page rt_test_sequence_007 [7] Synchronous Messages + * @page rt_test_sequence_007 [7] Mutexes, Condition Variables and Priority Inheritance * * File: @ref rt_test_sequence_007.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_007_001 + * - @subpage rt_test_007_002 + * - @subpage rt_test_007_003 + * - @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_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_007_001 [7.1] Messages Server loop + * @page rt_test_007_001 [7.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

- * - [7.1.1] Starting the messenger thread. - * - [7.1.2] Waiting for four messages then testing the receive order. + * - [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. * . */ -static void rt_test_007_001_execute(void) { - thread_t *tp; - msg_t msg; +static void rt_test_007_001_setup(void) { + chMtxObjectInit(&m1); +} - /* [7.1.1] Starting the messenger thread.*/ +static void rt_test_007_001_execute(void) { + tprio_t prio; + + /* [7.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); - /* [7.1.2] Waiting for four messages then testing the receive - order.*/ + /* [7.1.2] Locking the mutex.*/ test_set_step(2); { - 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"); + chMtxLock(&m1); } + 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.*/ + 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); + + /* [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 = { - "Messages Server loop", - NULL, + "Priority enqueuing test", + rt_test_007_001_setup, NULL, rt_test_007_001_execute }; +#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) +/** + * @page rt_test_007_002 [7.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

+ * - [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). + * . + */ + +static void rt_test_007_002_setup(void) { + chMtxObjectInit(&m1); +} + +static void rt_test_007_002_execute(void) { + systime_t time; + + /* [7.2.1] Getting the system time for test duration measurement.*/ + test_set_step(1); + { + time = test_wait_tick(); + } + 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.*/ + 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); + + /* [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", + 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 + * + *

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

+ * - [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). + * . + */ + +static void rt_test_007_003_setup(void) { + chMtxObjectInit(&m1); /* Mutex B.*/ + chMtxObjectInit(&m2); /* Mutex A.*/ +} + +static void rt_test_007_003_execute(void) { + systime_t time; + + /* [7.3.1] Getting the system time for test duration measurement.*/ + test_set_step(1); + { + time = test_wait_tick(); + } + 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.*/ + 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); + + /* [7.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_007_003 = { + "Priority inheritance, complex case", + 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 + * + *

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

+ * - [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). + * . + */ + +static void rt_test_007_004_setup(void) { + chMtxObjectInit(&m1); + chMtxObjectInit(&m2); +} + +static void rt_test_007_004_teardown(void) { + test_wait_threads(); +} + +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.*/ + test_set_step(1); + { + p = chThdGetPriorityX(); + pa = p + 1; + pb = p + 2; + } + test_end_step(1); + + /* [7.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); + + /* [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", + rt_test_007_004_setup, + rt_test_007_004_teardown, + 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 + * + *

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

+ * - [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. + * . + */ + +static void rt_test_007_005_setup(void) { + chMtxObjectInit(&m1); +} + +static void rt_test_007_005_execute(void) { + bool b; + tprio_t prio; + + /* [7.5.1] Getting current thread priority for later checks.*/ + test_set_step(1); + { + prio = chThdGetPriorityX(); + } + test_end_step(1); + + /* [7.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); + + /* [7.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); + + /* [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", + rt_test_007_005_setup, + NULL, + 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 + * + *

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

+ * - [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. + * . + */ + +static void rt_test_007_006_setup(void) { + chMtxObjectInit(&m1); +} + +static void rt_test_007_006_execute(void) { + bool b; + tprio_t prio; + + /* [7.6.1] Getting current thread priority for later checks.*/ + test_set_step(1); + { + prio = chThdGetPriorityX(); + } + test_end_step(1); + + /* [7.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); + + /* [7.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); + + /* [7.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); + + /* [7.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); + + /* [7.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); + + /* [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, + NULL, + 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. ****************************************************************************/ @@ -116,15 +1087,37 @@ static const testcase_t rt_test_007_001 = { */ 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 Synchronous Messages. + * @brief Mutexes, Condition Variables and Priority Inheritance. */ const testsequence_t rt_test_sequence_007 = { - "Synchronous Messages", + "Mutexes, Condition Variables and Priority Inheritance", rt_test_sequence_007_array }; -#endif /* CH_CFG_USE_MESSAGES */ +#endif /* CH_CFG_USE_MUTEXES */ diff --git a/test/rt/source/test/rt_test_sequence_008.c b/test/rt/source/test/rt_test_sequence_008.c index d2bb80504..26eba5940 100644 --- a/test/rt/source/test/rt_test_sequence_008.c +++ b/test/rt/source/test/rt_test_sequence_008.c @@ -21,56 +21,37 @@ * @file rt_test_sequence_008.c * @brief Test Sequence 008 code. * - * @page rt_test_sequence_008 [8] Event Sources and Event Flags + * @page rt_test_sequence_008 [8] Synchronous Messages * * File: @ref rt_test_sequence_008.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_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 * . */ -#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,477 +59,56 @@ static THD_FUNCTION(evt_thread7, p) { ****************************************************************************/ /** - * @page rt_test_008_001 [8.1] Events registration + * @page rt_test_008_001 [8.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

- * - [8.1.1] An Event Source is initialized. - * - [8.1.2] Two Event Listeners are registered on the Event Source, - * the Event Source is tested to have listeners. - * - [8.1.3] An Event Listener is unregistered, the Event Source must - * still have listeners. - * - [8.1.4] An Event Listener is unregistered, the Event Source must - * not have listeners. + * - [8.1.1] Starting the messenger thread. + * - [8.1.2] Waiting for four messages then testing the receive order. * . */ static void rt_test_008_001_execute(void) { - event_listener_t el1, el2; + thread_t *tp; + msg_t msg; - /* [8.1.1] An Event Source is initialized.*/ + /* [8.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); - /* [8.1.2] Two Event Listeners are registered on the Event Source, - the Event Source is tested to have listeners.*/ + /* [8.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; - /* [8.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"); - } - - /* [8.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"); + 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); } static const testcase_t rt_test_008_001 = { - "Events registration", + "Messages Server loop", NULL, NULL, rt_test_008_001_execute }; -/** - * @page rt_test_008_002 [8.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

- * - [8.2.1] Three evenf flag bits are raised then chEvtDispatch() is - * invoked, the sequence of handlers calls is tested. - * . - */ - -static void rt_test_008_002_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_008_002_execute(void) { - - /* [8.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"); - } -} - -static const testcase_t rt_test_008_002 = { - "Event Flags dispatching", - rt_test_008_002_setup, - NULL, - rt_test_008_002_execute -}; - -/** - * @page rt_test_008_003 [8.3] Events Flags wait using chEvtWaitOne() - * - *

Description

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

Test Steps

- * - [8.3.1] Setting three event flags. - * - [8.3.2] Calling chEvtWaitOne() three times, each time a single - * flag must be returned in order of priority. - * - [8.3.3] Getting current time and starting a signaler thread, the - * thread will set an event flag after 50mS. - * - [8.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_008_003_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_008_003_execute(void) { - eventmask_t m; - systime_t target_time; - - /* [8.3.1] Setting three event flags.*/ - test_set_step(1); - { - chEvtAddEvents(7); - } - - /* [8.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"); - } - - /* [8.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()); - } - - /* [8.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(); - } -} - -static const testcase_t rt_test_008_003 = { - "Events Flags wait using chEvtWaitOne()", - rt_test_008_003_setup, - NULL, - rt_test_008_003_execute -}; - -/** - * @page rt_test_008_004 [8.4] Events Flags wait using chEvtWaitAny() - * - *

Description

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

Test Steps

- * - [8.4.1] Setting two, non contiguous, event flags. - * - [8.4.2] Calling chEvtWaitAny() one time, the two flags must be - * returned. - * - [8.4.3] Getting current time and starting a signaler thread, the - * thread will set an event flag after 50mS. - * - [8.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_008_004_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_008_004_execute(void) { - eventmask_t m; - systime_t target_time; - - /* [8.4.1] Setting two, non contiguous, event flags.*/ - test_set_step(1); - { - chEvtAddEvents(5); - } - - /* [8.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"); - } - - /* [8.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()); - } - - /* [8.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(); - } -} - -static const testcase_t rt_test_008_004 = { - "Events Flags wait using chEvtWaitAny()", - rt_test_008_004_setup, - NULL, - rt_test_008_004_execute -}; - -/** - * @page rt_test_008_005 [8.5] Events Flags wait using chEvtWaitAll() - * - *

Description

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

Test Steps

- * - [8.5.1] Setting two, non contiguous, event flags. - * - [8.5.2] Calling chEvtWaitAll() one time, the two flags must be - * returned. - * - [8.5.3] Setting one event flag. - * - [8.5.4] Getting current time and starting a signaler thread, the - * thread will set another event flag after 50mS. - * - [8.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_008_005_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_008_005_execute(void) { - eventmask_t m; - systime_t target_time; - - /* [8.5.1] Setting two, non contiguous, event flags.*/ - test_set_step(1); - { - chEvtAddEvents(5); - } - - /* [8.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"); - } - - /* [8.5.3] Setting one event flag.*/ - test_set_step(3); - { - chEvtAddEvents(4); - } - - /* [8.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()); - } - - /* [8.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(); - } -} - -static const testcase_t rt_test_008_005 = { - "Events Flags wait using chEvtWaitAll()", - rt_test_008_005_setup, - NULL, - rt_test_008_005_execute -}; - -#if (CH_CFG_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) -/** - * @page rt_test_008_006 [8.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

- * - [8.6.1] The functions are invoked first with TIME_IMMEDIATE - * timeout, the timeout condition is tested. - * - [8.6.2] The functions are invoked first with a 50mS timeout, the - * timeout condition is tested. - * . - */ - -static void rt_test_008_006_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); -} - -static void rt_test_008_006_execute(void) { - eventmask_t m; - - /* [8.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"); - } - - /* [8.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"); - } -} - -static const testcase_t rt_test_008_006 = { - "Events Flags wait timeouts", - rt_test_008_006_setup, - NULL, - rt_test_008_006_execute -}; -#endif /* CH_CFG_USE_EVENTS_TIMEOUT */ - -/** - * @page rt_test_008_007 [8.7] Broadcasting using chEvtBroadcast() - * - *

Description

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

Test Steps

- * - [8.7.1] Registering on two event sources associating them with - * flags 1 and 4. - * - [8.7.2] Getting current time and starting a broadcaster thread, - * the thread broadcast the first Event Source immediately and the - * other after 50mS. - * - [8.7.3] Calling chEvtWaitAll() then verifying that both event - * flags have been received after 50mS and that the event flags mask - * has been emptied. - * - [8.7.4] Unregistering from the Event Sources. - * . - */ - -static void rt_test_008_007_setup(void) { - chEvtGetAndClearEvents(ALL_EVENTS); - chEvtObjectInit(&es1); - chEvtObjectInit(&es2); -} - -static void rt_test_008_007_execute(void) { - eventmask_t m; - event_listener_t el1, el2; - systime_t target_time; - - /* [8.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); - } - - /* [8.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"); - } - - /* [8.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(); - } - - /* [8.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"); - } -} - -static const testcase_t rt_test_008_007 = { - "Broadcasting using chEvtBroadcast()", - rt_test_008_007_setup, - NULL, - rt_test_008_007_execute -}; - /**************************************************************************** * Exported data. ****************************************************************************/ @@ -558,23 +118,15 @@ static const testcase_t rt_test_008_007 = { */ const testcase_t * const rt_test_sequence_008_array[] = { &rt_test_008_001, - &rt_test_008_002, - &rt_test_008_003, - &rt_test_008_004, - &rt_test_008_005, -#if (CH_CFG_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) - &rt_test_008_006, -#endif - &rt_test_008_007, NULL }; /** - * @brief Event Sources and Event Flags. + * @brief Synchronous Messages. */ const testsequence_t rt_test_sequence_008 = { - "Event Sources and Event Flags", + "Synchronous Messages", rt_test_sequence_008_array }; -#endif /* CH_CFG_USE_EVENTS */ +#endif /* CH_CFG_USE_MESSAGES */ diff --git a/test/rt/source/test/rt_test_sequence_009.c b/test/rt/source/test/rt_test_sequence_009.c index 5abc45081..b54cab562 100644 --- a/test/rt/source/test/rt_test_sequence_009.c +++ b/test/rt/source/test/rt_test_sequence_009.c @@ -21,247 +21,557 @@ * @file rt_test_sequence_009.c * @brief Test Sequence 009 code. * - * @page rt_test_sequence_009 [9] Dynamic threads + * @page rt_test_sequence_009 [9] Event Sources and Event Flags * * File: @ref rt_test_sequence_009.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_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_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_009_001 [9.1] Threads creation from Memory Heap + * @page rt_test_009_001 [9.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

- * - [9.1.1] Getting base priority for threads. - * - [9.1.2] Getting heap info before the test. - * - [9.1.3] Creating thread 1, it is expected to succeed. - * - [9.1.4] Creating thread 2, it is expected to succeed. - * - [9.1.5] Creating thread 3, it is expected to fail. - * - [9.1.6] Letting threads execute then checking the start order and - * freeing memory. - * - [9.1.7] Getting heap info again for verification. + * - [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. * . */ -static void rt_test_009_001_setup(void) { - chHeapObjectInit(&heap1, test_buffer, sizeof test_buffer); -} - static void rt_test_009_001_execute(void) { - size_t n1, total1, largest1; - size_t n2, total2, largest2; - tprio_t prio; + event_listener_t el1, el2; - /* [9.1.1] Getting base priority for threads.*/ + /* [9.1.1] An Event Source is initialized.*/ test_set_step(1); { - prio = chThdGetPriorityX(); + chEvtObjectInit(&es1); } + test_end_step(1); - /* [9.1.2] Getting heap info before the test.*/ + /* [9.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); - /* [9.1.3] Creating thread 1, it is expected to succeed.*/ + /* [9.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); - /* [9.1.4] Creating thread 2, it is expected to succeed.*/ + /* [9.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"); - } - - /* [9.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"); - } - - /* [9.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"); - } - - /* [9.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"); + chEvtUnregister(&es1, &el2); + test_assert_lock(!chEvtIsListeningI(&es1), "stuck listener"); } + test_end_step(4); } static const testcase_t rt_test_009_001 = { - "Threads creation from Memory Heap", - rt_test_009_001_setup, + "Events registration", + NULL, NULL, rt_test_009_001_execute }; -#endif /* CH_CFG_USE_HEAP */ -#if (CH_CFG_USE_MEMPOOLS) || defined(__DOXYGEN__) /** - * @page rt_test_009_002 [9.2] Threads creation from Memory Pool + * @page rt_test_009_002 [9.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

- * - [9.2.1] Adding four working areas to the pool. - * - [9.2.2] Getting base priority for threads. - * - [9.2.3] Creating the five threads. - * - [9.2.4] Testing that only the fifth thread creation failed. - * - [9.2.5] Letting them run, free the memory then checking the - * execution sequence. - * - [9.2.6] Testing that the pool contains four elements again. + * - [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) { - chPoolObjectInit(&mp1, THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), NULL); + chEvtGetAndClearEvents(ALL_EVENTS); } static void rt_test_009_002_execute(void) { - unsigned i; - tprio_t prio; - /* [9.2.1] Adding four working areas to the pool.*/ + /* [9.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]); - } - - /* [9.2.2] Getting base priority for threads.*/ - test_set_step(2); - { - prio = chThdGetPriorityX(); - } - - /* [9.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"); - } - - /* [9.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"); - } - - /* [9.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"); - } - - /* [9.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"); + chEvtDispatch(evhndl, 7); + test_assert_sequence("ABC", "invalid sequence"); } + test_end_step(1); } static const testcase_t rt_test_009_002 = { - "Threads creation from Memory Pool", + "Event Flags dispatching", rt_test_009_002_setup, NULL, rt_test_009_002_execute }; -#endif /* CH_CFG_USE_MEMPOOLS */ + +/** + * @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. @@ -271,21 +581,24 @@ static const testcase_t rt_test_009_002 = { * @brief Array of test cases. */ const testcase_t * const rt_test_sequence_009_array[] = { -#if (CH_CFG_USE_HEAP) || defined(__DOXYGEN__) &rt_test_009_001, -#endif -#if (CH_CFG_USE_MEMPOOLS) || defined(__DOXYGEN__) &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 Dynamic threads. + * @brief Event Sources and Event Flags. */ const testsequence_t rt_test_sequence_009 = { - "Dynamic threads", + "Event Sources and Event Flags", rt_test_sequence_009_array }; -#endif /* CH_CFG_USE_DYNAMIC */ +#endif /* CH_CFG_USE_EVENTS */ diff --git a/test/rt/source/test/rt_test_sequence_010.c b/test/rt/source/test/rt_test_sequence_010.c index 1c77343d0..10ac2080e 100644 --- a/test/rt/source/test/rt_test_sequence_010.c +++ b/test/rt/source/test/rt_test_sequence_010.c @@ -21,978 +21,260 @@ * @file rt_test_sequence_010.c * @brief Test Sequence 010 code. * - * @page rt_test_sequence_010 [10] Benchmarks + * @page rt_test_sequence_010 [10] Dynamic threads * * File: @ref rt_test_sequence_010.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_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 - * - @subpage rt_test_010_008 - * - @subpage rt_test_010_009 - * - @subpage rt_test_010_010 - * - @subpage rt_test_010_011 - * - @subpage rt_test_010_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_010_001 [10.1] Messages performance #1 + * @page rt_test_010_001 [10.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

- * - [10.1.1] The messenger thread is started at a lower priority than - * the current thread. - * - [10.1.2] The number of messages exchanged is counted in a one - * second time window. - * - [10.1.3] Score is printed. + * - [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. * . */ -static void rt_test_010_001_execute(void) { - uint32_t n; +static void rt_test_010_001_setup(void) { + chHeapObjectInit(&heap1, test_buffer, sizeof test_buffer); +} - /* [10.1.1] The messenger thread is started at a lower priority than - the current thread.*/ +static void rt_test_010_001_execute(void) { + size_t n1, total1, largest1; + size_t n2, total2, largest2; + tprio_t prio; + + /* [10.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); - /* [10.1.2] The number of messages exchanged is counted in a one - second time window.*/ + /* [10.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); - /* [10.1.3] Score is printed.*/ + /* [10.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"); + 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); + + /* [10.1.4] Creating thread 2, it is expected to succeed.*/ + 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"); + } + 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 = { - "Messages performance #1", - NULL, + "Threads creation from Memory Heap", + rt_test_010_001_setup, NULL, rt_test_010_001_execute }; -#endif /* CH_CFG_USE_MESSAGES */ +#endif /* CH_CFG_USE_HEAP */ -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MEMPOOLS) || defined(__DOXYGEN__) /** - * @page rt_test_010_002 [10.2] Messages performance #2 + * @page rt_test_010_002 [10.2] Threads creation from Memory Pool * *

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. + * 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_MESSAGES + * - CH_CFG_USE_MEMPOOLS * . * *

Test Steps

- * - [10.2.1] The messenger thread is started at an higher priority - * than the current thread. - * - [10.2.2] The number of messages exchanged is counted in a one - * second time window. - * - [10.2.3] Score is printed. + * - [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. * . */ -static void rt_test_010_002_execute(void) { - uint32_t n; +static void rt_test_010_002_setup(void) { + chPoolObjectInit(&mp1, THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), NULL); +} - /* [10.2.1] The messenger thread is started at an higher priority - than the current thread.*/ +static void rt_test_010_002_execute(void) { + unsigned i; + tprio_t prio; + + /* [10.2.1] Adding four working areas to the pool.*/ test_set_step(1); { - threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, bmk_thread1, NULL); + for (i = 0; i < 4; i++) + chPoolFree(&mp1, wa[i]); } + test_end_step(1); - /* [10.2.2] The number of messages exchanged is counted in a one - second time window.*/ + /* [10.2.2] Getting base priority for threads.*/ test_set_step(2); { - n = msg_loop_test(threads[0]); - test_wait_threads(); + prio = chThdGetPriorityX(); } + test_end_step(2); - /* [10.2.3] Score is printed.*/ + /* [10.2.3] Creating the five threads.*/ test_set_step(3); { - test_print("--- Score : "); - test_printn(n); - test_print(" msgs/S, "); - test_printn(n << 1); - test_println(" ctxswc/S"); + 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 = { - "Messages performance #2", - NULL, + "Threads creation from Memory Pool", + rt_test_010_002_setup, NULL, rt_test_010_002_execute }; -#endif /* CH_CFG_USE_MESSAGES */ - -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) -/** - * @page rt_test_010_003 [10.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

- * - [10.3.1] The messenger thread is started at an higher priority - * than the current thread. - * - [10.3.2] Four threads are started at a lower priority than the - * current thread. - * - [10.3.3] The number of messages exchanged is counted in a one - * second time window. - * - [10.3.4] Score is printed. - * . - */ - -static void rt_test_010_003_execute(void) { - uint32_t n; - - /* [10.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); - } - - /* [10.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); - } - - /* [10.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(); - } - - /* [10.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"); - } -} - -static const testcase_t rt_test_010_003 = { - "Messages performance #3", - NULL, - NULL, - rt_test_010_003_execute -}; -#endif /* CH_CFG_USE_MESSAGES */ - -/** - * @page rt_test_010_004 [10.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

- * - [10.4.1] Starting the target thread at an higher priority level. - * - [10.4.2] Waking up the thread as fast as possible in a one second - * time window. - * - [10.4.3] Stopping the target thread. - * - [10.4.4] Score is printed. - * . - */ - -static void rt_test_010_004_execute(void) { - thread_t *tp; - uint32_t n; - - /* [10.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); - } - - /* [10.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)); - } - - /* [10.4.3] Stopping the target thread.*/ - test_set_step(3); - { - chSysLock(); - chSchWakeupS(tp, MSG_TIMEOUT); - chSysUnlock(); - test_wait_threads(); - } - - /* [10.4.4] Score is printed.*/ - test_set_step(4); - { - test_print("--- Score : "); - test_printn(n * 2); - test_println(" ctxswc/S"); - } -} - -static const testcase_t rt_test_010_004 = { - "Context Switch performance", - NULL, - NULL, - rt_test_010_004_execute -}; - -/** - * @page rt_test_010_005 [10.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

- * - [10.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. - * - [10.5.2] Score is printed. - * . - */ - -static void rt_test_010_005_execute(void) { - uint32_t n; - tprio_t prio = chThdGetPriorityX() - 1; - systime_t start, end; - - /* [10.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)); - } - - /* [10.5.2] Score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n); - test_println(" threads/S"); - } -} - -static const testcase_t rt_test_010_005 = { - "Threads performance, full cycle", - NULL, - NULL, - rt_test_010_005_execute -}; - -/** - * @page rt_test_010_006 [10.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

- * - [10.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. - * - [10.6.2] Score is printed. - * . - */ - -static void rt_test_010_006_execute(void) { - uint32_t n; - tprio_t prio = chThdGetPriorityX() + 1; - systime_t start, end; - - /* [10.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)); - } - - /* [10.6.2] Score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n); - test_println(" threads/S"); - } -} - -static const testcase_t rt_test_010_006 = { - "Threads performance, create/exit only", - NULL, - NULL, - rt_test_010_006_execute -}; - -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) -/** - * @page rt_test_010_007 [10.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

- * - [10.7.1] Five threads are created at higher priority that - * immediately enqueue on a semaphore. - * - [10.7.2] The semaphore is reset waking up the five threads. The - * operation is repeated continuously in a one-second time window. - * - [10.7.3] The five threads are terminated. - * - [10.7.4] The score is printed. - * . - */ - -static void rt_test_010_007_setup(void) { - chSemObjectInit(&sem1, 0); -} - -static void rt_test_010_007_execute(void) { - uint32_t n; - - /* [10.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); - } - - /* [10.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)); - } - - /* [10.7.3] The five threads are terminated.*/ - test_set_step(3); - { - test_terminate_threads(); - chSemReset(&sem1, 0); - test_wait_threads(); - } - - /* [10.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"); - } -} - -static const testcase_t rt_test_010_007 = { - "Mass reschedule performance", - rt_test_010_007_setup, - NULL, - rt_test_010_007_execute -}; -#endif /* CH_CFG_USE_SEMAPHORES */ - -/** - * @page rt_test_010_008 [10.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

- * - [10.8.1] The five threads are created at lower priority. The - * threds have equal priority and start calling @p chThdYield() - * continuously. - * - [10.8.2] Waiting one second then terminating the 5 threads. - * - [10.8.3] The score is printed. - * . - */ - -static void rt_test_010_008_execute(void) { - uint32_t n; - - /* [10.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); - } - - /* [10.8.2] Waiting one second then terminating the 5 threads.*/ - test_set_step(2); - { - chThdSleepSeconds(1); - test_terminate_threads(); - test_wait_threads(); - } - - /* [10.8.3] The score is printed.*/ - test_set_step(3); - { - test_print("--- Score : "); - test_printn(n); - test_println(" ctxswc/S"); - } -} - -static const testcase_t rt_test_010_008 = { - "Round-Robin voluntary reschedule", - NULL, - NULL, - rt_test_010_008_execute -}; - -/** - * @page rt_test_010_009 [10.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

- * - [10.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. - * - [10.9.2] The score is printed. - * . - */ - -static void rt_test_010_009_execute(void) { - static virtual_timer_t vt1, vt2; - uint32_t n; - - /* [10.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)); - } - - /* [10.9.2] The score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n * 2); - test_println(" timers/S"); - } -} - -static const testcase_t rt_test_010_009 = { - "Virtual Timers set/reset performance", - NULL, - NULL, - rt_test_010_009_execute -}; - -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) -/** - * @page rt_test_010_010 [10.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

- * - [10.10.1] A semaphore is teken and released. The operation is - * repeated continuously in a one-second time window. - * - [10.10.2] The score is printed. - * . - */ - -static void rt_test_010_010_setup(void) { - chSemObjectInit(&sem1, 1); -} - -static void rt_test_010_010_execute(void) { - uint32_t n; - - /* [10.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)); - } - - /* [10.10.2] The score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n * 4); - test_println(" wait+signal/S"); - } -} - -static const testcase_t rt_test_010_010 = { - "Semaphores wait/signal performance", - rt_test_010_010_setup, - NULL, - rt_test_010_010_execute -}; -#endif /* CH_CFG_USE_SEMAPHORES */ - -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) -/** - * @page rt_test_010_011 [10.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

- * - [10.11.1] A mutex is locked and unlocked. The operation is - * repeated continuously in a one-second time window. - * - [10.11.2] The score is printed. - * . - */ - -static void rt_test_010_011_setup(void) { - chMtxObjectInit(&mtx1); -} - -static void rt_test_010_011_execute(void) { - uint32_t n; - - /* [10.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)); - } - - /* [10.11.2] The score is printed.*/ - test_set_step(2); - { - test_print("--- Score : "); - test_printn(n * 4); - test_println(" lock+unlock/S"); - } -} - -static const testcase_t rt_test_010_011 = { - "Mutexes lock/unlock performance", - rt_test_010_011_setup, - NULL, - rt_test_010_011_execute -}; -#endif /* CH_CFG_USE_MUTEXES */ - -/** - * @page rt_test_010_012 [10.12] RAM Footprint - * - *

Description

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

Test Steps

- * - [10.12.1] The size of the system area is printed. - * - [10.12.2] The size of a thread structure is printed. - * - [10.12.3] The size of a virtual timer structure is printed. - * - [10.12.4] The size of a semaphore structure is printed. - * - [10.12.5] The size of a mutex is printed. - * - [10.12.6] The size of a condition variable is printed. - * - [10.12.7] The size of an event source is printed. - * - [10.12.8] The size of an event listener is printed. - * - [10.12.9] The size of a mailbox is printed. - * . - */ - -static void rt_test_010_012_execute(void) { - - /* [10.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"); - } - - /* [10.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"); - } - - /* [10.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"); - } - - /* [10.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 - } - - /* [10.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 - } - - /* [10.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 - } - - /* [10.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 - } - - /* [10.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 - } - - /* [10.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 - } -} - -static const testcase_t rt_test_010_012 = { - "RAM Footprint", - NULL, - NULL, - rt_test_010_012_execute -}; +#endif /* CH_CFG_USE_MEMPOOLS */ /**************************************************************************** * Exported data. @@ -1002,37 +284,21 @@ static const testcase_t rt_test_010_012 = { * @brief Array of test cases. */ const testcase_t * const rt_test_sequence_010_array[] = { -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_HEAP) || defined(__DOXYGEN__) &rt_test_010_001, #endif -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) +#if (CH_CFG_USE_MEMPOOLS) || defined(__DOXYGEN__) &rt_test_010_002, #endif -#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) - &rt_test_010_003, -#endif - &rt_test_010_004, - &rt_test_010_005, - &rt_test_010_006, -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) - &rt_test_010_007, -#endif - &rt_test_010_008, - &rt_test_010_009, -#if (CH_CFG_USE_SEMAPHORES) || defined(__DOXYGEN__) - &rt_test_010_010, -#endif -#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__) - &rt_test_010_011, -#endif - &rt_test_010_012, NULL }; /** - * @brief Benchmarks. + * @brief Dynamic threads. */ const testsequence_t rt_test_sequence_010 = { - "Benchmarks", + "Dynamic threads", rt_test_sequence_010_array }; + +#endif /* CH_CFG_USE_DYNAMIC */ diff --git a/test/rt/source/test/rt_test_sequence_011.c b/test/rt/source/test/rt_test_sequence_011.c new file mode 100644 index 000000000..768e69720 --- /dev/null +++ b/test/rt/source/test/rt_test_sequence_011.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_011.c + * @brief Test Sequence 011 code. + * + * @page rt_test_sequence_011 [11] Benchmarks + * + * 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. + * + *

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 + * . + */ + +/**************************************************************************** + * 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_011_001 [11.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

+ * - [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. + * . + */ + +static void rt_test_011_001_execute(void) { + uint32_t n; + + /* [11.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); + + /* [11.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); + + /* [11.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_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(); + } + test_end_step(3); + + /* [11.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_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 + } + test_end_step(4); + + /* [11.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); + + /* [11.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); + + /* [11.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); + + /* [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", + NULL, + NULL, + rt_test_011_012_execute +}; + +/**************************************************************************** + * Exported data. + ****************************************************************************/ + +/** + * @brief Array of test cases. + */ +const testcase_t * const rt_test_sequence_011_array[] = { +#if (CH_CFG_USE_MESSAGES) || defined(__DOXYGEN__) + &rt_test_011_001, +#endif +#if (CH_CFG_USE_MESSAGES) || 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. + */ +const testsequence_t rt_test_sequence_011 = { + "Benchmarks", + rt_test_sequence_011_array +}; diff --git a/test/rt/source/test/rt_test_sequence_011.h b/test/rt/source/test/rt_test_sequence_011.h new file mode 100644 index 000000000..ebfcbf759 --- /dev/null +++ b/test/rt/source/test/rt_test_sequence_011.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_011.h + * @brief Test Sequence 011 header. + */ + +#ifndef RT_TEST_SEQUENCE_011_H +#define RT_TEST_SEQUENCE_011_H + +extern const testsequence_t rt_test_sequence_011; + +#endif /* RT_TEST_SEQUENCE_011_H */