GCC Code Coverage Report


Directory: src/gate/
File: src/gate/runnables.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 151 199 75.9%
Functions: 18 24 75.0%
Branches: 26 52 50.0%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/runnables.h"
30 #include "gate/results.h"
31
32 22 static gate_size_t align_param_size(gate_size_t native_size)
33 {
34 22 gate_size_t diff = native_size % sizeof(gate_c_maxalign_t);
35
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (diff != 0)
36 {
37 22 native_size += (sizeof(gate_c_maxalign_t) - diff);
38 }
39 22 return native_size;
40 }
41
42 4 static void gate_runnable_count_sizes(gate_size_t* ptr_param_count, gate_size_t* ptr_param_sizes, gate_va_list_carrier_t* carrier)
43 {
44 void* ptr_param;
45 gate_size_t param_size;
46
47 4 *ptr_param_count = 0;
48 4 *ptr_param_sizes = 0;
49
50 for (;;)
51 {
52 14 ptr_param = va_arg(carrier->data, void*);
53
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 if (!ptr_param)
54 {
55 4 break;
56 }
57 10 param_size = va_arg(carrier->data, size_t);
58 10 param_size = align_param_size(param_size);
59 10 ++(*ptr_param_count);
60 10 *ptr_param_sizes += param_size;
61 }
62 4 }
63
64 4 static void gate_runnable_copy_params(void** ptr_target_param, void* ptr_target_content, gate_va_list_carrier_t* carrier)
65 {
66 4 char* ptr = (char*)ptr_target_content;
67 void* src_param_ptr;
68 size_t src_param_size;
69
70 for (;;)
71 {
72 14 src_param_ptr = va_arg(carrier->data, void*);
73
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 if (!src_param_ptr)
74 {
75 4 break;
76 }
77 10 src_param_size = va_arg(carrier->data, size_t);
78
79 10 gate_mem_copy((void*)ptr, src_param_ptr, src_param_size);
80
81 10 *ptr_target_param = (void*)ptr;
82 10 ++ptr_target_param;
83
84 10 src_param_size = align_param_size(src_param_size);
85
86 10 ptr += src_param_size;
87 }
88 4 }
89
90 typedef struct gate_runner_disp_impl
91 {
92 GATE_INTERFACE_VTBL(gate_runnable) const* vtbl;
93
94 gate_atomic_int_t ref_counter;
95 gate_runnable_storage_t store;
96 } gate_runner_disp_t;
97
98 static char const* disp_get_interface_name(void* obj)
99 {
100 (void)obj;
101 return GATE_INTERFACE_NAME_RUNNABLE;
102 }
103 2 static void disp_release(void* obj)
104 {
105 2 gate_runner_disp_t* disp = (gate_runner_disp_t*)obj;
106
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (gate_atomic_int_dec(&disp->ref_counter) == 0)
107 {
108 2 gate_mem_dealloc(disp);
109 }
110 2 }
111 static int disp_retain(void* obj)
112 {
113 gate_runner_disp_t* disp = (gate_runner_disp_t*)obj;
114 return gate_atomic_int_inc(&disp->ref_counter);
115 }
116
117 2 static gate_result_t disp_run(void* obj)
118 {
119 2 gate_runner_disp_t* disp = (gate_runner_disp_t*)obj;
120 2 gate_result_t result = disp->store.dispatcher(&disp->store);
121 2 return result;
122 }
123
124 static GATE_INTERFACE_VTBL(gate_runnable) gate_runnable_disp_vtbl;
125 2 static void gate_init_runnable_disp_vtbl()
126 {
127
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!gate_runnable_disp_vtbl.get_interface_name)
128 {
129 GATE_INTERFACE_VTBL(gate_runnable) const local_vtbl =
130 {
131 &disp_get_interface_name,
132 &disp_release,
133 &disp_retain,
134 &disp_run
135 };
136 1 gate_runnable_disp_vtbl = local_vtbl;
137 }
138 2 }
139
140 2 gate_runnable_t* gate_runnable_dispatcher_create(void* ptr_target_func, gate_size_t func_size,
141 gate_runnable_dispatcher_t dispatcher,
142 ...)
143 {
144 2 gate_runner_disp_t* ret = NULL;
145 gate_size_t param_count;
146 gate_size_t param_size;
147 gate_va_list_carrier_t carrier;
148 gate_size_t total_size;
149
150 do
151 {
152 2 va_start(carrier.data, dispatcher);
153 2 gate_runnable_count_sizes(&param_count, &param_size, &carrier);
154 2 va_end(carrier.data);
155
156 2 total_size = sizeof(gate_runner_disp_t) + sizeof(void*) * param_count + param_size;
157 2 ret = (gate_runner_disp_t*)gate_mem_alloc(total_size);
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret == NULL)
159 {
160 break;
161 }
162 2 gate_mem_clear(ret, total_size);
163
164 2 gate_init_runnable_disp_vtbl();
165 2 ret->vtbl = &gate_runnable_disp_vtbl;
166 2 gate_atomic_int_init(&ret->ref_counter, 1);
167
168 2 gate_mem_copy(&ret->store.function_storage, &ptr_target_func, func_size);
169 2 ret->store.dispatcher = dispatcher;
170
171 2 va_start(carrier.data, dispatcher);
172 2 gate_runnable_copy_params(&ret->store.parameters.params[0], &ret->store.parameters.params[param_count], &carrier);
173 2 va_end(carrier.data);
174
175 } while (0);
176
177 2 return (gate_runnable_t*)ret;
178 }
179
180
181
182
183
184
185
186 typedef struct gate_taskimpl_class
187 {
188 GATE_INTERFACE_VTBL(gate_task) const* vtbl;
189
190 gate_atomic_int_t ref_counter;
191 gate_atomic_int_t status;
192 gate_result_t task_result;
193 gate_syncevent_t signal;
194 gate_task_storage_t store;
195 } gate_taskimpl_t;
196
197 static char const* gate_taskimpl_get_interface_name(void* obj)
198 {
199 GATE_UNUSED_ARG(obj);
200 return GATE_INTERFACE_NAME_TASK;
201 }
202
203 2 static void gate_taskimpl_destroy(gate_taskimpl_t* impl)
204 {
205 /* TODO */
206 2 gate_syncevent_destroy(&impl->signal);
207 2 gate_mem_dealloc(impl);
208 2 }
209
210 2 static void gate_taskimpl_release(void* obj)
211 {
212 2 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
213
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (obj)
214 {
215
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (0 == gate_atomic_int_dec(&impl->ref_counter))
216 {
217 2 gate_taskimpl_destroy(impl);
218 }
219 }
220 2 }
221 static int gate_taskimpl_retain(void* obj)
222 {
223 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
224 if (impl)
225 {
226 return gate_atomic_int_inc(&impl->ref_counter);
227 }
228 return 0;
229 }
230 2 static gate_bool_t gate_taskimpl_update_state(gate_taskimpl_t* impl, int from_state, int to_state)
231 {
232 2 gate_int32_t old_state = gate_atomic_int_xchg_if(&impl->status, from_state, to_state);
233 2 return old_state == from_state;
234 }
235
236 1 static gate_result_t gate_taskimpl_run(void* obj)
237 {
238 1 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
239 1 gate_result_t ret = GATE_RESULT_FAILED;
240 gate_int32_t old_state;
241 gate_int32_t target_state;
242
243 do
244 {
245
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!gate_taskimpl_update_state(impl, GATE_TASK_STATUS_PREPARED, GATE_TASK_STATUS_RUNNING))
246 {
247 ret = GATE_RESULT_INVALIDSTATE;
248 break;
249 }
250
251 1 impl->task_result = impl->store.dispatcher(&impl->store);
252
253
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 target_state = GATE_SUCCEEDED(impl->task_result) ? GATE_TASK_STATUS_COMPLETED : GATE_TASK_STATUS_FAILED;
254 1 old_state = gate_atomic_int_xchg_if(&impl->status, GATE_TASK_STATUS_RUNNING, target_state);
255
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_TASK_STATUS_RUNNING == old_state)
256 {
257 1 gate_syncevent_set(&impl->signal);
258 1 ret = GATE_RESULT_OK;
259 }
260 else if (GATE_TASK_STATUS_CANCELED == old_state)
261 {
262 ret = GATE_RESULT_CANCELED;
263 }
264 else
265 {
266 ret = GATE_RESULT_INVALIDSTATE;
267 }
268
269 } while (0);
270
271 1 return ret;
272 }
273
274 1 static gate_result_t gate_taskimpl_cancel(void* obj)
275 {
276 1 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
277
278
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (gate_taskimpl_update_state(impl, GATE_TASK_STATUS_PREPARED, GATE_TASK_STATUS_CANCELED))
279 {
280 1 gate_syncevent_set(&impl->signal);
281 1 return GATE_RESULT_OK;
282 }
283
284 if (gate_taskimpl_update_state(impl, GATE_TASK_STATUS_RUNNING, GATE_TASK_STATUS_CANCELED))
285 {
286 gate_syncevent_set(&impl->signal);
287 return GATE_RESULT_OK;
288 }
289
290 return GATE_RESULT_INVALIDSTATE;
291 }
292 3 static int gate_taskimpl_get_status(void* obj)
293 {
294 3 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
295 3 return gate_atomic_int_get(&impl->status);
296 }
297 3 static gate_bool_t gate_taskimpl_completed(void* obj)
298 {
299 3 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
300 3 int state = gate_atomic_int_get(&impl->status);
301
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 switch (state)
302 {
303 2 case GATE_TASK_STATUS_COMPLETED:
304 case GATE_TASK_STATUS_CANCELED:
305 case GATE_TASK_STATUS_FAILED:
306 2 return true;
307 }
308 1 return false;
309 }
310 2 static gate_result_t gate_taskimpl_get_result(void* obj, gate_result_t* ptr_task_result, void const** ptr_task_data)
311 {
312 2 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
313 gate_result_t task_result;
314 2 void const* task_data = NULL;
315 2 int state = gate_atomic_int_get(&impl->status);
316
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 switch (state)
317 {
318 case GATE_TASK_STATUS_PREPARED:
319 case GATE_TASK_STATUS_RUNNING:
320 task_result = GATE_RESULT_INVALIDSTATE;
321 break;
322 1 case GATE_TASK_STATUS_COMPLETED:
323 case GATE_TASK_STATUS_FAILED:
324 1 task_result = impl->task_result;
325 1 task_data = impl->store.parameters.params[0];
326 1 break;
327 1 case GATE_TASK_STATUS_CANCELED:
328 1 task_result = GATE_RESULT_CANCELED;
329 1 break;
330 default:
331 return GATE_RESULT_INVALIDSTATE;
332 }
333
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ptr_task_result)
334 {
335 2 *ptr_task_result = task_result;
336 }
337
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ptr_task_data)
338 {
339 2 *ptr_task_data = task_data;
340 }
341 2 return GATE_RESULT_OK;
342 }
343 static gate_result_t gate_taskimpl_wait(void* obj)
344 {
345 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
346 return gate_syncevent_wait(&impl->signal);
347 }
348 3 static gate_result_t gate_taskimpl_timed_wait(void* obj, gate_uint32_t timeout_ms)
349 {
350 3 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
351 3 return gate_syncevent_timed_wait(&impl->signal, timeout_ms);
352 }
353
354 static GATE_INTERFACE_VTBL(gate_task) gate_taskimpl_vtbl;
355
356 2 void gate_init_taskimpl_vtbl()
357 {
358
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!gate_taskimpl_vtbl.get_interface_name)
359 {
360 GATE_INTERFACE_VTBL(gate_task) const local_vtbl =
361 {
362 &gate_taskimpl_get_interface_name,
363 &gate_taskimpl_release,
364 &gate_taskimpl_retain,
365
366 &gate_taskimpl_run,
367
368 &gate_taskimpl_cancel,
369 &gate_taskimpl_get_status,
370 &gate_taskimpl_completed,
371 &gate_taskimpl_get_result,
372 &gate_taskimpl_wait,
373 &gate_taskimpl_timed_wait
374 };
375 1 gate_taskimpl_vtbl = local_vtbl;
376 }
377 2 }
378
379 gate_task_t* create_task()
380 {
381 gate_task_t* ret = NULL;
382 gate_taskimpl_t* impl = NULL;
383
384 do
385 {
386 gate_init_taskimpl_vtbl();
387 impl = (gate_taskimpl_t*)gate_mem_alloc(sizeof(gate_taskimpl_t));
388 if (!impl)
389 {
390 break;
391 }
392 impl->vtbl = &gate_taskimpl_vtbl;
393 gate_atomic_int_init(&impl->ref_counter, 1);
394 gate_atomic_int_init(&impl->status, GATE_TASK_STATUS_PREPARED);
395
396 ret = (gate_task_t*)impl;
397 impl = NULL;
398 } while (0);
399
400 if (impl)
401 {
402 gate_taskimpl_destroy(impl);
403 }
404
405 return ret;
406 }
407
408
409 2 gate_task_t* gate_task_dispatcher_create(void* func_ptr, gate_size_t func_ptr_size,
410 gate_task_dispatcher_t dispatcher,
411 gate_size_t ret_type_size,
412 ...)
413 {
414 2 gate_task_t* ret = NULL;
415 2 gate_taskimpl_t* impl = NULL;
416
417 gate_size_t param_count;
418 gate_size_t param_size;
419 gate_va_list_carrier_t carrier;
420 gate_size_t total_size;
421 gate_result_t result;
422 char* result_address;
423 char* param_store_address;
424
425 do
426 {
427 2 va_start(carrier.data, ret_type_size);
428 2 gate_runnable_count_sizes(&param_count, &param_size, &carrier);
429 2 va_end(carrier.data);
430
431 2 ret_type_size = align_param_size(ret_type_size);
432 2 total_size = sizeof(gate_taskimpl_t) + sizeof(void*) * (param_count + 1) + ret_type_size + param_size;
433 2 impl = (gate_taskimpl_t*)gate_mem_alloc(total_size);
434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (impl == NULL)
435 {
436 break;
437 }
438 2 gate_mem_clear(impl, total_size);
439
440 2 gate_init_taskimpl_vtbl();
441
442 2 impl->vtbl = &gate_taskimpl_vtbl;
443 2 gate_atomic_int_init(&impl->ref_counter, 1);
444 2 gate_atomic_int_init(&impl->status, GATE_TASK_STATUS_PREPARED);
445
446 2 result = gate_syncevent_create(&impl->signal, false);
447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
448 {
449 break;
450 }
451
452 2 gate_mem_copy(&impl->store.function_storage, &func_ptr, func_ptr_size);
453 2 impl->store.dispatcher = dispatcher;
454
455 2 result_address = (char*)&impl->store.parameters.params[param_count + 1];
456 2 param_store_address = result_address + ret_type_size;
457 2 impl->store.parameters.params[0] = result_address;
458
459 2 va_start(carrier.data, ret_type_size);
460 2 gate_runnable_copy_params(&impl->store.parameters.params[1], param_store_address, &carrier);
461 2 va_end(carrier.data);
462
463 2 ret = (gate_task_t*)impl;
464 2 impl = NULL;
465
466 } while (0);
467
468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (impl != NULL)
469 {
470 gate_taskimpl_destroy(impl);
471 }
472
473 2 return ret;
474 }
475
476