diff --git a/tests/rmutex/Makefile b/tests/rmutex/Makefile new file mode 100644 index 0000000000..8b1b9aacf3 --- /dev/null +++ b/tests/rmutex/Makefile @@ -0,0 +1,6 @@ +APPLICATION = rmutex +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := stm32f0discovery weio nucleo-f030 nucleo32-f042 + +include $(RIOTBASE)/Makefile.include diff --git a/tests/rmutex/README.md b/tests/rmutex/README.md new file mode 100644 index 0000000000..55854b418f --- /dev/null +++ b/tests/rmutex/README.md @@ -0,0 +1,86 @@ +Expected result +=============== + +When successful, you should see 5 different threads printing their +PID, priority and recursion depth. The thread with the lowest priority +should be able to lock (and unlock) the mutex first, followed by the +other threads in the order of their priority (highest next). If two +threads have the same priority the lower thread id should acquire the +lock. The output should look like the following: + +``` +main(): This is RIOT! (Version: xxx) +Recursive Mutex test +Please refer to the README.md for more information + +Recursive Mutex test +Please refer to the README.md for more information + +T3 (prio 6, depth 0): trying to lock rmutex now +T4 (prio 4, depth 0): trying to lock rmutex now +T5 (prio 5, depth 0): trying to lock rmutex now +T6 (prio 2, depth 0): trying to lock rmutex now +T7 (prio 3, depth 0): trying to lock rmutex now +main: unlocking recursive mutex +T6 (prio 2, depth 0): locked rmutex now +T6 (prio 2, depth 1): trying to lock rmutex now +T6 (prio 2, depth 1): locked rmutex now +T6 (prio 2, depth 2): trying to lock rmutex now +T6 (prio 2, depth 2): locked rmutex now +T6 (prio 2, depth 3): trying to lock rmutex now +T6 (prio 2, depth 3): locked rmutex now +T6 (prio 2, depth 3): unlocked rmutex +T6 (prio 2, depth 2): unlocked rmutex +T6 (prio 2, depth 1): unlocked rmutex +T6 (prio 2, depth 0): unlocked rmutex +T7 (prio 3, depth 0): locked rmutex now +T7 (prio 3, depth 1): trying to lock rmutex now +T7 (prio 3, depth 1): locked rmutex now +T7 (prio 3, depth 2): trying to lock rmutex now +T7 (prio 3, depth 2): locked rmutex now +T7 (prio 3, depth 3): trying to lock rmutex now +T7 (prio 3, depth 3): locked rmutex now +T7 (prio 3, depth 4): trying to lock rmutex now +T7 (prio 3, depth 4): locked rmutex now +T7 (prio 3, depth 4): unlocked rmutex +T7 (prio 3, depth 3): unlocked rmutex +T7 (prio 3, depth 2): unlocked rmutex +T7 (prio 3, depth 1): unlocked rmutex +T7 (prio 3, depth 0): unlocked rmutex +T4 (prio 4, depth 0): locked rmutex now +T4 (prio 4, depth 1): trying to lock rmutex now +T4 (prio 4, depth 1): locked rmutex now +T4 (prio 4, depth 2): trying to lock rmutex now +T4 (prio 4, depth 2): locked rmutex now +T4 (prio 4, depth 2): unlocked rmutex +T4 (prio 4, depth 1): unlocked rmutex +T4 (prio 4, depth 0): unlocked rmutex +T5 (prio 5, depth 0): locked rmutex now +T5 (prio 5, depth 1): trying to lock rmutex now +T5 (prio 5, depth 1): locked rmutex now +T5 (prio 5, depth 2): trying to lock rmutex now +T5 (prio 5, depth 2): locked rmutex now +T5 (prio 5, depth 2): unlocked rmutex +T5 (prio 5, depth 1): unlocked rmutex +T5 (prio 5, depth 0): unlocked rmutex +T3 (prio 6, depth 0): locked rmutex now +T3 (prio 6, depth 1): trying to lock rmutex now +T3 (prio 6, depth 1): locked rmutex now +T3 (prio 6, depth 2): trying to lock rmutex now +T3 (prio 6, depth 2): locked rmutex now +T3 (prio 6, depth 3): trying to lock rmutex now +T3 (prio 6, depth 3): locked rmutex now +T3 (prio 6, depth 4): trying to lock rmutex now +T3 (prio 6, depth 4): locked rmutex now +T3 (prio 6, depth 4): unlocked rmutex +T3 (prio 6, depth 3): unlocked rmutex +T3 (prio 6, depth 2): unlocked rmutex +T3 (prio 6, depth 1): unlocked rmutex +T3 (prio 6, depth 0): unlocked rmutex + +Test END, check the order of priorities above. +``` + +Background +========== +This test application stresses a mutex with a number of threads waiting on it. diff --git a/tests/rmutex/main.c b/tests/rmutex/main.c new file mode 100644 index 0000000000..03004ca933 --- /dev/null +++ b/tests/rmutex/main.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 Theobroma Systems Design & Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for testing recursive mutexes + * + * @author Hauke Petersen + * @author Martin Elshuber + * @} + */ + +#include + +#include "rmutex.h" +#include "thread.h" + +#define THREAD_NUMOF (5U) + +extern volatile thread_t *sched_active_thread; + +static char stacks[THREAD_NUMOF][THREAD_STACKSIZE_MAIN]; + +static const char prios[THREAD_NUMOF] = {THREAD_PRIORITY_MAIN - 1, 4, 5, 2, 4}; +static const char depth[THREAD_NUMOF] = {5, 3, 3, 4, 5}; + +static rmutex_t testlock; + +static void lock_recursive(char n, char depth) +{ + volatile thread_t *t = sched_active_thread; + + printf("T%i (prio %i, depth %i): trying to lock rmutex now\n", + (int)t->pid, (int)t->priority, (int)n); + rmutex_lock(&testlock); + + printf("T%i (prio %i, depth %i): locked rmutex now\n", + (int)t->pid, (int)t->priority, (int)n); + + if (n + 1 < depth) + lock_recursive(n + 1, depth); + + thread_yield(); + + rmutex_unlock(&testlock); + + printf("T%i (prio %i, depth %i): unlocked rmutex\n", + (int)t->pid, (int)t->priority, (int)n); +} + +static void *lockme(void *arg) +{ + intptr_t depth = (intptr_t)arg; + + lock_recursive(0, depth); + + return NULL; +} + +int main(void) +{ + puts("Recursive Mutex test"); + puts("Please refer to the README.md for more information\n"); + + rmutex_init(&testlock); + + /* lock mutex, so that spawned threads have to wait */ + rmutex_lock(&testlock); + /* create threads */ + for (unsigned i = 0; i < THREAD_NUMOF; i++) { + thread_create(stacks[i], sizeof(stacks[i]), prios[i], 0, + lockme, (void*)(intptr_t)depth[i], "t"); + } + /* allow threads to lock the mutex */ + printf("main: unlocking recursive mutex\n"); + + rmutex_unlock(&testlock); + + rmutex_lock(&testlock); + puts("\nTest END, check the order of priorities above."); + + return 0; +} diff --git a/tests/rmutex/tests/01-run.py b/tests/rmutex/tests/01-run.py new file mode 100755 index 0000000000..2433414db7 --- /dev/null +++ b/tests/rmutex/tests/01-run.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 Theobroma Systems Design & Consulting GmbH +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +# Author: Martin Elshuber + +import os +import sys + +sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner')) +import testrunner + +thread_prio = { + 3: 6, + 4: 4, + 5: 5, + 6: 2, + 7: 4 + } + +lock_depth = { + 3: 5, + 4: 3, + 5: 3, + 6: 4, + 7: 5 + } + +def thread_prio_sort(x): + return thread_prio.get(x)*1000 + x + +def testfunc(child): + for k in thread_prio.keys(): + child.expect(u"T%i \(prio %i, depth 0\): trying to lock rmutex now" % + (k, thread_prio[k])) + + pri_sorted = sorted(thread_prio, key=thread_prio_sort); + for T in pri_sorted: + for depth in range(lock_depth[T]): + child.expect(u"T%i \(prio %i, depth %i\): locked rmutex now" % + (T, thread_prio[T], depth)) + +if __name__ == "__main__": + sys.exit(testrunner.run(testfunc))