1 /*
2  * Copyright (c) 2024 Måns Ansgariusson <mansgariusson@gmail.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/ztest.h>
9 
10 #define NUM_TEST_ITEMS 10
11 /* In fact, each work item could take up to this value */
12 #define WORK_ITEM_WAIT_ALIGNED                                                                     \
13 	k_ticks_to_ms_floor64(k_ms_to_ticks_ceil32(CONFIG_TEST_WORK_ITEM_WAIT_MS) + _TICK_ALIGN)
14 #define CHECK_WAIT ((NUM_TEST_ITEMS + 1) * WORK_ITEM_WAIT_ALIGNED)
15 
16 static K_THREAD_STACK_DEFINE(work_q_stack, 1024 + CONFIG_TEST_EXTRA_STACK_SIZE);
17 
work_handler(struct k_work * work)18 static void work_handler(struct k_work *work)
19 {
20 	ARG_UNUSED(work);
21 	k_msleep(CONFIG_TEST_WORK_ITEM_WAIT_MS);
22 }
23 
ZTEST(workqueue_api,test_k_work_queue_start_stop)24 ZTEST(workqueue_api, test_k_work_queue_start_stop)
25 {
26 	size_t i;
27 	struct k_work work;
28 	struct k_work_q work_q = {0};
29 	struct k_work works[NUM_TEST_ITEMS];
30 	struct k_work_queue_config cfg = {
31 		.name = "test_work_q",
32 		.no_yield = true,
33 	};
34 
35 	zassert_equal(k_work_queue_stop(&work_q, K_FOREVER), -EALREADY,
36 		      "Succeeded to stop work queue on non-initialized work queue");
37 	k_work_queue_start(&work_q, work_q_stack, K_THREAD_STACK_SIZEOF(work_q_stack),
38 			   K_PRIO_PREEMPT(4), &cfg);
39 
40 	for (i = 0; i < NUM_TEST_ITEMS; i++) {
41 		k_work_init(&works[i], work_handler);
42 		zassert_equal(k_work_submit_to_queue(&work_q, &works[i]), 1,
43 			      "Failed to submit work item");
44 	}
45 
46 	/* Wait for the work item to complete */
47 	k_sleep(K_MSEC(CHECK_WAIT));
48 
49 	zassert_equal(k_work_queue_stop(&work_q, K_FOREVER), -EBUSY,
50 		      "Succeeded to stop work queue while it is running & not plugged");
51 	zassert_true(k_work_queue_drain(&work_q, true) >= 0, "Failed to drain & plug work queue");
52 	zassert_ok(k_work_queue_stop(&work_q, K_FOREVER), "Failed to stop work queue");
53 
54 	k_work_init(&work, work_handler);
55 	zassert_equal(k_work_submit_to_queue(&work_q, &work), -ENODEV,
56 		      "Succeeded to submit work item to non-initialized work queue");
57 }
58 
ZTEST(workqueue_api,test_k_work_queue_stop_sys_thread)59 ZTEST(workqueue_api, test_k_work_queue_stop_sys_thread)
60 {
61 	struct k_work_q work_q = {0};
62 	struct k_work_queue_config cfg = {
63 		.name = "test_work_q",
64 		.no_yield = true,
65 		.essential = true,
66 	};
67 
68 	k_work_queue_start(&work_q, work_q_stack, K_THREAD_STACK_SIZEOF(work_q_stack),
69 			   K_PRIO_PREEMPT(4), &cfg);
70 
71 	zassert_true(k_work_queue_drain(&work_q, true) >= 0, "Failed to drain & plug work queue");
72 	zassert_equal(-ENOTSUP, k_work_queue_stop(&work_q, K_FOREVER), "Failed to stop work queue");
73 }
74 
75 
76 #define STACK_SIZE (1024 + CONFIG_TEST_EXTRA_STACK_SIZE)
77 
78 static K_THREAD_STACK_DEFINE(run_stack, STACK_SIZE);
79 
run_q_main(void * workq_ptr,void * sem_ptr,void * p3)80 static void run_q_main(void *workq_ptr, void *sem_ptr, void *p3)
81 {
82 	ARG_UNUSED(p3);
83 
84 	struct k_work_q *queue = (struct k_work_q *)workq_ptr;
85 	struct k_sem *sem = (struct k_sem *)sem_ptr;
86 
87 	struct k_work_queue_config cfg = {
88 		.name = "wq.run_q",
89 		.no_yield = true,
90 	};
91 
92 	k_work_queue_run(queue, &cfg);
93 
94 	k_sem_give(sem);
95 }
96 
ZTEST(workqueue_api,test_k_work_queue_run_stop)97 ZTEST(workqueue_api, test_k_work_queue_run_stop)
98 {
99 	int rc;
100 	size_t i;
101 	struct k_thread thread;
102 	struct k_work work;
103 	struct k_work_q work_q = {0};
104 	struct k_work works[NUM_TEST_ITEMS];
105 	struct k_sem ret_sem;
106 
107 	k_sem_init(&ret_sem, 0, 1);
108 
109 	(void)k_thread_create(&thread, run_stack, STACK_SIZE, run_q_main, &work_q, &ret_sem, NULL,
110 			      K_PRIO_COOP(3), 0, K_FOREVER);
111 
112 	k_thread_start(&thread);
113 
114 	k_sleep(K_MSEC(CHECK_WAIT));
115 
116 	for (i = 0; i < NUM_TEST_ITEMS; i++) {
117 		k_work_init(&works[i], work_handler);
118 		zassert_equal(k_work_submit_to_queue(&work_q, &works[i]), 1,
119 			      "Failed to submit work item");
120 	}
121 
122 	/* Wait for the work item to complete */
123 	k_sleep(K_MSEC(CHECK_WAIT));
124 
125 	zassert_equal(k_work_queue_stop(&work_q, K_FOREVER), -EBUSY,
126 		      "Succeeded to stop work queue while it is running & not plugged");
127 	zassert_true(k_work_queue_drain(&work_q, true) >= 0, "Failed to drain & plug work queue");
128 	zassert_ok(k_work_queue_stop(&work_q, K_FOREVER), "Failed to stop work queue");
129 
130 	k_work_init(&work, work_handler);
131 	zassert_equal(k_work_submit_to_queue(&work_q, &work), -ENODEV,
132 		      "Succeeded to submit work item to non-initialized work queue");
133 
134 	/* Take the semaphore the other thread released once done running the queue */
135 	rc = k_sem_take(&ret_sem, K_MSEC(1));
136 	zassert_equal(rc, 0);
137 }
138 
139 ZTEST_SUITE(workqueue_api, NULL, NULL, NULL, NULL, NULL);
140