diff --git a/tests/kernel/msgq/msgq_usage/CMakeLists.txt b/tests/kernel/msgq/msgq_usage/CMakeLists.txt new file mode 100644 index 00000000000..14bb3772e91 --- /dev/null +++ b/tests/kernel/msgq/msgq_usage/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(msgq_usage) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/kernel/msgq/msgq_usage/prj.conf b/tests/kernel/msgq/msgq_usage/prj.conf new file mode 100644 index 00000000000..9467c292689 --- /dev/null +++ b/tests/kernel/msgq/msgq_usage/prj.conf @@ -0,0 +1 @@ +CONFIG_ZTEST=y diff --git a/tests/kernel/msgq/msgq_usage/src/main.c b/tests/kernel/msgq/msgq_usage/src/main.c new file mode 100644 index 00000000000..4ba093daf5a --- /dev/null +++ b/tests/kernel/msgq/msgq_usage/src/main.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2021 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define MSGQ_LEN (2) +#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE) +#define NUM_SERVICES 2 +#define TIMEOUT K_MSEC(100) + +K_MSGQ_DEFINE(manager_q, sizeof(unsigned long) * 2, 4, 4); +struct k_msgq service1_msgq; +struct k_msgq service2_msgq; +struct k_msgq client_msgq; +K_THREAD_STACK_DEFINE(service_manager_stack, STACK_SIZE); +K_THREAD_STACK_DEFINE(service1_stack, STACK_SIZE); +K_THREAD_STACK_DEFINE(service2_stack, STACK_SIZE); +K_THREAD_STACK_DEFINE(client_stack, STACK_SIZE); +K_SEM_DEFINE(service_sema, 2, 2); +K_SEM_DEFINE(test_continue, 0, 1); +struct k_thread service_manager; +struct k_thread service1; +struct k_thread service2; +struct k_thread client; +static ZTEST_DMEM unsigned long __aligned(4) service1_buf[MSGQ_LEN]; +static ZTEST_DMEM unsigned long __aligned(4) service2_buf[MSGQ_LEN]; +static ZTEST_DMEM unsigned long __aligned(4) client_buf[MSGQ_LEN * 2]; +static ZTEST_DMEM struct k_msgq *services[NUM_SERVICES]; +static ZTEST_DMEM struct k_msgq *pclient; +static ZTEST_DMEM bool service1_run; +static ZTEST_DMEM bool service2_run; +static ZTEST_DMEM k_tid_t tservice_manager, tservice1, tservice2, tclient; + +enum message_info { + QUERRY_SERVICE = 1, + REGISTER_SERVICE1, + REGISTER_SERVICE2, + GET_SERVICE, + SERVICE1_RUNNING, + SERVICE2_RUNNING, + SERVICE_QUIT +}; + +static void service_manager_entry(void *p1, void *p2, void *p3) +{ + unsigned long data[2]; + + while (1) { + k_msgq_get(&manager_q, data, K_FOREVER); + switch (data[0]) { + case QUERRY_SERVICE: + pclient = (struct k_msgq *)data[1]; + k_msgq_put(pclient, services, K_NO_WAIT); + break; + case REGISTER_SERVICE1: + services[0] = (struct k_msgq *)data[1]; + break; + case REGISTER_SERVICE2: + services[1] = (struct k_msgq *)data[1]; + break; + case SERVICE_QUIT: + for (int i = 0; i < NUM_SERVICES; i++) { + if (services[i] == (struct k_msgq *)data[1]) { + services[i] = NULL; + } + } + /* wake up threads waiting for this queue */ + k_msgq_purge((struct k_msgq *)data[1]); + break; + default: + TC_PRINT("Unknown message %ld\n", data[0]); + break; + } + k_msleep(10); + } +} + +static void start_service_manager(void) +{ + int pri = k_thread_priority_get(k_current_get()); + + tservice_manager = k_thread_create(&service_manager, + service_manager_stack, STACK_SIZE, + service_manager_entry, NULL, NULL, + NULL, pri, 0, K_NO_WAIT); +} + +static void service1_entry(void *p1, void *p2, void *p3) +{ + unsigned long service_data[2]; + struct k_msgq *client; + int ret; + + service_data[0] = REGISTER_SERVICE1; + service_data[1] = (unsigned long)&service1_msgq; + + k_msgq_init(&service1_msgq, (char *)service1_buf, + sizeof(service1_buf), 1); + ret = k_msgq_put(&manager_q, service_data, K_NO_WAIT); + zassert_equal(ret, 0, "Can't register service"); + + k_sem_take(&service_sema, K_NO_WAIT); + while (service1_run) { + k_msgq_get(&service1_msgq, service_data, K_FOREVER); + if (service_data[0] == GET_SERVICE) { + client = (struct k_msgq *)service_data[1]; + service_data[0] = SERVICE1_RUNNING; + k_msgq_put(client, service_data, K_NO_WAIT); + } + k_msleep(10); + } + + /* inform services manager */ + service_data[0] = SERVICE_QUIT; + service_data[1] = (unsigned long)&service1_msgq; + k_msgq_put(&manager_q, service_data, K_NO_WAIT); +} + +static void service2_entry(void *p1, void *p2, void *p3) +{ + unsigned long service_data[2]; + struct k_msgq *client; + int ret; + + service_data[0] = REGISTER_SERVICE2; + service_data[1] = (unsigned long)&service2_msgq; + + k_msgq_init(&service2_msgq, (char *)service2_buf, + sizeof(service2_buf), 1); + ret = k_msgq_put(&manager_q, service_data, K_NO_WAIT); + zassert_equal(ret, 0, "Can't register service"); + + k_sem_take(&service_sema, K_NO_WAIT); + while (service2_run) { + k_msgq_get(&service2_msgq, service_data, K_FOREVER); + if (service_data[0] == GET_SERVICE) { + client = (struct k_msgq *)service_data[1]; + service_data[0] = SERVICE2_RUNNING; + k_msgq_put(client, service_data, K_NO_WAIT); + } + k_msleep(10); + } + + /* inform services manager */ + service_data[0] = SERVICE_QUIT; + service_data[1] = (unsigned long)&service2_msgq; + k_msgq_put(&manager_q, service_data, K_NO_WAIT); +} + +static void register_service(void) +{ + + int pri = k_thread_priority_get(k_current_get()); + + service1_run = true; + tservice1 = k_thread_create(&service1, service1_stack, STACK_SIZE, + service1_entry, NULL, NULL, NULL, pri, + 0, K_NO_WAIT); + + service2_run = true; + tservice2 = k_thread_create(&service2, service2_stack, STACK_SIZE, + service2_entry, NULL, NULL, NULL, pri, + 0, K_NO_WAIT); +} + +static void client_entry(void *p1, void *p2, void *p3) +{ + unsigned long client_data[2]; + unsigned long service_data[2]; + struct k_msgq *service1q; + struct k_msgq *service2q; + bool query_service = false; + int ret; + + + k_msgq_init(&client_msgq, (char *)client_buf, + sizeof(unsigned long) * 2, 2); + client_data[0] = QUERRY_SERVICE; + client_data[1] = (unsigned long)&client_msgq; + + /* query services */ + k_msgq_put(&manager_q, client_data, K_NO_WAIT); + ret = k_msgq_get(&client_msgq, service_data, K_FOREVER); + zassert_equal(ret, 0, NULL); + + service1q = (struct k_msgq *)service_data[0]; + service2q = (struct k_msgq *)service_data[1]; + /* all services should be running */ + zassert_equal(service1q, &service1_msgq, NULL); + zassert_equal(service2q, &service2_msgq, NULL); + /* let the test thread continue */ + k_sem_give(&test_continue); + + while (1) { + /* service might quit */ + if (query_service) { + client_data[0] = QUERRY_SERVICE; + client_data[1] = (unsigned long)&client_msgq; + k_msgq_put(&manager_q, client_data, K_NO_WAIT); + k_msgq_get(&client_msgq, service_data, K_FOREVER); + service1q = (struct k_msgq *)service_data[0]; + service2q = (struct k_msgq *)service_data[1]; + query_service = false; + } + + if (!service1q && !service2q) { + break; + } + + client_data[0] = GET_SERVICE; + client_data[1] = (unsigned long)&client_msgq; + + if (service1q) { + k_msgq_put(service1q, client_data, K_NO_WAIT); + ret = k_msgq_get(&client_msgq, service_data, TIMEOUT); + if (!ret) { + zassert_equal(service_data[0], + SERVICE1_RUNNING, NULL); + } else { + /* service might down, query in next loop */ + query_service = true; + } + } + + if (service2q) { + k_msgq_put(service2q, client_data, K_NO_WAIT); + ret = k_msgq_get(&client_msgq, service_data, TIMEOUT); + if (!ret) { + zassert_equal(service_data[0], + SERVICE2_RUNNING, NULL); + } else { + query_service = true; + } + } + k_msleep(10); + } +} + +static void start_client(void) +{ + + int pri = k_thread_priority_get(k_current_get()); + + tclient = k_thread_create(&client, client_stack, STACK_SIZE, + client_entry, NULL, NULL, NULL, pri, + 0, K_NO_WAIT); +} + +void test_msgq_usage(void) +{ + start_service_manager(); + register_service(); + start_client(); + /* waiting to continue */ + k_sem_take(&test_continue, K_FOREVER); + + /* rather than schedule this thread by k_msleep(), use semaphor with + * a timeout value, so there is no give operation over service_sema + */ + TC_PRINT("try to kill service1\n"); + k_sem_take(&service_sema, Z_TIMEOUT_MS(500)); + service1_run = false; + + TC_PRINT("try to kill service2\n"); + k_sem_take(&service_sema, Z_TIMEOUT_MS(500)); + service2_run = false; + + k_thread_join(tservice1, K_FOREVER); + k_thread_join(tservice2, K_FOREVER); + k_thread_join(tclient, K_FOREVER); + k_thread_abort(tservice_manager); +} + +void test_main(void) +{ + ztest_test_suite(msgq_usage, ztest_unit_test(test_msgq_usage)); + ztest_run_test_suite(msgq_usage); +} diff --git a/tests/kernel/msgq/msgq_usage/testcase.yaml b/tests/kernel/msgq/msgq_usage/testcase.yaml new file mode 100644 index 00000000000..a991899a6f1 --- /dev/null +++ b/tests/kernel/msgq/msgq_usage/testcase.yaml @@ -0,0 +1,3 @@ +tests: + kernel.message_queue_usage: + tags: kernel