diff --git a/sys/include/vfs.h b/sys/include/vfs.h index 157b25e2e59b..216606a1da9e 100644 --- a/sys/include/vfs.h +++ b/sys/include/vfs.h @@ -54,15 +54,10 @@ #define VFS_H #include -/* The stdatomic.h in GCC gives compilation errors with C++ - * see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932 - */ #ifdef __cplusplus -#include -/* Make atomic_int available without namespace specifier */ -using std::atomic_int; +#include "c11_atomics_compat.hpp" #else -#include /* for atomic_int */ +#include #endif #include /* for struct stat */ #include /* for off_t etc. */ diff --git a/sys/include/xtimer.h b/sys/include/xtimer.h index bd1e1c50a35a..d30da427c5ca 100644 --- a/sys/include/xtimer.h +++ b/sys/include/xtimer.h @@ -36,6 +36,7 @@ #include "msg.h" #endif /* MODULE_CORE_MSG */ #include "mutex.h" +#include "rmutex.h" #include "kernel_types.h" #ifdef MODULE_ZTIMER_XTIMER_COMPAT @@ -413,6 +414,17 @@ static inline bool xtimer_less64(xtimer_ticks64_t a, xtimer_ticks64_t b); */ int xtimer_mutex_lock_timeout(mutex_t *mutex, uint64_t us); +/** + * @brief lock a rmutex but with timeout + * + * @param[in] rmutex rmutex to lock + * @param[in] us timeout in microseconds relative + * + * @return 0, when returned after mutex was locked + * @return -1, when the timeout occcured + */ +int xtimer_rmutex_lock_timeout(rmutex_t *rmutex, uint64_t us); + #if defined(MODULE_CORE_THREAD_FLAGS) || defined(DOXYGEN) /** * @brief Set timeout thread flag after @p timeout diff --git a/sys/xtimer/xtimer.c b/sys/xtimer/xtimer.c index 85762280848f..90be5fff14d2 100644 --- a/sys/xtimer/xtimer.c +++ b/sys/xtimer/xtimer.c @@ -24,6 +24,7 @@ #include "xtimer.h" #include "mutex.h" +#include "rmutex.h" #include "thread.h" #include "irq.h" #include "div.h" @@ -270,6 +271,23 @@ int xtimer_mutex_lock_timeout(mutex_t *mutex, uint64_t timeout) return -mt.dequeued; } +int xtimer_rmutex_lock_timeout(rmutex_t *rmutex, uint64_t timeout) +{ + if (rmutex_trylock(rmutex) == 1) { + return 0; + } + if (xtimer_mutex_lock_timeout(&rmutex->mutex, timeout) < 0) { + return -1; + } + /* the mutex is locked, update the owner */ + /* ensure that owner is written atomically, since others need a consistent value */ + atomic_store_explicit(&rmutex->owner, thread_getpid(), memory_order_relaxed); + /* increase the refcount */ + rmutex->refcount++; + + return 0; +} + #ifdef MODULE_CORE_THREAD_FLAGS static void _set_timeout_flag_callback(void* arg) { diff --git a/tests/rmutex/Makefile b/tests/rmutex/Makefile index 24bb48165705..8bff23ceead2 100644 --- a/tests/rmutex/Makefile +++ b/tests/rmutex/Makefile @@ -1,3 +1,5 @@ include ../Makefile.tests_common +USEMODULE += xtimer + include $(RIOTBASE)/Makefile.include diff --git a/tests/rmutex/main.c b/tests/rmutex/main.c index 03004ca9331b..2e34590c3d2a 100644 --- a/tests/rmutex/main.c +++ b/tests/rmutex/main.c @@ -22,6 +22,7 @@ #include "rmutex.h" #include "thread.h" +#include "xtimer.h" #define THREAD_NUMOF (5U) @@ -31,6 +32,8 @@ 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 const unsigned timeouts[THREAD_NUMOF] = { 100 * US_PER_MS, 200 * US_PER_MS, 300 * US_PER_MS, + 400 * US_PER_MS, 500 * US_PER_MS}; static rmutex_t testlock; @@ -45,8 +48,9 @@ static void lock_recursive(char n, char depth) printf("T%i (prio %i, depth %i): locked rmutex now\n", (int)t->pid, (int)t->priority, (int)n); - if (n + 1 < depth) + if (n + 1 < depth) { lock_recursive(n + 1, depth); + } thread_yield(); @@ -65,6 +69,42 @@ static void *lockme(void *arg) return NULL; } +static void lock_recursive_timeout(char n, uint32_t us) +{ + volatile thread_t *t = sched_active_thread; + + printf("T%i (prio %i, depth %i, timeout %" PRIu32 "): trying to lock rmutex now\n", + (int)t->pid, (int)t->priority, (int)n, us); + if (xtimer_rmutex_lock_timeout(&testlock, us) < 0) { + printf("T%i (prio %i, depth %i): timeout\n", + (int)t->pid, (int)t->priority, (int)n); + return; + } + + printf("T%i (prio %i, depth %i): locked rmutex now\n", + (int)t->pid, (int)t->priority, (int)n); + + if (n + 1 < 3) { + lock_recursive_timeout(n + 1, us); + } + + 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_timeout(void *arg) +{ + intptr_t timeout = (intptr_t)arg; + + lock_recursive_timeout(0, timeout); + + return NULL; +} + int main(void) { puts("Recursive Mutex test"); @@ -77,14 +117,30 @@ int main(void) /* 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"); + lockme, (void*)(intptr_t)depth[i], "t"); } /* allow threads to lock the mutex */ printf("main: unlocking recursive mutex\n"); rmutex_unlock(&testlock); + /* test 1 END */ + /* xtimer_rmutex_lock_timeout() test */ + /* 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_timeout, (void*)(intptr_t)timeouts[i], "t"); + } + /* wait to have some thread timeouts */ + puts("main: waiting 250ms"); + xtimer_usleep(250 * US_PER_MS); + /* allow threads to lock the mutex */ + printf("main: unlocking recursive mutex\n"); + + rmutex_unlock(&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 index 0ca8ae3748fe..b731b3e336d1 100755 --- a/tests/rmutex/tests/01-run.py +++ b/tests/rmutex/tests/01-run.py @@ -28,6 +28,14 @@ 7: 5 } +timeouts = { + 3: 100000, + 4: 200000, + 5: 300000, + 6: 400000, + 7: 500000 + } + def thread_prio_sort(x): return thread_prio.get(x)*1000 + x @@ -44,6 +52,24 @@ def testfunc(child): child.expect_exact("T{} (prio {}, depth {}): locked rmutex now" .format(T, thread_prio[T], depth)) + # xtimer_rmutex_lock_timeout() test + for k in thread_prio.keys(): + child.expect_exact("T{} (prio {}, depth 0, timeout {}): trying to lock rmutex now" + .format(k, thread_prio[k], timeouts[k])) + child.expect_exact("main: waiting 250ms") + for k in range(3, 4): + child.expect_exact("T{} (prio {}, depth 0): timeout" + .format(k, thread_prio[k])) + child.expect_exact + child.expect_exact("main: unlocking recursive mutex") + pri_sorted = sorted({k: thread_prio[k] for k in (5, 6, 7)}, key=thread_prio_sort) + for T in pri_sorted: + for depth in range(3): + child.expect_exact("T{} (prio {}, depth {}): locked rmutex now" + .format(T, thread_prio[T], depth)) + + child.expect_exact("Test END, check the order of priorities above.") + if __name__ == "__main__": sys.exit(run(testfunc))