| /* |
| * Copyright (c) 2021 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/ztest.h> |
| #include <zephyr/ztest_error_hook.h> |
| |
| /* Macro declarations */ |
| #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE) |
| #define SEM_INIT_VAL (0U) |
| #define SEM_MAX_VAL (3U) |
| #define TOTAL_MAX (4U) |
| #define STACK_NUMS 5 |
| #define PRIO 5 |
| #define LOW_PRIO 8 |
| #define HIGH_PRIO 2 |
| |
| static K_THREAD_STACK_ARRAY_DEFINE(multi_stack_give, STACK_NUMS, STACK_SIZE); |
| static K_THREAD_STACK_ARRAY_DEFINE(multi_stack_take, STACK_NUMS, STACK_SIZE); |
| |
| static struct k_thread multi_tid_give[STACK_NUMS]; |
| static struct k_thread multi_tid_take[STACK_NUMS]; |
| static struct k_sem usage_sem, sync_sem, limit_sem, uninit_sem; |
| static ZTEST_DMEM int flag; |
| static ZTEST_DMEM atomic_t atomic_count; |
| |
| /** |
| * @defgroup kernel_sys_sem_tests Semaphore |
| * @ingroup all_tests |
| * @{ |
| * @} |
| */ |
| |
| static void sem_thread_give_uninit(void *p1, void *p2, void *p3) |
| { |
| ztest_set_fault_valid(true); |
| |
| /* use sem without initialise */ |
| k_sem_give(&uninit_sem); |
| |
| ztest_test_fail(); |
| } |
| |
| static void sem_thread_give(void *p1, void *p2, void *p3) |
| { |
| flag = 1; |
| k_sem_give(&usage_sem); |
| } |
| |
| static void thread_low_prio_sem_take(void *p1, void *p2, void *p3) |
| { |
| k_sem_take(&usage_sem, K_FOREVER); |
| |
| flag = LOW_PRIO; |
| k_sem_give(&sync_sem); |
| } |
| |
| static void thread_high_prio_sem_take(void *p1, void *p2, void *p3) |
| { |
| k_sem_take(&usage_sem, K_FOREVER); |
| |
| flag = HIGH_PRIO; |
| k_sem_give(&sync_sem); |
| } |
| |
| /** |
| * @brief Test semaphore usage with multiple thread |
| * |
| * @details Using semaphore with some situations |
| * - Use a uninitialized semaphore |
| * - Use semaphore normally |
| * - Use semaphore with different priority threads |
| * |
| * @ingroup kernel_sys_sem_tests |
| */ |
| ZTEST_USER(kernel_sys_sem, test_multiple_thread_sem_usage) |
| { |
| k_sem_init(&usage_sem, SEM_INIT_VAL, SEM_MAX_VAL); |
| k_sem_init(&sync_sem, SEM_INIT_VAL, SEM_MAX_VAL); |
| /* Use a semaphore to synchronize processing between threads */ |
| k_sem_reset(&usage_sem); |
| k_thread_create(&multi_tid_give[0], multi_stack_give[0], STACK_SIZE, |
| sem_thread_give, NULL, NULL, |
| NULL, PRIO, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| k_sem_take(&usage_sem, K_FOREVER); |
| zassert_equal(flag, 1, "value != 1"); |
| zassert_equal(k_sem_count_get(&usage_sem), 0, "sem not be took"); |
| |
| k_sem_reset(&usage_sem); |
| /* Use sem with different priority thread */ |
| k_thread_create(&multi_tid_take[0], multi_stack_take[0], STACK_SIZE, |
| thread_low_prio_sem_take, NULL, NULL, |
| NULL, LOW_PRIO, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| k_thread_create(&multi_tid_take[1], multi_stack_take[1], STACK_SIZE, |
| thread_high_prio_sem_take, NULL, NULL, |
| NULL, HIGH_PRIO, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| k_sleep(K_MSEC(50)); |
| |
| /* Verify if the high prio thread take sem first */ |
| k_sem_give(&usage_sem); |
| k_sem_take(&sync_sem, K_FOREVER); |
| zassert_equal(flag, HIGH_PRIO, "high prio value error"); |
| |
| k_sem_give(&usage_sem); |
| k_sem_take(&sync_sem, K_FOREVER); |
| zassert_equal(flag, LOW_PRIO, "low prio value error"); |
| |
| k_thread_join(&multi_tid_give[0], K_FOREVER); |
| k_thread_join(&multi_tid_take[0], K_FOREVER); |
| k_thread_join(&multi_tid_take[1], K_FOREVER); |
| |
| k_thread_create(&multi_tid_give[1], multi_stack_give[1], STACK_SIZE, |
| sem_thread_give_uninit, NULL, NULL, |
| NULL, PRIO, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| k_sleep(K_MSEC(20)); |
| k_thread_join(&multi_tid_give[1], K_FOREVER); |
| } |
| |
| static void multi_thread_sem_give(void *p1, void *p2, void *p3) |
| { |
| int count; |
| |
| (void)atomic_inc(&atomic_count); |
| count = atomic_get(&atomic_count); |
| k_sem_give(&limit_sem); |
| |
| if (count < TOTAL_MAX) { |
| zassert_equal(k_sem_count_get(&limit_sem), count, "multi get sem error"); |
| } else { |
| zassert_equal(k_sem_count_get(&limit_sem), SEM_MAX_VAL, "count > SEM_MAX_VAL"); |
| } |
| |
| k_sem_take(&sync_sem, K_FOREVER); |
| } |
| |
| static void multi_thread_sem_take(void *p1, void *p2, void *p3) |
| { |
| int count; |
| |
| k_sem_take(&limit_sem, K_FOREVER); |
| (void)atomic_dec(&atomic_count); |
| count = atomic_get(&atomic_count); |
| |
| if (count >= 0) { |
| zassert_equal(k_sem_count_get(&limit_sem), count, "multi take sem error"); |
| } else { |
| zassert_equal(k_sem_count_get(&limit_sem), 0, "count < SEM_INIT_VAL"); |
| } |
| |
| k_sem_give(&sync_sem); |
| } |
| |
| /** |
| * @brief Test max semaphore can be give and take with multiple thread |
| * |
| * @details |
| * - Define and initialize semaphore and thread. |
| * - Give sem by multiple threads. |
| * - Verify more than max count about semaphore can reach. |
| * - Take sem by multiple threads and verify if sem count is correct. |
| * |
| * @ingroup kernel_sys_sem_tests |
| */ |
| ZTEST_USER(kernel_sys_sem, test_multi_thread_sem_limit) |
| { |
| k_sem_init(&limit_sem, SEM_INIT_VAL, SEM_MAX_VAL); |
| k_sem_init(&sync_sem, SEM_INIT_VAL, SEM_MAX_VAL); |
| |
| (void)atomic_set(&atomic_count, 0); |
| for (int i = 1; i <= TOTAL_MAX; i++) { |
| k_thread_create(&multi_tid_give[i], multi_stack_give[i], STACK_SIZE, |
| multi_thread_sem_give, NULL, NULL, NULL, |
| i, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| } |
| |
| k_sleep(K_MSEC(50)); |
| |
| (void)atomic_set(&atomic_count, SEM_MAX_VAL); |
| for (int i = 1; i <= TOTAL_MAX; i++) { |
| k_thread_create(&multi_tid_take[i], multi_stack_take[i], STACK_SIZE, |
| multi_thread_sem_take, NULL, NULL, NULL, |
| PRIO, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| } |
| |
| /* cleanup all threads for the following test cases */ |
| k_sleep(K_MSEC(50)); |
| for (int i = 1; i <= TOTAL_MAX; i++) { |
| k_thread_abort(&multi_tid_give[i]); |
| k_thread_abort(&multi_tid_take[i]); |
| } |
| } |
| |
| void *test_init(void) |
| { |
| k_thread_access_grant(k_current_get(), &usage_sem, &sync_sem, &limit_sem, |
| &multi_tid_give[0], &multi_tid_give[1], |
| &multi_tid_give[2], &multi_tid_give[3], |
| &multi_tid_give[4], &multi_tid_take[4], |
| &multi_tid_take[2], &multi_tid_take[3], |
| &multi_tid_take[0], &multi_tid_take[1], |
| &multi_tid_give[5], &multi_tid_take[5], |
| &multi_stack_take[0], &multi_stack_take[1], |
| &multi_stack_take[3], &multi_stack_take[4], |
| &multi_stack_take[2], &multi_stack_give[0], |
| &multi_stack_give[1], &multi_stack_give[2], |
| &multi_stack_give[3], &multi_stack_give[4]); |
| return NULL; |
| } |
| ZTEST_SUITE(kernel_sys_sem, NULL, test_init, |
| ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL); |