mirror of https://github.com/ARMmbed/mbed-os.git
commit
c5c470e22c
|
@ -12,12 +12,8 @@ using namespace utest::v1;
|
||||||
|
|
||||||
#define TEST_STACK_SIZE 512
|
#define TEST_STACK_SIZE 512
|
||||||
|
|
||||||
#define TEST_ONE_SEC_MS (1000)
|
#define TEST_LONG_DELAY 20
|
||||||
#define TEST_HALF_SEC_MS (500)
|
#define TEST_DELAY 10
|
||||||
#define TEST_HALF_SEC_US (500000)
|
|
||||||
#define TEST_ONE_MS_US (1000)
|
|
||||||
|
|
||||||
#define THREAD_DELAY 50
|
|
||||||
#define SIGNALS_TO_EMIT 100
|
#define SIGNALS_TO_EMIT 100
|
||||||
|
|
||||||
Mutex stdio_mutex;
|
Mutex stdio_mutex;
|
||||||
|
@ -26,11 +22,14 @@ volatile int change_counter = 0;
|
||||||
volatile bool changing_counter = false;
|
volatile bool changing_counter = false;
|
||||||
volatile bool mutex_defect = 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;
|
bool result = true;
|
||||||
|
|
||||||
osStatus stat = stdio_mutex.lock();
|
osStatus stat = stdio_mutex.lock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
|
|
||||||
|
core_util_critical_section_enter();
|
||||||
if (changing_counter == true) {
|
if (changing_counter == true) {
|
||||||
result = false;
|
result = false;
|
||||||
mutex_defect = true;
|
mutex_defect = true;
|
||||||
|
@ -38,25 +37,37 @@ bool manipulate_protected_zone(const int thread_delay) {
|
||||||
changing_counter = true;
|
changing_counter = true;
|
||||||
|
|
||||||
change_counter++;
|
change_counter++;
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
|
||||||
Thread::wait(thread_delay);
|
Thread::wait(thread_delay);
|
||||||
|
|
||||||
|
core_util_critical_section_enter();
|
||||||
changing_counter = false;
|
changing_counter = false;
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
|
||||||
stat = stdio_mutex.unlock();
|
stat = stdio_mutex.unlock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_thread(int const *thread_delay) {
|
void test_thread(int const *thread_delay)
|
||||||
|
{
|
||||||
while (true) {
|
while (true) {
|
||||||
manipulate_protected_zone(*thread_delay);
|
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)
|
void test_multiple_threads(void)
|
||||||
{
|
{
|
||||||
const int t1_delay = THREAD_DELAY * 1;
|
const int t1_delay = TEST_DELAY * 1;
|
||||||
const int t2_delay = THREAD_DELAY * 2;
|
const int t2_delay = TEST_DELAY * 2;
|
||||||
const int t3_delay = THREAD_DELAY * 3;
|
const int t3_delay = TEST_DELAY * 3;
|
||||||
|
|
||||||
Thread t2(osPriorityNormal, TEST_STACK_SIZE);
|
Thread t2(osPriorityNormal, TEST_STACK_SIZE);
|
||||||
Thread t3(osPriorityNormal, TEST_STACK_SIZE);
|
Thread t3(osPriorityNormal, TEST_STACK_SIZE);
|
||||||
|
@ -69,34 +80,51 @@ void test_multiple_threads(void)
|
||||||
Thread::wait(t1_delay);
|
Thread::wait(t1_delay);
|
||||||
manipulate_protected_zone(t1_delay);
|
manipulate_protected_zone(t1_delay);
|
||||||
|
|
||||||
|
core_util_critical_section_enter();
|
||||||
if (change_counter >= SIGNALS_TO_EMIT or mutex_defect == true) {
|
if (change_counter >= SIGNALS_TO_EMIT or mutex_defect == true) {
|
||||||
|
core_util_critical_section_exit();
|
||||||
t2.terminate();
|
t2.terminate();
|
||||||
t3.terminate();
|
t3.terminate();
|
||||||
break;
|
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)
|
void test_dual_thread_nolock_lock_thread(Mutex *mutex)
|
||||||
{
|
{
|
||||||
bool stat_b = mutex->trylock();
|
osStatus stat = mutex->lock(osWaitForever);
|
||||||
TEST_ASSERT_EQUAL(stat_b, true);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
|
|
||||||
osStatus stat = mutex->unlock();
|
stat = mutex->unlock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_dual_thread_nolock_trylock_thread(Mutex *mutex)
|
void test_dual_thread_nolock_trylock_thread(Mutex *mutex)
|
||||||
{
|
{
|
||||||
bool stat_b = mutex->trylock();
|
bool stat_b = mutex->trylock();
|
||||||
TEST_ASSERT_EQUAL(stat_b, true);
|
TEST_ASSERT_EQUAL(true, stat_b);
|
||||||
|
|
||||||
osStatus stat = mutex->unlock();
|
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 (*F)(Mutex *)>
|
template <void (*F)(Mutex *)>
|
||||||
void test_dual_thread_nolock(void)
|
void test_dual_thread_nolock(void)
|
||||||
{
|
{
|
||||||
|
@ -105,15 +133,24 @@ void test_dual_thread_nolock(void)
|
||||||
|
|
||||||
thread.start(callback(F, &mutex));
|
thread.start(callback(F, &mutex));
|
||||||
|
|
||||||
wait_us(TEST_HALF_SEC_MS);
|
wait_ms(TEST_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_dual_thread_lock_unlock_thread(Mutex *mutex)
|
void test_dual_thread_lock_unlock_thread(Mutex *mutex)
|
||||||
{
|
{
|
||||||
osStatus stat = mutex->lock(osWaitForever);
|
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)
|
void test_dual_thread_lock_unlock(void)
|
||||||
{
|
{
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
@ -121,31 +158,45 @@ void test_dual_thread_lock_unlock(void)
|
||||||
Thread thread(osPriorityNormal, TEST_STACK_SIZE);
|
Thread thread(osPriorityNormal, TEST_STACK_SIZE);
|
||||||
|
|
||||||
stat = mutex.lock();
|
stat = mutex.lock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
|
|
||||||
thread.start(callback(test_dual_thread_lock_unlock_thread, &mutex));
|
thread.start(callback(test_dual_thread_lock_unlock_thread, &mutex));
|
||||||
|
|
||||||
stat = mutex.unlock();
|
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)
|
void test_dual_thread_lock_trylock_thread(Mutex *mutex)
|
||||||
{
|
{
|
||||||
bool stat = mutex->trylock();
|
bool stat = mutex->trylock();
|
||||||
TEST_ASSERT_EQUAL(stat, false);
|
TEST_ASSERT_EQUAL(false, stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_dual_thread_lock_lock_thread(Mutex *mutex)
|
void test_dual_thread_lock_lock_thread(Mutex *mutex)
|
||||||
{
|
{
|
||||||
uint32_t start = us_ticker_read();
|
uint32_t start = us_ticker_read();
|
||||||
|
|
||||||
osStatus stat = mutex->lock(TEST_HALF_SEC_MS);
|
osStatus stat = mutex->lock(TEST_DELAY);
|
||||||
TEST_ASSERT_EQUAL(stat, osErrorTimeout);
|
TEST_ASSERT_EQUAL(osErrorTimeout, stat);
|
||||||
TEST_ASSERT_UINT32_WITHIN(TEST_ONE_MS_US, TEST_HALF_SEC_US, us_ticker_read() - start);
|
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 (*F)(Mutex *)>
|
template <void (*F)(Mutex *)>
|
||||||
void test_dual_thread_lock(void)
|
void test_dual_thread_lock(void)
|
||||||
{
|
{
|
||||||
|
@ -154,59 +205,78 @@ void test_dual_thread_lock(void)
|
||||||
Thread thread(osPriorityNormal, TEST_STACK_SIZE);
|
Thread thread(osPriorityNormal, TEST_STACK_SIZE);
|
||||||
|
|
||||||
stat = mutex.lock();
|
stat = mutex.lock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
|
|
||||||
thread.start(callback(F, &mutex));
|
thread.start(callback(F, &mutex));
|
||||||
|
|
||||||
wait_us(TEST_ONE_SEC_MS);
|
wait_ms(TEST_LONG_DELAY);
|
||||||
|
|
||||||
stat = mutex.unlock();
|
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)
|
void test_single_thread_lock_recursive(void)
|
||||||
{
|
{
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
osStatus stat;
|
osStatus stat;
|
||||||
|
|
||||||
stat = mutex.lock();
|
stat = mutex.lock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
|
|
||||||
stat = mutex.lock();
|
stat = mutex.lock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
|
|
||||||
stat = mutex.unlock();
|
stat = mutex.unlock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
|
|
||||||
stat = mutex.unlock();
|
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)
|
void test_single_thread_trylock(void)
|
||||||
{
|
{
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
bool stat_b = mutex.trylock();
|
bool stat_b = mutex.trylock();
|
||||||
TEST_ASSERT_EQUAL(stat_b, true);
|
TEST_ASSERT_EQUAL(true, stat_b);
|
||||||
|
|
||||||
osStatus stat = mutex.unlock();
|
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)
|
void test_single_thread_lock(void)
|
||||||
{
|
{
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
osStatus stat;
|
osStatus stat;
|
||||||
|
|
||||||
stat = mutex.lock();
|
stat = mutex.lock();
|
||||||
TEST_ASSERT_EQUAL(stat, osOK);
|
TEST_ASSERT_EQUAL(osOK, stat);
|
||||||
|
|
||||||
stat = mutex.unlock();
|
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) {
|
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||||
GREENTEA_SETUP(15, "default_auto");
|
{
|
||||||
|
GREENTEA_SETUP(10, "default_auto");
|
||||||
return verbose_test_setup_handler(number_of_cases);
|
return verbose_test_setup_handler(number_of_cases);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,6 +294,7 @@ Case cases[] = {
|
||||||
|
|
||||||
Specification specification(test_setup, cases);
|
Specification specification(test_setup, cases);
|
||||||
|
|
||||||
int main() {
|
int main()
|
||||||
|
{
|
||||||
return !Harness::run(specification);
|
return !Harness::run(specification);
|
||||||
}
|
}
|
||||||
|
|
13
rtos/Mutex.h
13
rtos/Mutex.h
|
@ -53,7 +53,12 @@ public:
|
||||||
|
|
||||||
/** Wait until a Mutex becomes available.
|
/** Wait until a Mutex becomes available.
|
||||||
@param millisec timeout value or 0 in case of no time-out. (default: osWaitForever)
|
@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);
|
osStatus lock(uint32_t millisec=osWaitForever);
|
||||||
|
|
||||||
|
@ -63,7 +68,11 @@ public:
|
||||||
bool trylock();
|
bool trylock();
|
||||||
|
|
||||||
/** Unlock the mutex that has previously been locked by the same thread
|
/** 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();
|
osStatus unlock();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue