comparison seobeo/tests/seobeo_worker_test.c @ 250:745fd127b2a1

[seobeo] Add bounded worker interface Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 06:23:37 -0700
parents
children
comparison
equal deleted inserted replaced
249:c5129452493e 250:745fd127b2a1
1 #include "seobeo/seobeo_worker.h"
2
3 #include <assert.h>
4 #include <stdatomic.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7
8 typedef struct {
9 atomic_int executed;
10 atomic_int cleaned;
11 atomic_int entered;
12 atomic_int release;
13 atomic_int cleanup_entered;
14 atomic_int hold_cleanup;
15 } Worker_Test_Context;
16
17 typedef struct {
18 Seobeo_Worker_Pool *p_pool;
19 Seobeo_Worker_Result result;
20 } Pool_Shutdown_Context;
21
22 typedef struct {
23 Seobeo_Worker_Pool *p_pool;
24 atomic_int returned;
25 } Pool_Wait_Context;
26
27 typedef struct {
28 Seobeo_Worker_Pool *p_pool;
29 Seobeo_Worker_Result wait_result;
30 Seobeo_Worker_Result shutdown_result;
31 atomic_int cleaned;
32 } Recursive_Cleanup_Context;
33
34 static void count_task(void *p_context)
35 {
36 Worker_Test_Context *p_test = p_context;
37 atomic_fetch_add(&p_test->executed, 1);
38 }
39
40 static void no_op_task(void *p_context)
41 {
42 (void)p_context;
43 }
44
45 static void blocking_task(void *p_context)
46 {
47 Worker_Test_Context *p_test = p_context;
48 atomic_fetch_add(&p_test->executed, 1);
49 atomic_store(&p_test->entered, 1);
50 while (!atomic_load(&p_test->release))
51 usleep(1000);
52 }
53
54 static void cleanup_task(void *p_context)
55 {
56 Worker_Test_Context *p_test = p_context;
57 if (atomic_load(&p_test->hold_cleanup))
58 {
59 atomic_store(&p_test->cleanup_entered, 1);
60 while (!atomic_load(&p_test->release))
61 usleep(1000);
62 }
63 atomic_fetch_add(&p_test->cleaned, 1);
64 }
65
66 static void shutdown_pool_task(void *p_context)
67 {
68 Pool_Shutdown_Context *p_shutdown = p_context;
69 p_shutdown->result =
70 Seobeo_Worker_Pool_Shutdown(p_shutdown->p_pool, FALSE);
71 }
72
73 static void wait_pool_task(void *p_context)
74 {
75 Pool_Wait_Context *p_wait = p_context;
76 assert(Seobeo_Worker_Pool_Wait(p_wait->p_pool) == SEOBEO_WORKER_OK);
77 atomic_store(&p_wait->returned, 1);
78 }
79
80 static void recursive_pool_cleanup(void *p_context)
81 {
82 Recursive_Cleanup_Context *p_cleanup = p_context;
83 p_cleanup->wait_result =
84 Seobeo_Worker_Pool_Wait(p_cleanup->p_pool);
85 p_cleanup->shutdown_result =
86 Seobeo_Worker_Pool_Shutdown(p_cleanup->p_pool, FALSE);
87 Seobeo_Worker_Pool_Destroy(p_cleanup->p_pool);
88 atomic_store(&p_cleanup->cleaned, 1);
89 }
90
91 static void wait_for_value(atomic_int *p_value, int expected)
92 {
93 for (int i = 0; i < 2000; i++)
94 {
95 if (atomic_load(p_value) == expected)
96 return;
97 usleep(1000);
98 }
99 assert(FALSE && "worker operation timed out");
100 }
101
102 static void test_joinable_thread(void)
103 {
104 Worker_Test_Context context = {0};
105 Seobeo_Thread *p_thread =
106 Seobeo_Thread_Start(count_task, &context, cleanup_task);
107 assert(p_thread);
108 assert(Seobeo_Thread_Join(p_thread) == SEOBEO_WORKER_OK);
109 assert(atomic_load(&context.executed) == 1);
110 assert(atomic_load(&context.cleaned) == 1);
111 }
112
113 static void test_detached_thread(void)
114 {
115 Worker_Test_Context context = {0};
116 assert(Seobeo_Thread_Start_Detached(
117 count_task,
118 &context,
119 cleanup_task) == SEOBEO_WORKER_OK);
120 wait_for_value(&context.cleaned, 1);
121 assert(atomic_load(&context.executed) == 1);
122 }
123
124 static void test_pool_drain(void)
125 {
126 Worker_Test_Context context = {0};
127 Seobeo_Worker_Pool *p_pool =
128 Seobeo_Worker_Pool_Create(2, 8);
129 assert(p_pool);
130 for (int i = 0; i < 8; i++)
131 {
132 assert(Seobeo_Worker_Pool_Submit(
133 p_pool,
134 count_task,
135 &context,
136 cleanup_task) == SEOBEO_WORKER_OK);
137 }
138 assert(Seobeo_Worker_Pool_Wait(p_pool) == SEOBEO_WORKER_OK);
139 assert(atomic_load(&context.executed) == 8);
140 assert(atomic_load(&context.cleaned) == 8);
141 assert(Seobeo_Worker_Pool_Pending(p_pool) == 0);
142 assert(Seobeo_Worker_Pool_Active(p_pool) == 0);
143 assert(Seobeo_Worker_Pool_Shutdown(p_pool, TRUE) == SEOBEO_WORKER_OK);
144 assert(Seobeo_Worker_Pool_Submit(
145 p_pool,
146 count_task,
147 &context,
148 cleanup_task) == SEOBEO_WORKER_STOPPED);
149 Seobeo_Worker_Pool_Destroy(p_pool);
150 }
151
152 static void test_pool_queue_limit(void)
153 {
154 Worker_Test_Context context = {0};
155 Seobeo_Worker_Pool *p_pool =
156 Seobeo_Worker_Pool_Create(1, 1);
157 assert(p_pool);
158 assert(Seobeo_Worker_Pool_Submit(
159 p_pool,
160 blocking_task,
161 &context,
162 cleanup_task) == SEOBEO_WORKER_OK);
163 wait_for_value(&context.entered, 1);
164 assert(Seobeo_Worker_Pool_Submit(
165 p_pool,
166 count_task,
167 &context,
168 cleanup_task) == SEOBEO_WORKER_OK);
169 assert(Seobeo_Worker_Pool_Submit(
170 p_pool,
171 count_task,
172 &context,
173 cleanup_task) == SEOBEO_WORKER_QUEUE_FULL);
174 atomic_store(&context.release, 1);
175 assert(Seobeo_Worker_Pool_Wait(p_pool) == SEOBEO_WORKER_OK);
176 assert(atomic_load(&context.executed) == 2);
177 assert(atomic_load(&context.cleaned) == 2);
178 Seobeo_Worker_Pool_Destroy(p_pool);
179 }
180
181 static void test_pool_cancel_queue(void)
182 {
183 Worker_Test_Context context = {0};
184 Seobeo_Worker_Pool *p_pool =
185 Seobeo_Worker_Pool_Create(1, 4);
186 assert(p_pool);
187 assert(Seobeo_Worker_Pool_Submit(
188 p_pool,
189 blocking_task,
190 &context,
191 cleanup_task) == SEOBEO_WORKER_OK);
192 wait_for_value(&context.entered, 1);
193 assert(Seobeo_Worker_Pool_Submit(
194 p_pool,
195 count_task,
196 &context,
197 cleanup_task) == SEOBEO_WORKER_OK);
198 Recursive_Cleanup_Context recursive = {
199 .p_pool = p_pool,
200 .wait_result = SEOBEO_WORKER_OK,
201 .shutdown_result = SEOBEO_WORKER_OK,
202 };
203 assert(Seobeo_Worker_Pool_Submit(
204 p_pool,
205 no_op_task,
206 &recursive,
207 recursive_pool_cleanup) == SEOBEO_WORKER_OK);
208
209 atomic_store(&context.hold_cleanup, 1);
210 Pool_Shutdown_Context shutdown = {
211 .p_pool = p_pool,
212 .result = SEOBEO_WORKER_THREAD_ERROR,
213 };
214 Seobeo_Thread *p_shutdown = Seobeo_Thread_Start(
215 shutdown_pool_task,
216 &shutdown,
217 NULL);
218 assert(p_shutdown);
219 wait_for_value(&context.cleanup_entered, 1);
220
221 Pool_Wait_Context wait = {
222 .p_pool = p_pool,
223 };
224 Seobeo_Thread *p_wait = Seobeo_Thread_Start(
225 wait_pool_task,
226 &wait,
227 NULL);
228 assert(p_wait);
229 usleep(20000);
230 assert(atomic_load(&wait.returned) == 0);
231
232 atomic_store(&context.release, 1);
233 assert(Seobeo_Thread_Join(p_shutdown) == SEOBEO_WORKER_OK);
234 assert(Seobeo_Thread_Join(p_wait) == SEOBEO_WORKER_OK);
235 assert(shutdown.result == SEOBEO_WORKER_OK);
236 assert(atomic_load(&context.executed) == 1);
237 assert(atomic_load(&context.cleaned) == 2);
238 assert(atomic_load(&recursive.cleaned) == 1);
239 assert(recursive.wait_result == SEOBEO_WORKER_INVALID_ARGUMENT);
240 assert(recursive.shutdown_result == SEOBEO_WORKER_INVALID_ARGUMENT);
241 Seobeo_Worker_Pool_Destroy(p_pool);
242 }
243
244 int main(void)
245 {
246 assert(Seobeo_Thread_Current_Id() != 0);
247 assert(Seobeo_Thread_Start(NULL, NULL, NULL) == NULL);
248 assert(Seobeo_Thread_Start_Detached(
249 NULL,
250 NULL,
251 NULL) == SEOBEO_WORKER_INVALID_ARGUMENT);
252 assert(Seobeo_Worker_Pool_Create(0, 1) == NULL);
253 assert(Seobeo_Worker_Pool_Create(1, 0) == NULL);
254
255 test_joinable_thread();
256 test_detached_thread();
257 test_pool_drain();
258 test_pool_queue_limit();
259 test_pool_cancel_queue();
260 return 0;
261 }