The unit test designed to test mutex_lock/mutex_unlock functions does not execute as implied by the comments in ec/test/mutex.c.
tldr version:
1. The simple contention test does not actually test contention on the mutex.
2. Random time delays for the task_wait_event() have no effect on the outcome of the test, assuming the delay is > 10 usec. So this seems an unnecessary complication at best and likely was not the intent when the test was written.
3. The for loop in the main test allows for a task to be woken while it's already pending on the mutex being freed. This not an expected use case.
4. The overall pass/fail criteria simply requires that task MTX1 completes. It does not check the following:
a) Verifying that lock contention actually occurs.
b) Verify that high priority tasks acquire the lock in the correct order when there is contention for the lock by multiple tasks.
c) Ensure that all scheduled tasks pending on the mutex are run and the mutex is freed prior to exiting the test.
For more detail and logs see below.
I believe the issue is related to the fact that the test is using random delays to wake the tasks, but in the emulator environment, there are no real timers. Instead time is just forwarded by the amount given.
The simple contention test has the following code:
/* --- Serialization to test simple contention --- */
ccprintf("Simple contention :\n");
/* lock the mutex from the other task */
task_set_event(TASK_ID_MTX2, TASK_EVENT_WAKE, 1);
/* block on the mutex */
ccprintf("MTX1: blocking...\n");
mutex_lock(&mtx);
ccprintf("MTX1: get lock\n");
mutex_unlock(&mtx);
The comments imply that the mutex should be locked by TASK_ID_MTX2, then have a contention when MTX1 attempts to lock the mutex which is resolved when MTX2 releases the lock. However, when running the test the MTX2 task locks/releases the mutex followed by the same sequence in task MTX1.
MTX2: locking...done
MTX2: unlocking...
MTX1: blocking...
MTX1: get lock
I added debug code to print to the console when there is contention on the mutex lock and that print doesn't happen in this test.
Most of the test is loop which is designed to pseudo randomly wake one of 3 tasks which call the same function and acquires/releases the mutex lock. As shown below there is also a random delay to be used to wait before returning back to the main loop.
for (i = 0; i < 500; i++) {
/* Wake up a random task */
task_wake(RANDOM_TASK(rtask));
/* next pseudo random delay */
rtask = prng(rtask);
/* Wait for a "random" period */
task_wait_event(PERIOD_US(rdelay));
/* next pseudo random delay */
rdelay = prng(rdelay);
}
However, the rdelay has no real effect on the test outcome since the emulator does not model it. When the mutex is locked, the task needs to be woken again by this loop in order for it to be unlocked. Depending on the pseudo random order of rtask, there are instances where a task will be woken by the for loop while it already is pending on the lock being released by one of the other 2 tasks. In this case when the mutex is released by the locking task, the task that is pending on the lock will be be run 2 consecutive times. Between acquiring and releasing the lock the task must be woken again by the for loop.
The test is considered passed if the the for loop completes and there are no checks made to see if the tasks pending on the mutex are resumed in the proper order or that the lock is released by the end of the test.
Adding some console prints and shortening the loop iterations to 50 (from 500) gives the following output. Note that A = task 6, B = task 5, and C = task 4.
Massive locking/unlocking :
Waking task 5
B+
B=
Waking task 6
A+
Lock held by task 5, waiters = 0x40
Waking task 4
C+
Lock held by task 5, waiters = 0x50
Waking task 5
B-
A=
Waking task 5
B+
Lock held by task 6, waiters = 0x30
Waking task 5
Waking task 5
Waking task 6
A-
B=
B-
C=
Waking task 4
C-
Waking task 4
C+
C=
Waking task 4
C-
Waking task 4
C+
C=
Waking task 4
C-
Waking task 4
C+
C=
Waking task 6
A+
Lock held by task 4, waiters = 0x40
Waking task 5
B+
Lock held by task 4, waiters = 0x60
Waking task 4
C-
A=
Waking task 4
C+
Lock held by task 6, waiters = 0x30
Waking task 5
Waking task 6
A-
B=
B-
C=
Waking task 6
A+
Lock held by task 4, waiters = 0x40
Waking task 4
C-
A=
Waking task 6
A-
Waking task 5
B+
B=
Waking task 6
A+
Lock held by task 5, waiters = 0x40
Waking task 6
Waking task 6
Waking task 6
Waking task 5
B-
A=
A-
Waking task 4
C+
C=
Waking task 6
A+
Lock held by task 4, waiters = 0x40
Waking task 6
Waking task 6
Waking task 6
Waking task 4
C-
A=
A-
Waking task 4
C+
C=
Waking task 4
C-
Waking task 5
B+
B=
Waking task 4
C+
Lock held by task 5, waiters = 0x10
Waking task 4
Waking task 5
B-
C=
C-
Waking task 5
B+
B=
Waking task 5
B-
Waking task 6
A+
A=
Waking task 6
A-
Waking task 4
C+
C=
Waking task 5
B+
Lock held by task 4, waiters = 0x20
Waking task 4
C-
B=
Waking task 4
C+
Lock held by task 5, waiters = 0x10
Waking task 4
Pass!
Mutex: Lock = 1, Waiters = 0x10