diff --git a/demos/STM32/NIL-STM32F303-DISCOVERY/cfg/chconf.h b/demos/STM32/NIL-STM32F303-DISCOVERY/cfg/chconf.h index bd2e50a45..5e49eb114 100644 --- a/demos/STM32/NIL-STM32F303-DISCOVERY/cfg/chconf.h +++ b/demos/STM32/NIL-STM32F303-DISCOVERY/cfg/chconf.h @@ -47,7 +47,7 @@ * @note This values also defines the number of available priorities * (0..CH_CFG_MAX_THREADS-1). */ -#define CH_CFG_MAX_THREADS 4 +#define CH_CFG_MAX_THREADS 5 /** * @brief Auto starts threads when @p chSysInit() is invoked. diff --git a/demos/STM32/NIL-STM32F303-DISCOVERY/main.c b/demos/STM32/NIL-STM32F303-DISCOVERY/main.c index 00f151f19..249daaea5 100644 --- a/demos/STM32/NIL-STM32F303-DISCOVERY/main.c +++ b/demos/STM32/NIL-STM32F303-DISCOVERY/main.c @@ -110,7 +110,6 @@ THD_FUNCTION(Thread3, arg) { THD_TABLE_BEGIN THD_TABLE_THREAD(0, "blinker1", waThread1, Thread1, NULL) THD_TABLE_THREAD(1, "blinker2", waThread2, Thread2, NULL) - THD_TABLE_THREAD(2, "test_support", wa_test_support, test_support, (void *)&nil.threads[3]) THD_TABLE_THREAD(3, "tester", waThread3, Thread3, NULL) THD_TABLE_END diff --git a/os/nil/include/ch.h b/os/nil/include/ch.h index bc2522c4a..a16aec5a0 100644 --- a/os/nil/include/ch.h +++ b/os/nil/include/ch.h @@ -582,7 +582,7 @@ struct nil_system { */ #define THD_TABLE_THREAD(prio, name, wap, funcp, arg) \ {prio, name, \ - wap, ((stkalign_t *)(wap)) + (sizeof (wap) / sizeof(stkalign_t)), \ + wap, THD_WORKING_AREA_END(wap), \ funcp, arg}, /** @@ -665,6 +665,18 @@ struct nil_system { #define THD_WORKING_AREA(s, n) PORT_WORKING_AREA(s, n) /** @} */ +/** + * @brief Returns the top address of a working area. + * @note The parameter is assumed to be an array of @p stkalign_t. The + * macros is invalid for anything else. + * + * @param[in] wa working area array + * + * @api + */ +#define THD_WORKING_AREA_END(wa) \ + ((wa) + ((sizeof wa) / sizeof (stkalign_t))) + /** * @name Threads abstraction macros */ diff --git a/os/nil/include/chevt.h b/os/nil/include/chevt.h index 1a4c817c7..cc7613665 100644 --- a/os/nil/include/chevt.h +++ b/os/nil/include/chevt.h @@ -214,6 +214,70 @@ typedef void (*evhandler_t)(eventid_t id); * @api */ #define chEvtGetEventsX(void) (nil.current->epmask) + +/** + * @brief Waits for exactly one of the specified events. + * @details The function waits for one event among those specified in + * @p events to become pending then the event is cleared and returned. + * @note One and only one event is served in the function, the one with the + * lowest event id. The function is meant to be invoked into a loop + * in order to serve all the pending events.
+ * This means that Event Listeners with a lower event identifier have + * an higher priority. + * + * @param[in] events events that the function should wait + * for, @p ALL_EVENTS enables all the events + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The mask of the lowest event id served and cleared. + * @retval 0 if the operation has timed out. + * + * @api + */ +#define chEvtWaitOne(events) chEvtWaitOneTimeout(events, TIME_INFINITE) + +/** + * @brief Waits for any of the specified events. + * @details The function waits for any event among those specified in + * @p mask to become pending then the events are cleared and + * returned. + * + * @param[in] mask mask of the event flags that the function should wait + * for, @p ALL_EVENTS enables all the events + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The mask of the served and cleared events. + * @retval 0 if the operation has timed out. + * + * @api + */ +#define chEvtWaitAny(events) chEvtWaitAnyTimeout(events, TIME_INFINITE) + +/** + * @brief Waits for all the specified events. + * @details The function waits for all the events specified in @p mask to + * become pending then the events are cleared and returned. + * + * @param[in] mask mask of the event flags that the function should wait + * for, @p ALL_EVENTS enables all the events + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The mask of the served and cleared events. + * @retval 0 if the operation has timed out. + * + * @api + */ +#define chEvtWaitAll(events) chEvtWaitAllTimeout(events, TIME_INFINITE) + /** @} */ /*===========================================================================*/ diff --git a/os/nil/src/ch.c b/os/nil/src/ch.c index 8cf69662e..dd994c8a7 100644 --- a/os/nil/src/ch.c +++ b/os/nil/src/ch.c @@ -749,7 +749,13 @@ msg_t chSchGoSleepTimeoutS(tstate_t newstate, sysinterval_t timeout) { thread_t *chThdCreateI(const thread_config_t *tcp) { thread_t *tp; - chDbgCheck(tcp->prio < CH_CFG_MAX_THREADS); + chDbgCheck((tcp->prio < CH_CFG_MAX_THREADS) && + (tcp->wbase != NULL) && + MEM_IS_ALIGNED(tcp->wbase, PORT_WORKING_AREA_ALIGN) && + (tcp->wend > tcp->wbase) && + MEM_IS_ALIGNED(tcp->wbase, PORT_STACK_ALIGN) && + (tcp->funcp != NULL)); + chDbgCheckClassI(); /* Pointer to the thread slot to be used.*/ @@ -757,7 +763,10 @@ thread_t *chThdCreateI(const thread_config_t *tcp) { chDbgAssert(NIL_THD_IS_WTSTART(tp) || NIL_THD_IS_FINAL(tp), "priority slot taken"); -#if (CH_DBG_ENABLE_STACK_CHECK == TRUE) || defined(__DOXYGEN__) +#if CH_CFG_USE_EVENTS == TRUE + tp->epmask = (eventmask_t)0; +#endif +#if CH_DBG_ENABLE_STACK_CHECK == TRUE tp->wabase = (stkalign_t *)tcp->wbase; #endif diff --git a/os/nil/src/chevt.c b/os/nil/src/chevt.c index b3449158b..31a583a84 100644 --- a/os/nil/src/chevt.c +++ b/os/nil/src/chevt.c @@ -340,6 +340,7 @@ void chEvtDispatch(const evhandler_t *handlers, eventmask_t events) { eid++; } } + /** * @brief Waits for exactly one of the specified events. * @details The function waits for one event among those specified in diff --git a/test/nil/configuration.xml b/test/nil/configuration.xml index 2229458d7..1e6d3d16d 100644 --- a/test/nil/configuration.xml +++ b/test/nil/configuration.xml @@ -39,47 +39,22 @@ + +/* + * Allowed delay in timeout checks. + */ +#define ALLOWED_DELAY TIME_MS2I(2) + +systime_t test_wait_tick(void);]]> - @@ -466,8 +441,35 @@ test_assert_time_window(chTimeAddX(time, 100), + +static thread_t *tp1; +static bool terminate; +static semaphore_t sem1, sem2; + +/* + * Signaler thread. + */ +static THD_WORKING_AREA(wa_signaler, 128); +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); + } +}]]> @@ -545,10 +547,19 @@ test_assert_lock(chSemGetCounterI(&sem1) == 2, "wrong counter value");]]> - + - + @@ -563,10 +574,10 @@ test_assert_lock(chSemGetCounterI(&sem1) == 2, "wrong counter value");]]> - @@ -580,8 +591,8 @@ test_assert(MSG_OK == msg, "wrong returned message");]]> @@ -653,16 +664,36 @@ test_assert(MSG_TIMEOUT == msg, "wrong timeout message");]]> Internal Tests - Suspend/Resume and Event Flags. + Suspend/Resume. - This sequence tests the ChibiOS/NIL functionalities related to threads suspend/resume and event flags. + This sequence tests the ChibiOS/NIL functionalities related to threads suspend/resume. - + @@ -677,10 +708,19 @@ test_assert(MSG_TIMEOUT == msg, "wrong timeout message");]]> - + - + @@ -711,28 +751,78 @@ test_assert(MSG_OK == msg,"wrong returned message");]]> - + + + + + Internal Tests + + + Event Sources and Event Flags. + + + This module implements the test sequence for the Events subsystem. + + + CH_CFG_USE_EVENTS + + + + + - Events Flags functionality. + Events registration. - Event flags functionality is tested. + Two event listeners are registered on an event source and then unregistered in the same order.<br> +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. - CH_CFG_USE_EVENTS + @@ -742,55 +832,522 @@ test_assert(MSG_TIMEOUT == msg, "wrong returned message");]]> - + - A set of event flags are set on the current thread then the function chEvtWaitAnyTimeout() is invoked, the function is supposed to return immediately because the event flags are already pending, after return the events mask is tested. + An Event Source is initialized. - + - The pending event flags mask is cleared then the function chEvtWaitAnyTimeout() is invoked, after return the events mask is tested. The thread is signaled by another thread. + Two Event Listeners are registered on the Event Source, the Event Source is tested to have listeners. - epmask = 0; -events = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(1000)); -test_assert((eventmask_t)0 != events, "timed out"); -test_assert((eventmask_t)0x55 == events, "wrong events mask");]]> + - The function chEvtWaitAnyTimeout() is invoked, no event can wakeup the thread, the function must return because timeout. + An Event Listener is unregistered, the Event Source must still have listeners. - + + + + + + An Event Listener is unregistered, the Event Source must not have listeners. + + + + + + + + + + + + + Event Flags dispatching. + + + The test dispatches three event flags and verifies that the associated event handlers are invoked in LSb-first order. + + + + + + + + + + + + + + + + + + + Three evenf flag bits are raised then chEvtDispatch() is invoked, the sequence of handlers calls is tested. + + + + + + + + + + + + + Events Flags wait using chEvtWaitOne(). + + + Functionality of chEvtWaitOne() is tested under various scenarios. + + + + + + + + + + + + + + + + + + + Setting three event flags. + + + + + + + + + + + Calling chEvtWaitOne() three times, each time a single flag must be returned in order of priority. + + + + + + + + + + + Getting current time and starting a signaler thread, the thread will set an event flag after 50mS. + + + + + + + + + + + Calling chEvtWaitOne() then verifying that the event has been received after 50mS and that the event flags mask has been emptied. + + + + + + + + + + + + + Events Flags wait using chEvtWaitAny(). + + + Functionality of chEvtWaitAny() is tested under various scenarios. + + + + + + + + + + + + + + + + + + + Setting two, non contiguous, event flags. + + + + + + + + + + + Calling chEvtWaitAny() one time, the two flags must be returned. + + + + + + + + + + + Getting current time and starting a signaler thread, the thread will set an event flag after 50mS. + + + + + + + + + + + Calling chEvtWaitAny() then verifying that the event has been received after 50mS and that the event flags mask has been emptied. + + + + + + + + + + + + + Events Flags wait using chEvtWaitAll(). + + + Functionality of chEvtWaitAll() is tested under various scenarios. + + + + + + + + + + + + + + + + + + + Setting two, non contiguous, event flags. + + + + + + + + + + + Calling chEvtWaitAll() one time, the two flags must be returned. + + + + + + + + + + + Setting one event flag. + + + + + + + + + + + Getting current time and starting a signaler thread, the thread will set another event flag after 50mS. + + + + + + + + + + + Calling chEvtWaitAll() then verifying that both event flags have been received after 50mS and that the event flags mask has been emptied. + + + + + + + + + + + + + Events Flags wait timeouts. + + + Timeout functionality is tested for chEvtWaitOneTimeout(), chEvtWaitAnyTimeout() and chEvtWaitAllTimeout(). + + + + + + + + + + + F + + + + + + + + The functions are invoked first with TIME_IMMEDIATE timeout, the timeout condition is tested. + + + + + + + + + + + The functions are invoked first with a 50mS timeout, the timeout condition is tested. + + + + + + + + + + + + + Broadcasting using chEvtBroadcast(). + + + Functionality of chEvtBroadcast() is tested. + + + + + + + + + + + + + + + + + + + Registering on two event sources associating them with flags 1 and 4. + + + + + + + + + + + Getting current time and starting a broadcaster thread, the thread broadcast the first Event Source immediately and the other after 50mS. + + + + + + + + + + + Calling chEvtWaitAll() then verifying that both event flags have been received after 50mS and that the event flags mask has been emptied. + + + + + + + + + + + Unregistering from the Event Sources. + + + + + + diff --git a/test/nil/nil_test.mk b/test/nil/nil_test.mk index c49541d73..70659cf7a 100644 --- a/test/nil/nil_test.mk +++ b/test/nil/nil_test.mk @@ -3,7 +3,8 @@ TESTSRC += ${CHIBIOS}/test/nil/source/test/nil_test_root.c \ ${CHIBIOS}/test/nil/source/test/nil_test_sequence_001.c \ ${CHIBIOS}/test/nil/source/test/nil_test_sequence_002.c \ ${CHIBIOS}/test/nil/source/test/nil_test_sequence_003.c \ - ${CHIBIOS}/test/nil/source/test/nil_test_sequence_004.c + ${CHIBIOS}/test/nil/source/test/nil_test_sequence_004.c \ + ${CHIBIOS}/test/nil/source/test/nil_test_sequence_005.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 b996df583..e960aea0c 100644 --- a/test/nil/source/test/nil_test_root.c +++ b/test/nil/source/test/nil_test_root.c @@ -25,6 +25,7 @@ * - @subpage nil_test_sequence_002 * - @subpage nil_test_sequence_003 * - @subpage nil_test_sequence_004 + * - @subpage nil_test_sequence_005 * . */ @@ -52,6 +53,9 @@ const testsequence_t * const nil_test_suite_array[] = { &nil_test_sequence_003, #endif &nil_test_sequence_004, +#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) + &nil_test_sequence_005, +#endif NULL }; @@ -67,38 +71,13 @@ const testsuite_t nil_test_suite = { /* Shared code. */ /*===========================================================================*/ -semaphore_t gsem1, gsem2; -thread_reference_t gtr1; - /* - * Support thread. + * Delays execution until next system time tick. */ -THD_WORKING_AREA(wa_test_support, 128); -THD_FUNCTION(test_support, arg) { -#if CH_CFG_USE_EVENTS == TRUE - thread_t *tp = (thread_t *)arg; -#else - (void)arg; -#endif +systime_t test_wait_tick(void) { - /* Initializing global resources.*/ - chSemObjectInit(&gsem1, 0); - chSemObjectInit(&gsem2, 0); - - while (true) { - chSysLock(); - if (chSemGetCounterI(&gsem1) < 0) - chSemSignalI(&gsem1); - chSemResetI(&gsem2, 0); - chThdResumeI(>r1, MSG_OK); -#if CH_CFG_USE_EVENTS == TRUE - chEvtSignalI(tp, 0x55); -#endif - chSchRescheduleS(); - chSysUnlock(); - - chThdSleepMilliseconds(250); - } + chThdSleep(1); + return chVTGetSystemTimeX(); } #endif /* !defined(__DOXYGEN__) */ diff --git a/test/nil/source/test/nil_test_root.h b/test/nil/source/test/nil_test_root.h index 95c04bed7..93b51b951 100644 --- a/test/nil/source/test/nil_test_root.h +++ b/test/nil/source/test/nil_test_root.h @@ -28,6 +28,7 @@ #include "nil_test_sequence_002.h" #include "nil_test_sequence_003.h" #include "nil_test_sequence_004.h" +#include "nil_test_sequence_005.h" #if !defined(__DOXYGEN__) @@ -50,12 +51,12 @@ extern "C" { #define TEST_SUITE_NAME "ChibiOS/NIL Test Suite" -extern semaphore_t gsem1, gsem2; -extern thread_reference_t gtr1; -extern THD_WORKING_AREA(wa_test_support, 128); +/* + * Allowed delay in timeout checks. + */ +#define ALLOWED_DELAY TIME_MS2I(2) -void test_print_port_info(void); -THD_FUNCTION(test_support, arg); +systime_t test_wait_tick(void); #endif /* !defined(__DOXYGEN__) */ diff --git a/test/nil/source/test/nil_test_sequence_003.c b/test/nil/source/test/nil_test_sequence_003.c index dc92f01b3..9f8121cc3 100644 --- a/test/nil/source/test/nil_test_sequence_003.c +++ b/test/nil/source/test/nil_test_sequence_003.c @@ -50,7 +50,34 @@ #include "ch.h" -static semaphore_t sem1; +static thread_t *tp1; +static bool terminate; +static semaphore_t sem1, sem2; + +/* + * Signaler thread. + */ +static THD_WORKING_AREA(wa_signaler, 128); +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. @@ -136,11 +163,20 @@ static const testcase_t nil_test_003_001 = { */ static void nil_test_003_002_setup(void) { - chSemObjectInit(&gsem1, 0); + thread_config_t tc = { + chThdGetPriorityX() - 1, + "signaler", + wa_signaler, + THD_WORKING_AREA_END(wa_signaler), + signaler, + NULL + }; + tp1 = chThdCreate(&tc); } static void nil_test_003_002_teardown(void) { - chSemReset(&gsem1, 0); + terminate = true; + chThdWait(tp1); } static void nil_test_003_002_execute(void) { @@ -152,8 +188,8 @@ static void nil_test_003_002_execute(void) { { msg_t msg; - msg = chSemWait(&gsem1); - test_assert_lock(chSemGetCounterI(&gsem1) == 0, "wrong counter value"); + msg = chSemWait(&sem1); + test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value"); test_assert(MSG_OK == msg, "wrong returned message"); } @@ -164,8 +200,8 @@ static void nil_test_003_002_execute(void) { { msg_t msg; - msg = chSemWait(&gsem2); - test_assert_lock(chSemGetCounterI(&gsem2) == 0,"wrong counter value"); + msg = chSemWait(&sem2); + test_assert_lock(chSemGetCounterI(&sem2) == 0,"wrong counter value"); test_assert(MSG_RESET == msg, "wrong returned message"); } } diff --git a/test/nil/source/test/nil_test_sequence_004.c b/test/nil/source/test/nil_test_sequence_004.c index 77c7d1f01..423eaa407 100644 --- a/test/nil/source/test/nil_test_sequence_004.c +++ b/test/nil/source/test/nil_test_sequence_004.c @@ -21,17 +21,16 @@ * @file nil_test_sequence_004.c * @brief Test Sequence 004 code. * - * @page nil_test_sequence_004 [4] Suspend/Resume and Event Flags + * @page nil_test_sequence_004 [4] Suspend/Resume * * File: @ref nil_test_sequence_004.c * *

Description

* This sequence tests the ChibiOS/NIL functionalities related to - * threads suspend/resume and event flags. + * threads suspend/resume. * *

Test Cases

* - @subpage nil_test_004_001 - * - @subpage nil_test_004_002 * . */ @@ -39,8 +38,28 @@ * Shared code. ****************************************************************************/ +static thread_t *tp1; +static bool terminate; static thread_reference_t tr1; +/* + * Resumer thread. + */ +static THD_WORKING_AREA(wa_resumer, 128); +static THD_FUNCTION(resumer, arg) { + + (void)arg; + + /* Initializing global resources.*/ + terminate = false; + tr1 = NULL; + + while (!terminate) { + chThdResume(&tr1, MSG_OK); + chThdSleepMilliseconds(250); + } +} + /**************************************************************************** * Test cases. ****************************************************************************/ @@ -63,7 +82,20 @@ static thread_reference_t tr1; */ static void nil_test_004_001_setup(void) { - tr1 = NULL; + thread_config_t tc = { + chThdGetPriorityX() - 1, + "resumer", + wa_resumer, + THD_WORKING_AREA_END(wa_resumer), + resumer, + NULL + }; + tp1 = chThdCreate(&tc); +} + +static void nil_test_004_001_teardown(void) { + terminate = true; + chThdWait(tp1); } static void nil_test_004_001_execute(void) { @@ -76,9 +108,9 @@ static void nil_test_004_001_execute(void) { test_set_step(1); { chSysLock(); - msg = chThdSuspendTimeoutS(>r1, TIME_INFINITE); + msg = chThdSuspendTimeoutS(&tr1, TIME_INFINITE); chSysUnlock(); - test_assert(NULL == gtr1, "not NULL"); + test_assert(NULL == tr1, "not NULL"); test_assert(MSG_OK == msg,"wrong returned message"); } @@ -87,14 +119,16 @@ static void nil_test_004_001_execute(void) { the state of the reference are tested.*/ test_set_step(2); { + thread_reference_t tr = NULL; + chSysLock(); time = chVTGetSystemTimeX(); - msg = chThdSuspendTimeoutS(&tr1, TIME_MS2I(1000)); + 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 == tr1, "not NULL"); + test_assert(NULL == tr, "not NULL"); test_assert(MSG_TIMEOUT == msg, "wrong returned message"); } } @@ -102,86 +136,10 @@ static void nil_test_004_001_execute(void) { static const testcase_t nil_test_004_001 = { "Suspend and Resume functionality", nil_test_004_001_setup, - NULL, + nil_test_004_001_teardown, nil_test_004_001_execute }; -#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) -/** - * @page nil_test_004_002 [4.2] Events Flags functionality - * - *

Description

- * Event flags functionality is tested. - * - *

Conditions

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

Test Steps

- * - [4.2.1] A set of event flags are set on the current thread then - * the function chEvtWaitAnyTimeout() is invoked, the function is - * supposed to return immediately because the event flags are already - * pending, after return the events mask is tested. - * - [4.2.2] The pending event flags mask is cleared then the function - * chEvtWaitAnyTimeout() is invoked, after return the events mask is - * tested. The thread is signaled by another thread. - * - [4.2.3] The function chEvtWaitAnyTimeout() is invoked, no event - * can wakeup the thread, the function must return because timeout. - * . - */ - -static void nil_test_004_002_execute(void) { - systime_t time; - eventmask_t events; - - /* [4.2.1] A set of event flags are set on the current thread then - the function chEvtWaitAnyTimeout() is invoked, the function is - supposed to return immediately because the event flags are already - pending, after return the events mask is tested.*/ - test_set_step(1); - { - time = chVTGetSystemTimeX(); - chEvtSignal(chThdGetSelfX(), 0x55); - events = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(1000)); - test_assert((eventmask_t)0 != events, "timed out"); - test_assert((eventmask_t)0x55 == events, "wrong events mask"); - } - - /* [4.2.2] The pending event flags mask is cleared then the function - chEvtWaitAnyTimeout() is invoked, after return the events mask is - tested. The thread is signaled by another thread.*/ - test_set_step(2); - { - time = chVTGetSystemTimeX(); - chThdGetSelfX()->epmask = 0; - events = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(1000)); - test_assert((eventmask_t)0 != events, "timed out"); - test_assert((eventmask_t)0x55 == events, "wrong events mask"); - } - - /* [4.2.3] The function chEvtWaitAnyTimeout() is invoked, no event - can wakeup the thread, the function must return because timeout.*/ - test_set_step(3); - { - time = chVTGetSystemTimeX(); - events = chEvtWaitAnyTimeout(0, TIME_MS2I(1000)); - test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)), - chTimeAddX(time, TIME_MS2I(1000) + 1), - "out of time window"); - test_assert((eventmask_t)0 == events, "wrong events mask"); - } -} - -static const testcase_t nil_test_004_002 = { - "Events Flags functionality", - NULL, - NULL, - nil_test_004_002_execute -}; -#endif /* CH_CFG_USE_EVENTS */ - /**************************************************************************** * Exported data. ****************************************************************************/ @@ -191,16 +149,13 @@ static const testcase_t nil_test_004_002 = { */ const testcase_t * const nil_test_sequence_004_array[] = { &nil_test_004_001, -#if (CH_CFG_USE_EVENTS) || defined(__DOXYGEN__) - &nil_test_004_002, -#endif NULL }; /** - * @brief Suspend/Resume and Event Flags. + * @brief Suspend/Resume. */ const testsequence_t nil_test_sequence_004 = { - "Suspend/Resume and Event Flags", + "Suspend/Resume", nil_test_sequence_004_array }; diff --git a/test/nil/source/test/nil_test_sequence_005.c b/test/nil/source/test/nil_test_sequence_005.c new file mode 100644 index 000000000..9e7eea5f3 --- /dev/null +++ b/test/nil/source/test/nil_test_sequence_005.c @@ -0,0 +1,610 @@ +/* + 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_005.c + * @brief Test Sequence 005 code. + * + * @page nil_test_sequence_005 [5] Event Sources and Event Flags + * + * 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 + * . + * + *

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}; + +/* + * Direct events thread. + */ +static THD_WORKING_AREA(wa_evtthd1, 128); +static THD_FUNCTION(evtthd1, p) { + + chThdSleepMilliseconds(50); + chEvtSignal((thread_t *)p, 1); +} + +/* + * Broadcaster thread. + */ +static THD_WORKING_AREA(wa_evtthd2, 128); +static THD_FUNCTION(evtthd2, p) { + + (void)p; + chEvtBroadcast(&es1); + chThdSleepMilliseconds(50); + chEvtBroadcast(&es2); +} + +/**************************************************************************** + * Test cases. + ****************************************************************************/ + +/** + * @page nil_test_005_001 [5.1] Events registration + * + *

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

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. + * . + */ + +static void nil_test_005_001_execute(void) { + event_listener_t el1, el2; + + /* [5.1.1] An Event Source is initialized.*/ + test_set_step(1); + { + chEvtObjectInit(&es1); + } + + /* [5.1.2] Two Event Listeners are registered on the Event Source, + the Event Source is tested to have listeners.*/ + test_set_step(2); + { + chEvtRegisterMask(&es1, &el1, 1); + chEvtRegisterMask(&es1, &el2, 2); + test_assert_lock(chEvtIsListeningI(&es1), "no listener"); + } + + /* [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"); + } + + /* [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"); + } +} + +static const testcase_t nil_test_005_001 = { + "Events registration", + NULL, + NULL, + 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"); + } +} + +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); + } + + /* [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"); + } + + /* [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_config_t tc = { + chThdGetPriorityX() + 1, + "event1", + wa_evtthd1, + THD_WORKING_AREA_END(wa_evtthd1), + evtthd1, + chThdGetSelfX() + }; + tp = chThdCreate(&tc); + } + + /* [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); + } +} + +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); + } + + /* [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"); + } + + /* [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_config_t tc = { + chThdGetPriorityX() + 1, + "event1", + wa_evtthd1, + THD_WORKING_AREA_END(wa_evtthd1), + evtthd1, + chThdGetSelfX() + }; + tp = chThdCreate(&tc); + } + + /* [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); + } +} + +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); + } + + /* [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"); + } + + /* [5.5.3] Setting one event flag.*/ + test_set_step(3); + { + chEvtAddEvents(4); + } + + /* [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_config_t tc = { + chThdGetPriorityX() + 1, + "event1", + wa_evtthd1, + THD_WORKING_AREA_END(wa_evtthd1), + evtthd1, + chThdGetSelfX() + }; + tp = chThdCreate(&tc); + } + + /* [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); + } +} + +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"); + } + + /* [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"); + } +} + +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); + } + + /* [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_config_t tc = { + chThdGetPriorityX() + 1, + "event2", + wa_evtthd2, + THD_WORKING_AREA_END(wa_evtthd2), + evtthd2, + NULL + }; + tp = chThdCreate(&tc); + } + + /* [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); + } + + /* [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"); + } +} + +static const testcase_t nil_test_005_007 = { + "Broadcasting using chEvtBroadcast()", + nil_test_005_007_setup, + NULL, + nil_test_005_007_execute +}; + +/**************************************************************************** + * Exported data. + ****************************************************************************/ + +/** + * @brief Array of test cases. + */ +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. + */ +const testsequence_t nil_test_sequence_005 = { + "Event Sources and Event Flags", + nil_test_sequence_005_array +}; + +#endif /* CH_CFG_USE_EVENTS */ diff --git a/test/nil/source/test/nil_test_sequence_005.h b/test/nil/source/test/nil_test_sequence_005.h new file mode 100644 index 000000000..317de09cd --- /dev/null +++ b/test/nil/source/test/nil_test_sequence_005.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_005.h + * @brief Test Sequence 005 header. + */ + +#ifndef NIL_TEST_SEQUENCE_005_H +#define NIL_TEST_SEQUENCE_005_H + +extern const testsequence_t nil_test_sequence_005; + +#endif /* NIL_TEST_SEQUENCE_005_H */