diff --git a/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp b/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp index a53c45527c..9e41e80342 100644 --- a/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp +++ b/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp @@ -12,13 +12,9 @@ using namespace utest::v1; #define TEST_STACK_SIZE 512 -#define TEST_ONE_SEC_MS (1000) -#define TEST_HALF_SEC_MS (500) -#define TEST_HALF_SEC_US (500000) -#define TEST_ONE_MS_US (1000) - -#define THREAD_DELAY 50 -#define SIGNALS_TO_EMIT 100 +#define TEST_LONG_DELAY 20 +#define TEST_DELAY 10 +#define SIGNALS_TO_EMIT 100 Mutex stdio_mutex; @@ -26,11 +22,14 @@ volatile int change_counter = 0; volatile bool changing_counter = false; volatile bool mutex_defect = false; -bool manipulate_protected_zone(const int thread_delay) { +bool manipulate_protected_zone(const int thread_delay) +{ bool result = true; osStatus stat = stdio_mutex.lock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); + + core_util_critical_section_enter(); if (changing_counter == true) { result = false; mutex_defect = true; @@ -38,25 +37,37 @@ bool manipulate_protected_zone(const int thread_delay) { changing_counter = true; change_counter++; + core_util_critical_section_exit(); + Thread::wait(thread_delay); + core_util_critical_section_enter(); changing_counter = false; + core_util_critical_section_exit(); + stat = stdio_mutex.unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); return result; } -void test_thread(int const *thread_delay) { +void test_thread(int const *thread_delay) +{ while (true) { manipulate_protected_zone(*thread_delay); } } +/** Test multiple thread + + Given 3 threads started with different delays and a section protected with a mutex + when each thread runs it tries to lock the mutex + then no more than one thread should be able to access protected region +*/ void test_multiple_threads(void) { - const int t1_delay = THREAD_DELAY * 1; - const int t2_delay = THREAD_DELAY * 2; - const int t3_delay = THREAD_DELAY * 3; + const int t1_delay = TEST_DELAY * 1; + const int t2_delay = TEST_DELAY * 2; + const int t3_delay = TEST_DELAY * 3; Thread t2(osPriorityNormal, TEST_STACK_SIZE); Thread t3(osPriorityNormal, TEST_STACK_SIZE); @@ -69,34 +80,51 @@ void test_multiple_threads(void) Thread::wait(t1_delay); manipulate_protected_zone(t1_delay); + core_util_critical_section_enter(); if (change_counter >= SIGNALS_TO_EMIT or mutex_defect == true) { + core_util_critical_section_exit(); t2.terminate(); t3.terminate(); break; } + core_util_critical_section_exit(); } - TEST_ASSERT_EQUAL(mutex_defect, false); + TEST_ASSERT_EQUAL(false, mutex_defect); } void test_dual_thread_nolock_lock_thread(Mutex *mutex) { - bool stat_b = mutex->trylock(); - TEST_ASSERT_EQUAL(stat_b, true); + osStatus stat = mutex->lock(osWaitForever); + TEST_ASSERT_EQUAL(osOK, stat); - osStatus stat = mutex->unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + stat = mutex->unlock(); + TEST_ASSERT_EQUAL(osOK, stat); } void test_dual_thread_nolock_trylock_thread(Mutex *mutex) { bool stat_b = mutex->trylock(); - TEST_ASSERT_EQUAL(stat_b, true); + TEST_ASSERT_EQUAL(true, stat_b); osStatus stat = mutex->unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); } +/** Test dual thread no-lock + + Test dual thread second thread lock + Given two threads A & B and a mutex + When thread A creates a mutex and starts thread B + and thread B calls @a lock and @a unlock + Then returned statuses are osOK + + Test dual thread second thread trylock + Given two threads A & B and a mutex + When thread A creates a mutex and starts thread B + and thread B calls @a trylock and @a unlock + Then returned statuses are true and osOK +*/ template void test_dual_thread_nolock(void) { @@ -105,15 +133,24 @@ void test_dual_thread_nolock(void) thread.start(callback(F, &mutex)); - wait_us(TEST_HALF_SEC_MS); + wait_ms(TEST_DELAY); } void test_dual_thread_lock_unlock_thread(Mutex *mutex) { osStatus stat = mutex->lock(osWaitForever); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); } +/** Test dual thread lock unlock + + Given two threads and a lock + When thread A locks the lock and starts thread B + and thread B calls @a lock on the mutex + Then thread B waits for thread A to unlock the lock + When thread A calls @a unlock on the mutex + Then thread B acquires the lock +*/ void test_dual_thread_lock_unlock(void) { Mutex mutex; @@ -121,31 +158,45 @@ void test_dual_thread_lock_unlock(void) Thread thread(osPriorityNormal, TEST_STACK_SIZE); stat = mutex.lock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); thread.start(callback(test_dual_thread_lock_unlock_thread, &mutex)); stat = mutex.unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); - wait_us(TEST_HALF_SEC_MS); + wait_ms(TEST_DELAY); } void test_dual_thread_lock_trylock_thread(Mutex *mutex) { bool stat = mutex->trylock(); - TEST_ASSERT_EQUAL(stat, false); + TEST_ASSERT_EQUAL(false, stat); } void test_dual_thread_lock_lock_thread(Mutex *mutex) { uint32_t start = us_ticker_read(); - osStatus stat = mutex->lock(TEST_HALF_SEC_MS); - TEST_ASSERT_EQUAL(stat, osErrorTimeout); - TEST_ASSERT_UINT32_WITHIN(TEST_ONE_MS_US, TEST_HALF_SEC_US, us_ticker_read() - start); + osStatus stat = mutex->lock(TEST_DELAY); + TEST_ASSERT_EQUAL(osErrorTimeout, stat); + TEST_ASSERT_UINT32_WITHIN(5000, TEST_DELAY*1000, us_ticker_read() - start); } +/** Test dual thread lock + + Test dual thread lock locked + Given a mutex and two threads A & B + When thread A calls @a lock and starts thread B + and thread B calls @a lock with 500ms timeout + Then thread B waits 500ms and timeouts + + Test dual thread trylock locked + Given a mutex and two threads A & B + When thread A calls @a lock and starts thread B + Then thread B calls @a trylock + and thread B fails to acquire the lock +*/ template void test_dual_thread_lock(void) { @@ -154,59 +205,78 @@ void test_dual_thread_lock(void) Thread thread(osPriorityNormal, TEST_STACK_SIZE); stat = mutex.lock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); thread.start(callback(F, &mutex)); - wait_us(TEST_ONE_SEC_MS); + wait_ms(TEST_LONG_DELAY); stat = mutex.unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); } +/** Test single thread lock recursive + + Given a mutex and a single running thread + When thread calls @a lock twice and @a unlock twice on the mutex + Then the returned statuses are osOK +*/ void test_single_thread_lock_recursive(void) { Mutex mutex; osStatus stat; stat = mutex.lock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); stat = mutex.lock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); stat = mutex.unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); stat = mutex.unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); } +/** Test single thread trylock + + Given a mutex and a single running thread + When thread calls @a trylock and @a unlock on the mutex + Then the returned statuses are osOK +*/ void test_single_thread_trylock(void) { Mutex mutex; bool stat_b = mutex.trylock(); - TEST_ASSERT_EQUAL(stat_b, true); + TEST_ASSERT_EQUAL(true, stat_b); osStatus stat = mutex.unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); } +/** Test single thread lock + + Given a mutex and a single running thread + When thread calls @a lock and @a unlock on the mutex + Then the returned statuses are osOK +*/ void test_single_thread_lock(void) { Mutex mutex; osStatus stat; stat = mutex.lock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); stat = mutex.unlock(); - TEST_ASSERT_EQUAL(stat, osOK); + TEST_ASSERT_EQUAL(osOK, stat); } -utest::v1::status_t test_setup(const size_t number_of_cases) { - GREENTEA_SETUP(15, "default_auto"); +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); return verbose_test_setup_handler(number_of_cases); } @@ -224,6 +294,7 @@ Case cases[] = { Specification specification(test_setup, cases); -int main() { +int main() +{ return !Harness::run(specification); } diff --git a/rtos/Mutex.h b/rtos/Mutex.h index 8f5c0e2adb..4f1ed8a6aa 100644 --- a/rtos/Mutex.h +++ b/rtos/Mutex.h @@ -53,17 +53,26 @@ public: /** Wait until a Mutex becomes available. @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever) - @return status code that indicates the execution status of the function. + @return status code that indicates the execution status of the function: + @a osOK the mutex has been obtained. + @a osErrorTimeout the mutex could not be obtained in the given time. + @a osErrorParameter internal error. + @a osErrorResource the mutex could not be obtained when no timeout was specified. + @a osErrorISR this function cannot be called from the interrupt service routine. */ osStatus lock(uint32_t millisec=osWaitForever); /** Try to lock the mutex, and return immediately - @return true if the mutex was acquired, false otherwise. + @return true if the mutex was acquired, false otherwise. */ bool trylock(); /** Unlock the mutex that has previously been locked by the same thread - @return status code that indicates the execution status of the function. + @return status code that indicates the execution status of the function: + @a osOK the mutex has been released. + @a osErrorParameter internal error. + @a osErrorResource the mutex was not locked or the current thread wasn't the owner. + @a osErrorISR this function cannot be called from the interrupt service routine. */ osStatus unlock();