aboutsummaryrefslogtreecommitdiffstats
path: root/test/rt/source/test/rt_test_sequence_006.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/rt/source/test/rt_test_sequence_006.c')
-rw-r--r--test/rt/source/test/rt_test_sequence_006.c1069
1 files changed, 1069 insertions, 0 deletions
diff --git a/test/rt/source/test/rt_test_sequence_006.c b/test/rt/source/test/rt_test_sequence_006.c
new file mode 100644
index 000000000..7c6847b51
--- /dev/null
+++ b/test/rt/source/test/rt_test_sequence_006.c
@@ -0,0 +1,1069 @@
+/*
+ ChibiOS - Copyright (C) 2006..2017 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 "ch_test.h"
+#include "rt_test_root.h"
+
+/**
+ * @file rt_test_sequence_006.c
+ * @brief Test Sequence 006 code.
+ *
+ * @page rt_test_sequence_006 [6] Mutexes, Condition Variables and Priority Inheritance
+ *
+ * File: @ref rt_test_sequence_006.c
+ *
+ * <h2>Description</h2>
+ * This sequence tests the ChibiOS/RT functionalities related to
+ * mutexes, condition variables and priority inheritance algorithm.
+ *
+ * <h2>Conditions</h2>
+ * This sequence is only executed if the following preprocessor condition
+ * evaluates to true:
+ * - CH_CFG_USE_MUTEXES
+ * .
+ *
+ * <h2>Test Cases</h2>
+ * - @subpage rt_test_006_001
+ * - @subpage rt_test_006_002
+ * - @subpage rt_test_006_003
+ * - @subpage rt_test_006_004
+ * - @subpage rt_test_006_005
+ * - @subpage rt_test_006_006
+ * - @subpage rt_test_006_007
+ * - @subpage rt_test_006_008
+ * - @subpage rt_test_006_009
+ * .
+ */
+
+#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__)
+
+/****************************************************************************
+ * Shared code.
+ ****************************************************************************/
+
+static MUTEX_DECL(m1);
+static MUTEX_DECL(m2);
+#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__)
+static CONDVAR_DECL(c1);
+#endif
+
+#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 = start + TIME_MS2I(duration);
+ do {
+ now = chThdGetTicksX(chThdGetSelfX());
+#if defined(SIMULATOR)
+ _sim_check_for_interrupts();
+#endif
+ }
+ while (chVTIsTimeWithinX(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_006_001 [6.1] Priority enqueuing test
+ *
+ * <h2>Description</h2>
+ * 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.
+ *
+ * <h2>Test Steps</h2>
+ * - [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.
+ * .
+ */
+
+static void rt_test_006_001_setup(void) {
+ chMtxObjectInit(&m1);
+}
+
+static void rt_test_006_001_execute(void) {
+ tprio_t prio;
+
+ /* [6.1.1] Getting the initial priority.*/
+ test_set_step(1);
+ {
+ prio = chThdGetPriorityX();
+ }
+
+ /* [6.1.2] Locking the mutex.*/
+ test_set_step(2);
+ {
+ chMtxLock(&m1);
+ }
+
+ /* [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.*/
+ 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");
+ }
+}
+
+static const testcase_t rt_test_006_001 = {
+ "Priority enqueuing test",
+ rt_test_006_001_setup,
+ NULL,
+ rt_test_006_001_execute
+};
+
+#if (CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__)
+/**
+ * @page rt_test_006_002 [6.2] Priority inheritance, simple case
+ *
+ * <h2>Description</h2>
+ * 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.
+ *
+ * <h2>Conditions</h2>
+ * This test is only executed if the following preprocessor condition
+ * evaluates to true:
+ * - CH_DBG_THREADS_PROFILING
+ * .
+ *
+ * <h2>Test Steps</h2>
+ * - [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).
+ * .
+ */
+
+static void rt_test_006_002_setup(void) {
+ chMtxObjectInit(&m1);
+}
+
+static void rt_test_006_002_execute(void) {
+ systime_t time;
+
+ /* [6.2.1] Getting the system time for test duration measurement.*/
+ test_set_step(1);
+ {
+ time = test_wait_tick();
+ }
+
+ /* [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.*/
+ 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");
+ }
+
+ /* [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(time + TIME_MS2I(100), time + TIME_MS2I(100) + ALLOWED_DELAY,
+ "out of time window");
+ }
+}
+
+static const testcase_t rt_test_006_002 = {
+ "Priority inheritance, simple case",
+ 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
+ *
+ * <h2>Description</h2>
+ * 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.
+ *
+ * <h2>Conditions</h2>
+ * This test is only executed if the following preprocessor condition
+ * evaluates to true:
+ * - CH_DBG_THREADS_PROFILING
+ * .
+ *
+ * <h2>Test Steps</h2>
+ * - [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).
+ * .
+ */
+
+static void rt_test_006_003_setup(void) {
+ chMtxObjectInit(&m1); /* Mutex B.*/
+ chMtxObjectInit(&m2); /* Mutex A.*/
+}
+
+static void rt_test_006_003_execute(void) {
+ systime_t time;
+
+ /* [6.3.1] Getting the system time for test duration measurement.*/
+ test_set_step(1);
+ {
+ time = test_wait_tick();
+ }
+
+ /* [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.*/
+ 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");
+ }
+
+ /* [6.3.3] Testing that all threads completed within the specified
+ time windows (110mS...110mS+ALLOWED_DELAY).*/
+ test_set_step(3);
+ {
+ test_assert_time_window(time + TIME_MS2I(110), time + TIME_MS2I(110) + ALLOWED_DELAY,
+ "out of time window");
+ }
+}
+
+static const testcase_t rt_test_006_003 = {
+ "Priority inheritance, complex case",
+ 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
+ *
+ * <h2>Description</h2>
+ * 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.<br> Thread A performs
+ * wait(50), lock(m1), unlock(m1), exit. Thread B performs wait(150),
+ * lock(m2), unlock(m2), exit.
+ *
+ * <h2>Test Steps</h2>
+ * - [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).
+ * .
+ */
+
+static void rt_test_006_004_setup(void) {
+ chMtxObjectInit(&m1);
+ chMtxObjectInit(&m2);
+}
+
+static void rt_test_006_004_teardown(void) {
+ test_wait_threads();
+}
+
+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.*/
+ test_set_step(1);
+ {
+ p = chThdGetPriorityX();
+ pa = p + 1;
+ pb = p + 2;
+ }
+
+ /* [6.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");
+ }
+
+ /* [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");
+ }
+}
+
+static const testcase_t rt_test_006_004 = {
+ "Priority return verification",
+ rt_test_006_004_setup,
+ rt_test_006_004_teardown,
+ 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
+ *
+ * <h2>Description</h2>
+ * The behavior of multiple mutex locks from the same thread is tested
+ * when recursion is disabled.
+ *
+ * <h2>Conditions</h2>
+ * This test is only executed if the following preprocessor condition
+ * evaluates to true:
+ * - !CH_CFG_USE_MUTEXES_RECURSIVE
+ * .
+ *
+ * <h2>Test Steps</h2>
+ * - [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.
+ * .
+ */
+
+static void rt_test_006_005_setup(void) {
+ chMtxObjectInit(&m1);
+}
+
+static void rt_test_006_005_execute(void) {
+ bool b;
+ tprio_t prio;
+
+ /* [6.5.1] Getting current thread priority for later checks.*/
+ test_set_step(1);
+ {
+ prio = chThdGetPriorityX();
+ }
+
+ /* [6.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");
+ }
+
+ /* [6.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");
+ }
+
+ /* [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");
+ }
+}
+
+static const testcase_t rt_test_006_005 = {
+ "Repeated locks, non recursive scenario",
+ rt_test_006_005_setup,
+ NULL,
+ 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
+ *
+ * <h2>Description</h2>
+ * The behavior of multiple mutex locks from the same thread is tested
+ * when recursion is enabled.
+ *
+ * <h2>Conditions</h2>
+ * This test is only executed if the following preprocessor condition
+ * evaluates to true:
+ * - CH_CFG_USE_MUTEXES_RECURSIVE
+ * .
+ *
+ * <h2>Test Steps</h2>
+ * - [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.
+ * .
+ */
+
+static void rt_test_006_006_setup(void) {
+ chMtxObjectInit(&m1);
+}
+
+static void rt_test_006_006_execute(void) {
+ bool b;
+ tprio_t prio;
+
+ /* [6.6.1] Getting current thread priority for later checks.*/
+ test_set_step(1);
+ {
+ prio = chThdGetPriorityX();
+ }
+
+ /* [6.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");
+ }
+
+ /* [6.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");
+ }
+
+ /* [6.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");
+ }
+
+ /* [6.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");
+ }
+
+ /* [6.6.6] Testing that priority has not changed after operations.*/
+ 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");
+ }
+}
+
+static const testcase_t rt_test_006_006 = {
+ "Repeated locks using, recursive scenario",
+ rt_test_006_006_setup,
+ NULL,
+ 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
+ *
+ * <h2>Description</h2>
+ * 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.<br> The test expects the threads to
+ * reach their goal in increasing priority order regardless of the
+ * initial order.
+ *
+ * <h2>Conditions</h2>
+ * This test is only executed if the following preprocessor condition
+ * evaluates to true:
+ * - CH_CFG_USE_CONDVARS
+ * .
+ *
+ * <h2>Test Steps</h2>
+ * - [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
+ *
+ * <h2>Description</h2>
+ * Five threads take a mutex and then enter a conditional variable
+ * queue, the tester thread then proceeds to broadcast the conditional
+ * variable.<br> The test expects the threads to reach their goal in
+ * increasing priority order regardless of the initial order.
+ *
+ * <h2>Conditions</h2>
+ * This test is only executed if the following preprocessor condition
+ * evaluates to true:
+ * - CH_CFG_USE_CONDVARS
+ * .
+ *
+ * <h2>Test Steps</h2>
+ * - [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
+ *
+ * <h2>Description</h2>
+ * 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)}.
+ *
+ * <h2>Conditions</h2>
+ * This test is only executed if the following preprocessor condition
+ * evaluates to true:
+ * - CH_CFG_USE_CONDVARS
+ * .
+ *
+ * <h2>Test Steps</h2>
+ * - [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.
+ ****************************************************************************/
+
+/**
+ * @brief Mutexes, Condition Variables and Priority Inheritance.
+ */
+const testcase_t * const rt_test_sequence_006[] = {
+ &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
+};
+
+#endif /* CH_CFG_USE_MUTEXES */