| /* |
| * Copyright (c) 2020 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| |
| #include <zephyr/ztest.h> |
| #include <zephyr/irq_offload.h> |
| #include <zephyr/ztest_error_hook.h> |
| |
| #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE) |
| |
| #define PRIO_WAIT (CONFIG_ZTEST_THREAD_PRIORITY) |
| #define PRIO_WAKE (CONFIG_ZTEST_THREAD_PRIORITY) |
| |
| K_THREAD_STACK_DEFINE(stack_1, STACK_SIZE); |
| K_THREAD_STACK_DEFINE(condvar_wake_stack, STACK_SIZE); |
| |
| struct k_thread condvar_tid; |
| struct k_thread condvar_wake_tid; |
| |
| struct k_condvar simple_condvar; |
| K_MUTEX_DEFINE(test_mutex); |
| |
| #define TOTAL_THREADS_WAITING (3) |
| #define TCOUNT 10 |
| #define COUNT_LIMIT 12 |
| |
| ZTEST_BMEM int woken; |
| ZTEST_BMEM int timeout; |
| ZTEST_BMEM int index[TOTAL_THREADS_WAITING]; |
| ZTEST_BMEM int count; |
| |
| struct k_condvar multiple_condvar[TOTAL_THREADS_WAITING]; |
| |
| struct k_thread multiple_tid[TOTAL_THREADS_WAITING]; |
| struct k_thread multiple_wake_tid[TOTAL_THREADS_WAITING]; |
| 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); |
| |
| |
| /******************************************************************************/ |
| /* Helper functions */ |
| void condvar_isr_wake(const void *condvar) |
| { |
| k_condvar_signal((struct k_condvar *)condvar); |
| } |
| |
| void condvar_wake_from_isr(struct k_condvar *condvar) |
| { |
| irq_offload(condvar_isr_wake, (const void *)condvar); |
| } |
| |
| /* test condvar wait, no condvar wake */ |
| void condvar_wait_task(void *p1, void *p2, void *p3) |
| { |
| int32_t ret_value; |
| k_ticks_t time_val = *(int *)p1; |
| |
| k_condvar_init(&simple_condvar); |
| zassert_true(time_val >= (int)K_TICKS_FOREVER, |
| "invalid timeout parameter"); |
| |
| k_mutex_lock(&test_mutex, K_FOREVER); |
| ret_value = k_condvar_wait(&simple_condvar, &test_mutex, K_TICKS(time_val)); |
| |
| switch (time_val) { |
| case K_TICKS_FOREVER: |
| zassert_true(ret_value == 0, |
| "k_condvar_wait failed."); |
| zassert_false(ret_value == 0, |
| "condvar wait task wakeup."); |
| break; |
| case 0: |
| zassert_true(ret_value == -EAGAIN, |
| "k_condvar_wait failed."); |
| break; |
| default: |
| zassert_true(ret_value == -EAGAIN, |
| "k_condvar_wait failed.: %d", ret_value); |
| break; |
| } |
| |
| k_mutex_unlock(&test_mutex); |
| |
| } |
| |
| void condvar_wake_task(void *p1, void *p2, void *p3) |
| { |
| int32_t ret_value; |
| |
| ret_value = k_condvar_signal(&simple_condvar); |
| zassert_equal(ret_value, 0, |
| "k_condvar_wake failed. (%d!=%d)", ret_value, 0); |
| } |
| |
| void condvar_wake_multiple(void *p1, void *p2, void *p3) |
| { |
| int32_t ret_value; |
| int woken_num = *(int *)p1; |
| |
| ret_value = k_condvar_broadcast(&simple_condvar); |
| zassert_true(ret_value == woken_num, |
| "k_condvar_wake failed. (%d!=%d)", ret_value, woken_num); |
| } |
| |
| void condvar_wait_wake_task(void *p1, void *p2, void *p3) |
| { |
| int32_t ret_value; |
| int time_val = *(int *)p1; |
| |
| zassert_true(time_val >= (int)K_TICKS_FOREVER, "invalid timeout parameter"); |
| k_mutex_lock(&test_mutex, K_FOREVER); |
| ret_value = k_condvar_wait(&simple_condvar, &test_mutex, K_TICKS(time_val)); |
| |
| switch (time_val) { |
| case K_TICKS_FOREVER: |
| zassert_true(ret_value == 0, |
| "k_condvar_wait failed."); |
| break; |
| case 0: |
| zassert_true(ret_value == -EAGAIN, |
| "k_condvar_wait failed."); |
| break; |
| default: |
| zassert_true(ret_value == 0, |
| "k_condvar_wait failed."); |
| break; |
| } |
| |
| k_mutex_unlock(&test_mutex); |
| } |
| |
| /** |
| * @brief Test k_condvar_wait() and k_condvar_wake() |
| */ |
| ZTEST_USER(condvar_tests, test_condvar_wait_forever_wake) |
| { |
| woken = 1; |
| timeout = K_TICKS_FOREVER; |
| |
| k_condvar_init(&simple_condvar); |
| k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| condvar_wait_wake_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* giving time for the condvar_wait_wake_task to execute */ |
| k_yield(); |
| |
| k_thread_create(&condvar_wake_tid, condvar_wake_stack, STACK_SIZE, |
| condvar_wake_task, &woken, NULL, NULL, |
| PRIO_WAKE, K_USER | K_INHERIT_PERMS, K_MSEC(1)); |
| |
| /* giving time for the condvar_wake_task |
| * and condvar_wait_wake_task to execute |
| */ |
| k_yield(); |
| |
| k_thread_abort(&condvar_wake_tid); |
| k_thread_abort(&condvar_tid); |
| } |
| |
| |
| ZTEST_USER(condvar_tests, test_condvar_wait_timeout_wake) |
| { |
| woken = 1; |
| timeout = k_ms_to_ticks_ceil32(100); |
| |
| k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| condvar_wait_wake_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* giving time for the condvar_wait_wake_task to execute */ |
| k_yield(); |
| |
| k_thread_create(&condvar_wake_tid, condvar_wake_stack, STACK_SIZE, |
| condvar_wake_task, &woken, NULL, NULL, |
| PRIO_WAKE, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* |
| * giving time for the condvar_wake_task |
| * and condvar_wait_wake_task to execute |
| */ |
| k_yield(); |
| |
| |
| k_thread_abort(&condvar_wake_tid); |
| k_thread_abort(&condvar_tid); |
| } |
| |
| ZTEST_USER(condvar_tests, test_condvar_wait_timeout) |
| { |
| timeout = k_ms_to_ticks_ceil32(50); |
| |
| k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| condvar_wait_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* giving time for the condvar_wait_task to execute */ |
| k_sleep(K_MSEC(100)); |
| |
| k_thread_abort(&condvar_tid); |
| } |
| |
| |
| /** |
| * @brief Test k_condvar_wait() forever |
| */ |
| ZTEST_USER(condvar_tests, test_condvar_wait_forever) |
| { |
| timeout = K_TICKS_FOREVER; |
| |
| |
| k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| condvar_wait_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* giving time for the condvar_wait_task to execute */ |
| k_yield(); |
| |
| k_thread_abort(&condvar_tid); |
| } |
| |
| |
| ZTEST_USER(condvar_tests, test_condvar_wait_nowait) |
| { |
| timeout = 0; |
| |
| k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| condvar_wait_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* giving time for the condvar_wait_task to execute */ |
| k_sleep(K_MSEC(100)); |
| |
| k_thread_abort(&condvar_tid); |
| } |
| |
| |
| ZTEST_USER(condvar_tests, test_condvar_wait_nowait_wake) |
| { |
| woken = 0; |
| timeout = 0; |
| |
| k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| condvar_wait_wake_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the condvar_wait_wake_task to execute */ |
| k_sleep(K_MSEC(100)); |
| |
| k_thread_create(&condvar_wake_tid, condvar_wake_stack, STACK_SIZE, |
| condvar_wake_task, &woken, NULL, NULL, |
| PRIO_WAKE, K_USER | K_INHERIT_PERMS, |
| K_NO_WAIT); |
| |
| /* giving time for the condvar_wake_task to execute */ |
| k_yield(); |
| |
| k_thread_abort(&condvar_wake_tid); |
| k_thread_abort(&condvar_tid); |
| } |
| |
| |
| ZTEST(condvar_tests, test_condvar_wait_forever_wake_from_isr) |
| { |
| timeout = K_TICKS_FOREVER; |
| |
| k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| condvar_wait_wake_task, &timeout, NULL, NULL, |
| PRIO_WAIT, K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* giving time for the condvar_wait_wake_task to execute */ |
| k_yield(); |
| |
| condvar_wake_from_isr(&simple_condvar); |
| |
| /* giving time for the condvar_wait_wake_task to execute */ |
| k_yield(); |
| |
| k_thread_abort(&condvar_tid); |
| } |
| |
| ZTEST_USER(condvar_tests, test_condvar_multiple_threads_wait_wake) |
| { |
| timeout = K_TICKS_FOREVER; |
| woken = TOTAL_THREADS_WAITING; |
| |
| k_condvar_init(&simple_condvar); |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| |
| k_thread_create(&multiple_tid[i], multiple_stack[i], |
| STACK_SIZE, condvar_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(&condvar_wake_tid, condvar_wake_stack, |
| STACK_SIZE, condvar_wake_multiple, &woken, |
| NULL, NULL, PRIO_WAKE, |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* giving time for the other threads to execute */ |
| k_yield(); |
| |
| k_thread_abort(&condvar_wake_tid); |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| k_thread_abort(&multiple_tid[i]); |
| } |
| } |
| |
| |
| void condvar_multiple_wait_wake_task(void *p1, void *p2, void *p3) |
| { |
| int32_t ret_value; |
| int time_val = *(int *)p1; |
| int idx = *(int *)p2; |
| |
| k_condvar_init(&multiple_condvar[idx]); |
| |
| zassert_true(time_val == (int)K_TICKS_FOREVER, "invalid timeout parameter"); |
| k_mutex_lock(&test_mutex, K_FOREVER); |
| |
| ret_value = k_condvar_wait(&multiple_condvar[idx], |
| &test_mutex, K_TICKS(time_val)); |
| zassert_true(ret_value == 0, "k_condvar_wait failed."); |
| |
| k_mutex_unlock(&test_mutex); |
| } |
| |
| void condvar_multiple_wake_task(void *p1, void *p2, void *p3) |
| { |
| int32_t ret_value; |
| int woken_num = *(int *)p1; |
| int idx = *(int *)p2; |
| |
| zassert_true(woken_num > 0, "invalid woken number"); |
| |
| if (woken > 1) { |
| ret_value = k_condvar_signal(&multiple_condvar[idx]); |
| } else { |
| ret_value = k_condvar_broadcast(&multiple_condvar[idx]); |
| } |
| |
| zassert_true(ret_value == woken_num, "k_condvar_wake failed. (%d!=%d)", |
| ret_value, woken_num); |
| } |
| |
| ZTEST_USER(condvar_tests, test_multiple_condvar_wait_wake) |
| { |
| woken = 1; |
| timeout = K_TICKS_FOREVER; |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| index[i] = i; |
| |
| k_thread_create(&multiple_tid[i], multiple_stack[i], |
| STACK_SIZE, condvar_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_msleep(10); |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| k_thread_create(&multiple_wake_tid[i], multiple_wake_stack[i], |
| STACK_SIZE, condvar_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++) { |
| ; |
| } |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| k_thread_abort(&multiple_tid[i]); |
| k_thread_abort(&multiple_wake_tid[i]); |
| } |
| } |
| |
| #ifdef CONFIG_USERSPACE |
| static void cond_init_null(void *p1, void *p2, void *p3) |
| { |
| ztest_set_fault_valid(true); |
| k_condvar_init(NULL); |
| |
| /* should not go here*/ |
| ztest_test_fail(); |
| } |
| |
| ZTEST_USER(condvar_tests, test_condvar_init_null) |
| { |
| k_tid_t tid = k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| (k_thread_entry_t)cond_init_null, |
| NULL, NULL, NULL, |
| K_PRIO_PREEMPT(0), |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| k_thread_join(tid, K_FOREVER); |
| } |
| #else |
| ZTEST_USER(condvar_tests, test_condvar_init_null) |
| { |
| ztest_test_skip(); |
| } |
| #endif |
| |
| #ifdef CONFIG_USERSPACE |
| static void cond_signal_null(void *p1, void *p2, void *p3) |
| { |
| ztest_set_fault_valid(true); |
| k_condvar_signal(NULL); |
| |
| /* should not go here*/ |
| ztest_test_fail(); |
| } |
| |
| static void cond_broadcast_null(void *p1, void *p2, void *p3) |
| { |
| ztest_set_fault_valid(true); |
| k_condvar_broadcast(NULL); |
| |
| /* should not go here*/ |
| ztest_test_fail(); |
| } |
| |
| static void cond_wait_null(void *p1, void *p2, void *p3) |
| { |
| ztest_set_fault_valid(true); |
| k_condvar_wait(NULL, NULL, K_FOREVER); |
| |
| /* should not go here*/ |
| ztest_test_fail(); |
| } |
| |
| ZTEST_USER(condvar_tests, test_condvar_signal_null) |
| { |
| k_tid_t tid = k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| (k_thread_entry_t)cond_signal_null, |
| NULL, NULL, NULL, |
| K_PRIO_PREEMPT(0), |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| k_thread_join(tid, K_FOREVER); |
| } |
| ZTEST_USER(condvar_tests, test_condvar_broadcast_null) |
| { |
| k_tid_t tid = k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| (k_thread_entry_t)cond_broadcast_null, |
| NULL, NULL, NULL, |
| K_PRIO_PREEMPT(0), |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| k_thread_join(tid, K_FOREVER); |
| } |
| ZTEST_USER(condvar_tests, test_condvar_wait_null) |
| { |
| k_tid_t tid = k_thread_create(&condvar_tid, stack_1, STACK_SIZE, |
| (k_thread_entry_t)cond_wait_null, |
| NULL, NULL, NULL, |
| K_PRIO_PREEMPT(0), |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| k_thread_join(tid, K_FOREVER); |
| } |
| |
| #else |
| ZTEST_USER(condvar_tests, test_condvar_signal_null) |
| { |
| ztest_test_skip(); |
| } |
| ZTEST_USER(condvar_tests, test_condvar_broadcast_null) |
| { |
| ztest_test_skip(); |
| } |
| ZTEST_USER(condvar_tests, test_condvar_wait_null) |
| { |
| ztest_test_skip(); |
| } |
| #endif |
| |
| |
| void inc_count(void *p1, void *p2, void *p3) |
| { |
| int i; |
| long multi = (long)p2; |
| |
| for (i = 0; i < TCOUNT; i++) { |
| k_mutex_lock(&test_mutex, K_FOREVER); |
| count++; |
| |
| if (count == COUNT_LIMIT) { |
| if (multi) { |
| k_condvar_broadcast(&simple_condvar); |
| } else { |
| k_condvar_signal(&simple_condvar); |
| } |
| } |
| |
| k_mutex_unlock(&test_mutex); |
| |
| /* Sleep so threads can alternate on mutex lock */ |
| k_sleep(K_MSEC(50)); |
| } |
| } |
| |
| void watch_count(void *p1, void *p2, void *p3) |
| { |
| long my_id = (long)p1; |
| |
| printk("Starting %s: thread %ld\n", __func__, my_id); |
| |
| k_mutex_lock(&test_mutex, K_FOREVER); |
| while (count < COUNT_LIMIT) { |
| k_condvar_wait(&simple_condvar, &test_mutex, K_FOREVER); |
| } |
| count += 125; |
| k_mutex_unlock(&test_mutex); |
| } |
| |
| void _condvar_usecase(long multi) |
| { |
| long t1 = 1, t2 = 2, t3 = 3; |
| int i; |
| |
| count = 0; |
| |
| /* Reinit mutex to prevent affection from previous testcases */ |
| k_mutex_init(&test_mutex); |
| |
| k_thread_create(&multiple_tid[0], multiple_stack[0], STACK_SIZE, watch_count, |
| INT_TO_POINTER(t1), NULL, NULL, K_PRIO_PREEMPT(10), |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| k_thread_create(&multiple_tid[1], multiple_stack[1], STACK_SIZE, inc_count, |
| INT_TO_POINTER(t2), INT_TO_POINTER(multi), NULL, K_PRIO_PREEMPT(10), |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| k_thread_create(&multiple_tid[2], multiple_stack[2], STACK_SIZE, inc_count, |
| INT_TO_POINTER(t3), INT_TO_POINTER(multi), NULL, K_PRIO_PREEMPT(10), |
| K_USER | K_INHERIT_PERMS, K_NO_WAIT); |
| |
| /* Wait for all threads to complete */ |
| for (i = 0; i < 3; i++) { |
| k_thread_join(&multiple_tid[i], K_FOREVER); |
| } |
| |
| zassert_equal(count, 145, "Count not equal to 145"); |
| |
| } |
| |
| ZTEST_USER(condvar_tests, test_condvar_usecase_signal) |
| { |
| _condvar_usecase(0); |
| } |
| |
| ZTEST_USER(condvar_tests, test_condvar_usecase_broadcast) |
| { |
| _condvar_usecase(1); |
| } |
| |
| /*test case main entry*/ |
| static void *condvar_tests_setup(void) |
| { |
| #ifdef CONFIG_USERSPACE |
| k_thread_access_grant(k_current_get(), &test_mutex, &condvar_tid, &condvar_wake_tid, |
| &simple_condvar, &stack_1, &condvar_wake_stack); |
| |
| for (int i = 0; i < TOTAL_THREADS_WAITING; i++) { |
| k_thread_access_grant(k_current_get(), |
| &multiple_tid[i], |
| &multiple_wake_tid[i], |
| &multiple_stack[i], |
| &multiple_condvar[i], |
| &multiple_wake_stack[i]); |
| } |
| #endif |
| return NULL; |
| } |
| |
| ZTEST_SUITE(condvar_tests, NULL, condvar_tests_setup, NULL, NULL, NULL); |