Skip to content

RTOS: Mutex: Rework tests #4729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 115 additions & 44 deletions TESTS/mbedmicro-rtos-mbed/mutex/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,62 @@ 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;

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;
}
changing_counter = true;

change_counter++;
core_util_critical_section_exit();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, I'm thinking about, change_counter Is the variable under test. It is maybe not the best to protect it with a critical section like other flags.


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);
Expand All @@ -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 (*F)(Mutex *)>
void test_dual_thread_nolock(void)
{
Expand All @@ -105,47 +133,70 @@ 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;
osStatus stat;
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 (*F)(Mutex *)>
void test_dual_thread_lock(void)
{
Expand All @@ -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);
}

Expand All @@ -224,6 +294,7 @@ Case cases[] = {

Specification specification(test_setup, cases);

int main() {
int main()
{
return !Harness::run(specification);
}
15 changes: 12 additions & 3 deletions rtos/Mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,26 @@ class Mutex : private mbed::NonCopyable<Mutex> {

/** 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();

Expand Down