aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorGiovanni Di Sirio <gdisirio@gmail.com>2016-03-29 15:33:45 +0000
committerGiovanni Di Sirio <gdisirio@gmail.com>2016-03-29 15:33:45 +0000
commitda68278385b02505d32cbaa9b623d689ed68cbcf (patch)
treee79d864e0d06c5e87c3eba1069bfa5ff2a0126b1 /test
parentea61c2791c10c1eb40382cc813e1c5468b02afe6 (diff)
downloadChibiOS-da68278385b02505d32cbaa9b623d689ed68cbcf.tar.gz
ChibiOS-da68278385b02505d32cbaa9b623d689ed68cbcf.tar.bz2
ChibiOS-da68278385b02505d32cbaa9b623d689ed68cbcf.zip
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@9180 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'test')
-rw-r--r--test/rt/configuration.xml515
-rw-r--r--test/rt/source/test/test_sequence_005.c480
2 files changed, 978 insertions, 17 deletions
diff --git a/test/rt/configuration.xml b/test/rt/configuration.xml
index bd6bd6525..32a6bf141 100644
--- a/test/rt/configuration.xml
+++ b/test/rt/configuration.xml
@@ -1205,7 +1205,7 @@ test_assert(sem1.cnt == 0, "counter not zero");]]></value>
<value />
</teardown_code>
<local_variables>
- <value><![CDATA[binary_semaphore_t bsem;
+ <value><![CDATA[binary_semaphore_t bsem;
msg_t msg;]]></value>
</local_variables>
</various_code>
@@ -1218,7 +1218,7 @@ msg_t msg;]]></value>
<value />
</tags>
<code>
- <value><![CDATA[chBSemObjectInit(&bsem, true);
+ <value><![CDATA[chBSemObjectInit(&bsem, true);
test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken");]]></value>
</code>
</step>
@@ -1242,7 +1242,7 @@ test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken");]]></value>
<value />
</tags>
<code>
- <value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE,
+ <value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE,
chThdGetPriorityX()-1, thread4, &bsem);]]></value>
</code>
</step>
@@ -1254,8 +1254,8 @@ test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken");]]></value>
<value />
</tags>
<code>
- <value><![CDATA[msg = chBSemWait(&bsem);
-test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken");
+ <value><![CDATA[msg = chBSemWait(&bsem);
+test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken");
test_assert(msg == MSG_OK, "unexpected message");]]></value>
</code>
</step>
@@ -1267,8 +1267,8 @@ test_assert(msg == MSG_OK, "unexpected message");]]></value>
<value />
</tags>
<code>
- <value><![CDATA[chBSemSignal(&bsem);
-test_assert_lock(chBSemGetStateI(&bsem) ==false, "still taken");
+ <value><![CDATA[chBSemSignal(&bsem);
+test_assert_lock(chBSemGetStateI(&bsem) ==false, "still taken");
test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter");]]></value>
</code>
</step>
@@ -1280,8 +1280,8 @@ test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter");]]></va
<value />
</tags>
<code>
- <value><![CDATA[chBSemSignal(&bsem);
-test_assert_lock(chBSemGetStateI(&bsem) == false, "taken");
+ <value><![CDATA[chBSemSignal(&bsem);
+test_assert_lock(chBSemGetStateI(&bsem) == false, "taken");
test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter");]]></value>
</code>
</step>
@@ -1303,13 +1303,498 @@ test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter");]]></va
<value>CH_CFG_USE_MUTEXES</value>
</condition>
<shared_code>
- <value><![CDATA[static MUTEX_DECL(m1);
-static MUTEX_DECL(m2);
-#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__)
-static CONDVAR_DECL(c1);
-#endif]]></value>
+ <value><![CDATA[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 + MS2ST(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);
+}
+
+/* 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');
+}
+
+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);
+}
+#endif /* CH_CFG_USE_CONDVARS */]]></value>
</shared_code>
- <cases />
+ <cases>
+ <case>
+ <brief>
+ <value>Priority enqueuing test.</value>
+ </brief>
+ <description>
+ <value>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.</value>
+ </description>
+ <condition>
+ <value />
+ </condition>
+ <various_code>
+ <setup_code>
+ <value><![CDATA[chMtxObjectInit(&m1);]]></value>
+ </setup_code>
+ <teardown_code>
+ <value />
+ </teardown_code>
+ <local_variables>
+ <value><![CDATA[tprio_t prio;]]></value>
+ </local_variables>
+ </various_code>
+ <steps>
+ <step>
+ <description>
+ <value>Getting the initial priority.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[prio = chThdGetPriorityX();]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>Locking the mutex.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[chMtxLock(&m1);]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>Five threads are created that try to lock and unlock the mutex then terminate. The threads are created in ascending priority order.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[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");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>Unlocking the mutex, the threads will wakeup in priority order because the mutext queue is an ordered one.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[chMtxUnlock(&m1);
+test_wait_threads();
+test_assert(prio == chThdGetPriorityX(), "wrong priority level");
+test_assert_sequence("ABCDE", "invalid sequence");]]></value>
+ </code>
+ </step>
+ </steps>
+ </case>
+ <case>
+ <brief>
+ <value>Priority inheritance, simple case.</value>
+ </brief>
+ <description>
+ <value>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.</value>
+ </description>
+ <condition>
+ <value />
+ </condition>
+ <various_code>
+ <setup_code>
+ <value><![CDATA[chMtxObjectInit(&m1);]]></value>
+ </setup_code>
+ <teardown_code>
+ <value />
+ </teardown_code>
+ <local_variables>
+ <value><![CDATA[systime_t time;]]></value>
+ </local_variables>
+ </various_code>
+ <steps>
+ <step>
+ <description>
+ <value>Getting the system time for test duration measurement.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[time = test_wait_tick();]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>The three contenders threads are created and let run atomically, the goals sequence is tested, the threads must complete in priority order.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[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");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>Testing that all threads completed within the specified time windows (100mS...100mS+ALLOWED_DELAY).</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[test_assert_time_window(time + MS2ST(100), time + MS2ST(100) + ALLOWED_DELAY,
+ "out of time window");]]></value>
+ </code>
+ </step>
+ </steps>
+ </case>
+ <case>
+ <brief>
+ <value>Priority inheritance, complex case.</value>
+ </brief>
+ <description>
+ <value>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.</value>
+ </description>
+ <condition>
+ <value />
+ </condition>
+ <various_code>
+ <setup_code>
+ <value><![CDATA[chMtxObjectInit(&m1); /* Mutex B.*/
+chMtxObjectInit(&m2); /* Mutex A.*/]]></value>
+ </setup_code>
+ <teardown_code>
+ <value />
+ </teardown_code>
+ <local_variables>
+ <value><![CDATA[systime_t time;]]></value>
+ </local_variables>
+ </various_code>
+ <steps>
+ <step>
+ <description>
+ <value>Getting the system time for test duration measurement.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[time = test_wait_tick();]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>The five contenders threads are created and let run atomically, the goals sequence is tested, the threads must complete in priority order.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[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");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>Testing that all threads completed within the specified time windows (110mS...110mS+ALLOWED_DELAY).</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[test_assert_time_window(time + MS2ST(110), time + MS2ST(110) + ALLOWED_DELAY,
+ "out of time window");]]></value>
+ </code>
+ </step>
+ </steps>
+ </case>
+ <case>
+ <brief>
+ <value>Priority return verification.</value>
+ </brief>
+ <description>
+ <value>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.&lt;br&gt;&#xD;
+Thread A performs wait(50), lock(m1), unlock(m1), exit. Thread B performs wait(150), lock(m2), unlock(m2), exit.</value>
+ </description>
+ <condition>
+ <value />
+ </condition>
+ <various_code>
+ <setup_code>
+ <value><![CDATA[chMtxObjectInit(&m1);
+chMtxObjectInit(&m2);]]></value>
+ </setup_code>
+ <teardown_code>
+ <value />
+ </teardown_code>
+ <local_variables>
+ <value><![CDATA[tprio_t p, pa, pb;]]></value>
+ </local_variables>
+ </various_code>
+ <steps>
+ <step>
+ <description>
+ <value>Getting current thread priority P(0) and assigning to the threads A and B priorities +1 and +2.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[p = chThdGetPriorityX();
+pa = p + 1;
+pb = p + 2;]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>Spawning threads A and B at priorities P(A) and P(B).</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, pa, thread4A, "A");
+threads[1] = chThdCreateStatic(wa[1], WA_SIZE, pb, thread4B, "B");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>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.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[chMtxLock(&m1);
+test_assert(chThdGetPriorityX() == p, "wrong priority level");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>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.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[chThdSleepMilliseconds(100);
+test_assert(chThdGetPriorityX() == pa, "wrong priority level");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>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.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[chMtxLock(&m2);
+test_assert(chThdGetPriorityX() == pa, "wrong priority level");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>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.</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[chThdSleepMilliseconds(100);
+test_assert(chThdGetPriorityX() == pb, "wrong priority level");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>Unlocking M2, the priority should fall back to P(A).</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[chMtxUnlock(&m2);
+test_assert(chThdGetPriorityX() == pa, "wrong priority level");]]></value>
+ </code>
+ </step>
+ <step>
+ <description>
+ <value>Unlocking M1, the priority should fall back to P(0).</value>
+ </description>
+ <tags>
+ <value />
+ </tags>
+ <code>
+ <value><![CDATA[chMtxUnlock(&m1);
+test_assert(chThdGetPriorityX() == p, "wrong priority level");]]></value>
+ </code>
+ </step>
+ </steps>
+ </case>
+ </cases>
</sequence>
<sequence>
<type index="0">
diff --git a/test/rt/source/test/test_sequence_005.c b/test/rt/source/test/test_sequence_005.c
index da845e148..ad953d3af 100644
--- a/test/rt/source/test/test_sequence_005.c
+++ b/test/rt/source/test/test_sequence_005.c
@@ -34,7 +34,11 @@
* .
*
* <h2>Test Cases</h2>
- * No test cases defined in the test sequence.
+ * - @subpage test_005_001
+ * - @subpage test_005_002
+ * - @subpage test_005_003
+ * - @subpage test_005_004
+ * .
*/
#if (CH_CFG_USE_MUTEXES) || defined(__DOXYGEN__)
@@ -47,12 +51,480 @@ static MUTEX_DECL(m1);
static MUTEX_DECL(m2);
#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__)
static CONDVAR_DECL(c1);
-#endif
+#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 + MS2ST(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);
+}
+
+/* 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');
+}
+
+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);
+}
+#endif /* CH_CFG_USE_CONDVARS */
/****************************************************************************
* Test cases.
****************************************************************************/
+/**
+ * @page test_005_001 [5.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>
+ * - [5.1.1] Getting the initial priority.
+ * - [5.1.2] Locking the mutex.
+ * - [5.1.3] Five threads are created that try to lock and unlock the
+ * mutex then terminate. The threads are created in ascending
+ * priority order.
+ * - [5.1.4] Unlocking the mutex, the threads will wakeup in priority
+ * order because the mutext queue is an ordered one.
+ * .
+ */
+
+static void test_005_001_setup(void) {
+ chMtxObjectInit(&m1);
+}
+
+static void test_005_001_execute(void) {
+ tprio_t prio;
+
+ /* [5.1.1] Getting the initial priority.*/
+ test_set_step(1);
+ {
+ prio = chThdGetPriorityX();
+ }
+
+ /* [5.1.2] Locking the mutex.*/
+ test_set_step(2);
+ {
+ chMtxLock(&m1);
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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 test_005_001 = {
+ "Priority enqueuing test",
+ test_005_001_setup,
+ NULL,
+ test_005_001_execute
+};
+
+/**
+ * @page test_005_002 [5.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>Test Steps</h2>
+ * - [5.2.1] Getting the system time for test duration measurement.
+ * - [5.2.2] The three contenders threads are created and let run
+ * atomically, the goals sequence is tested, the threads must
+ * complete in priority order.
+ * - [5.2.3] Testing that all threads completed within the specified
+ * time windows (100mS...100mS+ALLOWED_DELAY).
+ * .
+ */
+
+static void test_005_002_setup(void) {
+ chMtxObjectInit(&m1);
+}
+
+static void test_005_002_execute(void) {
+ systime_t time;
+
+ /* [5.2.1] Getting the system time for test duration measurement.*/
+ test_set_step(1);
+ {
+ time = test_wait_tick();
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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 + MS2ST(100), time + MS2ST(100) + ALLOWED_DELAY,
+ "out of time window");
+ }
+}
+
+static const testcase_t test_005_002 = {
+ "Priority inheritance, simple case",
+ test_005_002_setup,
+ NULL,
+ test_005_002_execute
+};
+
+/**
+ * @page test_005_003 [5.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>Test Steps</h2>
+ * - [5.3.1] Getting the system time for test duration measurement.
+ * - [5.3.2] The five contenders threads are created and let run
+ * atomically, the goals sequence is tested, the threads must
+ * complete in priority order.
+ * - [5.3.3] Testing that all threads completed within the specified
+ * time windows (110mS...110mS+ALLOWED_DELAY).
+ * .
+ */
+
+static void test_005_003_setup(void) {
+ chMtxObjectInit(&m1); /* Mutex B.*/
+ chMtxObjectInit(&m2); /* Mutex A.*/
+}
+
+static void test_005_003_execute(void) {
+ systime_t time;
+
+ /* [5.3.1] Getting the system time for test duration measurement.*/
+ test_set_step(1);
+ {
+ time = test_wait_tick();
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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 + MS2ST(110), time + MS2ST(110) + ALLOWED_DELAY,
+ "out of time window");
+ }
+}
+
+static const testcase_t test_005_003 = {
+ "Priority inheritance, complex case",
+ test_005_003_setup,
+ NULL,
+ test_005_003_execute
+};
+
+/**
+ * @page test_005_004 [5.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>
+ * - [5.4.1] Getting current thread priority P(0) and assigning to the
+ * threads A and B priorities +1 and +2.
+ * - [5.4.2] Spawning threads A and B at priorities P(A) and P(B).
+ * - [5.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.
+ * - [5.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.
+ * - [5.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.
+ * - [5.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.
+ * - [5.4.7] Unlocking M2, the priority should fall back to P(A).
+ * - [5.4.8] Unlocking M1, the priority should fall back to P(0).
+ * .
+ */
+
+static void test_005_004_setup(void) {
+ chMtxObjectInit(&m1);
+ chMtxObjectInit(&m2);
+}
+
+static void test_005_004_execute(void) {
+ tprio_t p, pa, pb;
+
+ /* [5.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;
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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");
+ }
+
+ /* [5.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 test_005_004 = {
+ "Priority return verification",
+ test_005_004_setup,
+ NULL,
+ test_005_004_execute
+};
+
/****************************************************************************
* Exported data.
****************************************************************************/
@@ -61,6 +533,10 @@ static CONDVAR_DECL(c1);
* @brief Mutexes, Condition Variables and Priority Inheritance.
*/
const testcase_t * const test_sequence_005[] = {
+ &test_005_001,
+ &test_005_002,
+ &test_005_003,
+ &test_005_004,
NULL
};