| /* |
| * Copyright (c) 2019 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <ztest.h> |
| #include <irq_offload.h> |
| #include <sys/mutex.h> |
| |
| /* Macro declarations */ |
| #define TOTAL_THREADS_WAITING (3) |
| #define PRIO_WAIT (CONFIG_ZTEST_THREAD_PRIORITY - 1) |
| #define PRIO_WAKE (CONFIG_ZTEST_THREAD_PRIORITY - 2) |
| #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE) |
| |
| /******************************************************************************/ |
| /* declaration */ |
| K_THREAD_STACK_DEFINE(stack_1, STACK_SIZE); |
| K_THREAD_STACK_DEFINE(futex_wake_stack, STACK_SIZE); |
| K_THREAD_STACK_ARRAY_DEFINE(multiple_stack, |
| TOTAL_THREADS_WAITING, STACK_SIZE); |
| K_THREAD_STACK_ARRAY_DEFINE(multiple_wake_stack, |
| TOTAL_THREADS_WAITING, STACK_SIZE); |
| |
| ZTEST_BMEM int woken; |
| ZTEST_BMEM int timeout; |
| ZTEST_BMEM int index[TOTAL_THREADS_WAITING]; |
| ZTEST_BMEM struct k_futex simple_futex; |
| ZTEST_BMEM struct k_futex multiple_futex[TOTAL_THREADS_WAITING]; |
| struct k_futex no_access_futex; |
| ZTEST_BMEM atomic_t not_a_futex; |
| ZTEST_BMEM struct sys_mutex also_not_a_futex; |
| |
| struct k_thread futex_tid; |
| struct k_thread futex_wake_tid; |
| struct k_thread multiple_tid[TOTAL_THREADS_WAITING]; |
| struct k_thread multiple_wake_tid[TOTAL_THREADS_WAITING]; |
| |
| /******************************************************************************/ |
| /* Helper functions */ |
| void futex_isr_wake(void *futex) |
| { |
| k_futex_wake((struct k_futex *)futex, false); |
| } |
| |
| void futex_wake_from_isr(struct k_futex *futex) |
| { |
| irq_offload(futex_isr_wake, futex); |
| } |
| |
| /* test futex wait, no futex wake */ |
| void futex_wait_task(void *p1, void *p2, void *p3) |
| { |
| s32_t ret_value; |
| int time_val = *(int *)p1; |
| |
| zassert_true(time_val >= K_FOREVER, "invalid timeout parameter"); |
| |
| ret_value = k_futex_wait(&simple_futex, |
| atomic_get(&simple_futex.val), time_val); |
| |
| switch (time_val) { |
| case K_FOREVER: |
| zassert_true(ret_value == 0, |
| "k_futex_wait failed when it shouldn't have"); |
| zassert_false(ret_value == 0, |
| "futex wait task wakeup when it shouldn't have"); |
| break; |
| case K_NO_WAIT: |
| zassert_true(ret_value == -ETIMEDOUT, |
| "k_futex_wait failed when it shouldn't have"); |
| atomic_sub(&simple_futex.val, 1); |
| break; |
| default: |
| zassert_true(ret_value == -ETIMEDOUT, |
| "k_futex_wait failed when it shouldn't have"); |
| atomic_sub(&simple_futex.val, 1); |
| break; |
| } |
| } |
| |
| void futex_wake_task(void *p1, void *p2, void *p3) |
| { |
| s32_t ret_value; |
| int woken_num = *(int *)p1; |
| |
| ret_value = k_futex_wake(&simple_futex, |
| woken_num == 1 ? false : true); |
| zassert_true(ret_value == woken_num, |
| "k_futex_wake failed when it shouldn't have"); |
| } |
| |
| void futex_wait_wake_task(void *p1, void *p2, void *p3) |
| { |
| s32_t ret_value; |
| int time_val = *(int *)p1; |
| |
| zassert_true(time_val >= K_FOREVER, "invalid timeout parameter"); |
| |
| ret_value = k_futex_wait(&simple_futex, |
| atomic_get(&simple_futex.val), time_val); |
| |
| switch (time_val) { |
| case K_FOREVER: |
| zassert_true(ret_value == 0, |
| "k_futex_wait failed when it shouldn't have"); |
| break; |
| case K_NO_WAIT: |
| zassert_true(ret_value == -ETIMEDOUT, |
| "k_futex_wait failed when it shouldn't have"); |
| break; |
| default: |
| zassert_true(ret_value == 0, |
| "k_futex_wait failed when it shouldn't have"); |
| break; |
| } |
| |
| atomic_sub(&simple_futex.val, 1); |
| } |
| |
| void futex_multiple_wake_task(void *p1, void *p2, void *p3) |
| { |
| s32_t ret_value; |
| int woken_num = *(int *)p1; |
| int idx = *(int *)p2; |
| |
| zassert_true(woken_num > 0, "invalid woken number"); |
| |
| ret_value = k_futex_wake(&multiple_futex[idx], |
| woken_num == 1 ? false : true); |
| zassert_true(ret_value == woken_num, |
| "k_futex_wake failed when it shouldn't have"); |
| } |
| |
| void futex_multiple_wait_wake_task(void *p1, void *p2, void *p3) |
| { |
| s32_t ret_value; |
| int time_val = *(int *)p1; |
| int idx = *(int *)p2; |
| |
| zassert_true(time_val == K_FOREVER, "invalid timeout parameter"); |
| |
| ret_value = k_futex_wait(&multiple_futex[idx], |
| atomic_get(&(multiple_futex[idx].val)), time_val); |
| zassert_true(ret_value == 0, |
| "k_futex_wait failed when it shouldn't have"); |
| |
| atomic_sub(&(multiple_futex[idx].val), 1); |
| } |
| |
| /** |
| * @ingroup futex_tests |
| * @{ |
| */ |
| |
| /** |
| * @brief Test k_futex_wait() forever |
| */ |
| void test_futex_wait_forever(void) |
| { |
| timeout = K_FOREVER; |
| |
| atomic_set(&simple_futex.val, 1); |
| |
| k_thread_create(&futex_tid, stack_1, STACK_SIZE, |
| futex_wait_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the futex_wait_task to execute */ |
| k_yield(); |
| |
| zassert_false(atomic_get(&simple_futex.val) == 0, |
| "wait forever shouldn't wake"); |
| |
| k_thread_abort(&futex_tid); |
| } |
| |
| void test_futex_wait_timeout(void) |
| { |
| timeout = K_MSEC(50); |
| |
| atomic_set(&simple_futex.val, 1); |
| |
| k_thread_create(&futex_tid, stack_1, STACK_SIZE, |
| futex_wait_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the futex_wait_task to execute */ |
| k_sleep(K_MSEC(100)); |
| |
| zassert_true(atomic_get(&simple_futex.val) == 0, |
| "wait timeout doesn't timeout"); |
| |
| k_thread_abort(&futex_tid); |
| } |
| |
| void test_futex_wait_nowait(void) |
| { |
| timeout = K_NO_WAIT; |
| |
| atomic_set(&simple_futex.val, 1); |
| |
| k_thread_create(&futex_tid, stack_1, STACK_SIZE, |
| futex_wait_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the futex_wait_task to execute */ |
| k_sleep(K_MSEC(100)); |
| |
| zassert_true(atomic_get(&simple_futex.val) == 0, "wait nowait fail"); |
| |
| k_thread_abort(&futex_tid); |
| } |
| |
| /** |
| * @brief Test k_futex_wait() and k_futex_wake() |
| */ |
| void test_futex_wait_forever_wake(void) |
| { |
| woken = 1; |
| timeout = K_FOREVER; |
| |
| atomic_set(&simple_futex.val, 1); |
| |
| k_thread_create(&futex_tid, stack_1, STACK_SIZE, |
| futex_wait_wake_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the futex_wait_wake_task to execute */ |
| k_yield(); |
| |
| k_thread_create(&futex_wake_tid, futex_wake_stack, STACK_SIZE, |
| futex_wake_task, &woken, NULL, NULL, |
| PRIO_WAKE, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* |
| * giving time for the futex_wake_task |
| * and futex_wait_wake_task to execute |
| */ |
| k_yield(); |
| |
| zassert_true(atomic_get(&simple_futex.val) == 0, |
| "wait forever doesn't wake"); |
| |
| k_thread_abort(&futex_wake_tid); |
| k_thread_abort(&futex_tid); |
| } |
| |
| void test_futex_wait_timeout_wake(void) |
| { |
| woken = 1; |
| timeout = K_MSEC(100); |
| |
| atomic_set(&simple_futex.val, 1); |
| |
| k_thread_create(&futex_tid, stack_1, STACK_SIZE, |
| futex_wait_wake_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the futex_wait_wake_task to execute */ |
| k_yield(); |
| |
| k_thread_create(&futex_wake_tid, futex_wake_stack, STACK_SIZE, |
| futex_wake_task, &woken, NULL, NULL, |
| PRIO_WAKE, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* |
| * giving time for the futex_wake_task |
| * and futex_wait_wake_task to execute |
| */ |
| k_yield(); |
| |
| zassert_true(atomic_get(&simple_futex.val) == 0, |
| "wait timeout doesn't wake"); |
| |
| k_thread_abort(&futex_wake_tid); |
| k_thread_abort(&futex_tid); |
| } |
| |
| void test_futex_wait_nowait_wake(void) |
| { |
| woken = 0; |
| timeout = K_NO_WAIT; |
| |
| atomic_set(&simple_futex.val, 1); |
| |
| k_thread_create(&futex_tid, stack_1, STACK_SIZE, |
| futex_wait_wake_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the futex_wait_wake_task to execute */ |
| k_sleep(K_MSEC(100)); |
| |
| k_thread_create(&futex_wake_tid, futex_wake_stack, STACK_SIZE, |
| futex_wake_task, &woken, NULL, NULL, |
| PRIO_WAKE, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the futex_wake_task to execute */ |
| k_yield(); |
| |
| k_thread_abort(&futex_wake_tid); |
| k_thread_abort(&futex_tid); |
| } |
| |
| void test_futex_wait_forever_wake_from_isr(void) |
| { |
| timeout = K_FOREVER; |
| |
| atomic_set(&simple_futex.val, 1); |
| |
| k_thread_create(&futex_tid, stack_1, STACK_SIZE, |
| futex_wait_wake_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the futex_wait_wake_task to execute */ |
| k_yield(); |
| |
| futex_wake_from_isr(&simple_futex); |
| |
| /* giving time for the futex_wait_wake_task to execute */ |
| k_yield(); |
| |
| zassert_true(atomic_get(&simple_futex.val) == 0, |
| "wait forever wake from isr doesn't wake"); |
| |
| k_thread_abort(&futex_tid); |
| } |
| |
| void test_futex_multiple_threads_wait_wake(void) |
| { |
| timeout = K_FOREVER; |
| woken = TOTAL_THREADS_WAITING; |
| |
| atomic_clear(&simple_futex.val); |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| atomic_inc(&simple_futex.val); |
| k_thread_create(&multiple_tid[i], multiple_stack[i], |
| STACK_SIZE, futex_wait_wake_task, |
| &timeout, NULL, NULL, PRIO_WAIT, |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| } |
| |
| /* giving time for the other threads to execute */ |
| k_yield(); |
| |
| k_thread_create(&futex_wake_tid, futex_wake_stack, |
| STACK_SIZE, futex_wake_task, &woken, |
| NULL, NULL, PRIO_WAKE, |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* giving time for the other threads to execute */ |
| k_yield(); |
| |
| zassert_true(atomic_get(&simple_futex.val) == 0, |
| "wait forever wake doesn't wake all threads"); |
| |
| k_thread_abort(&futex_wake_tid); |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| k_thread_abort(&multiple_tid[i]); |
| } |
| } |
| |
| void test_multiple_futex_wait_wake(void) |
| { |
| woken = 1; |
| timeout = K_FOREVER; |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| index[i] = i; |
| atomic_set(&multiple_futex[i].val, 1); |
| k_thread_create(&multiple_tid[i], multiple_stack[i], |
| STACK_SIZE, futex_multiple_wait_wake_task, |
| &timeout, &index[i], NULL, PRIO_WAIT, |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| } |
| |
| /* giving time for the other threads to execute */ |
| k_yield(); |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| k_thread_create(&multiple_wake_tid[i], multiple_wake_stack[i], |
| STACK_SIZE, futex_multiple_wake_task, |
| &woken, &index[i], NULL, PRIO_WAKE, |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| } |
| |
| /* giving time for the other threads to execute */ |
| k_yield(); |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| zassert_true(atomic_get(&multiple_futex[i].val) == 0, |
| "wait forever wake doesn't wake %d thread", i); |
| } |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| k_thread_abort(&multiple_tid[i]); |
| k_thread_abort(&multiple_wake_tid[i]); |
| } |
| } |
| |
| void test_user_futex_bad(void) |
| { |
| int ret; |
| |
| /* Is a futex, but no access to its memory */ |
| ret = k_futex_wait(&no_access_futex, 0, K_NO_WAIT); |
| zassert_equal(ret, -EACCES, "shouldn't have been able to access"); |
| ret = k_futex_wake(&no_access_futex, false); |
| zassert_equal(ret, -EACCES, "shouldn't have been able to access"); |
| |
| /* Access to memory, but not a kernel object */ |
| ret = k_futex_wait((struct k_futex *)¬_a_futex, 0, K_NO_WAIT); |
| zassert_equal(ret, -EINVAL, "waited on non-futex"); |
| ret = k_futex_wake((struct k_futex *)¬_a_futex, false); |
| zassert_equal(ret, -EINVAL, "woke non-futex"); |
| |
| /* Access to memory, but wrong object type */ |
| ret = k_futex_wait((struct k_futex *)&also_not_a_futex, 0, K_NO_WAIT); |
| zassert_equal(ret, -EINVAL, "waited on non-futex"); |
| ret = k_futex_wake((struct k_futex *)&also_not_a_futex, false); |
| zassert_equal(ret, -EINVAL, "woke non-futex"); |
| |
| /* Wait with unexpected value */ |
| atomic_set(&simple_futex.val, 100); |
| ret = k_futex_wait(&simple_futex, 0, K_NO_WAIT); |
| zassert_equal(ret, -EAGAIN, "waited when values did not match"); |
| |
| /* Timeout case */ |
| ret = k_futex_wait(&simple_futex, 100, K_NO_WAIT); |
| zassert_equal(ret, -ETIMEDOUT, "didn't time out"); |
| } |
| |
| /* ztest main entry*/ |
| void test_main(void) |
| { |
| ztest_test_suite(test_futex, |
| ztest_user_unit_test(test_user_futex_bad), |
| ztest_unit_test(test_futex_wait_forever_wake), |
| ztest_unit_test(test_futex_wait_timeout_wake), |
| ztest_unit_test(test_futex_wait_nowait_wake), |
| ztest_unit_test(test_futex_wait_forever_wake_from_isr), |
| ztest_unit_test(test_futex_multiple_threads_wait_wake), |
| ztest_unit_test(test_multiple_futex_wait_wake), |
| ztest_unit_test(test_futex_wait_forever), |
| ztest_unit_test(test_futex_wait_timeout), |
| ztest_unit_test(test_futex_wait_nowait)); |
| ztest_run_test_suite(test_futex); |
| } |
| /******************************************************************************/ |