GCC Code Coverage Report


Directory: src/gate/
File: src/gate/runnables.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 153 201 76.1%
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
45 4 *ptr_param_count = 0;
46 4 *ptr_param_sizes = 0;
47
48 for (;;)
49 10 {
50 gate_size_t param_size;
51 14 void* ptr_param = va_arg(carrier->data, void*);
52
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 if (!ptr_param)
53 {
54 4 break;
55 }
56 10 param_size = va_arg(carrier->data, size_t);
57 10 param_size = align_param_size(param_size);
58 10 ++(*ptr_param_count);
59 10 *ptr_param_sizes += param_size;
60 }
61 4 }
62
63 4 static void gate_runnable_copy_params(void** ptr_target_param, void* ptr_target_content, gate_va_list_carrier_t* carrier)
64 {
65 4 char* ptr = (char*)ptr_target_content;
66
67 for (;;)
68 10 {
69 size_t src_param_size;
70 14 void* src_param_ptr = va_arg(carrier->data, void*);
71
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 if (!src_param_ptr)
72 {
73 4 break;
74 }
75 10 src_param_size = va_arg(carrier->data, size_t);
76
77 10 gate_mem_copy((void*)ptr, src_param_ptr, src_param_size);
78
79 10 *ptr_target_param = (void*)ptr;
80 10 ++ptr_target_param;
81
82 10 src_param_size = align_param_size(src_param_size);
83
84 10 ptr += src_param_size;
85 }
86 4 }
87
88 typedef struct gate_runner_disp_impl
89 {
90 GATE_INTERFACE_VTBL(gate_runnable) const* vtbl;
91
92 gate_atomic_int_t ref_counter;
93 gate_runnable_storage_t store;
94 } gate_runner_disp_t;
95
96 static char const* disp_get_interface_name(void* obj)
97 {
98 (void)obj;
99 return GATE_INTERFACE_NAME_RUNNABLE;
100 }
101 2 static void disp_release(void* obj)
102 {
103 2 gate_runner_disp_t* disp = (gate_runner_disp_t*)obj;
104
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (gate_atomic_int_dec(&disp->ref_counter) == 0)
105 {
106 2 gate_mem_dealloc(disp);
107 }
108 2 }
109 static int disp_retain(void* obj)
110 {
111 gate_runner_disp_t* disp = (gate_runner_disp_t*)obj;
112 return gate_atomic_int_inc(&disp->ref_counter);
113 }
114
115 2 static gate_result_t disp_run(void* obj)
116 {
117 2 gate_runner_disp_t* disp = (gate_runner_disp_t*)obj;
118 2 gate_result_t result = disp->store.dispatcher(&disp->store);
119 2 return result;
120 }
121
122 static GATE_INTERFACE_VTBL(gate_runnable) gate_runnable_disp_vtbl;
123 2 static void gate_init_runnable_disp_vtbl()
124 {
125
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!gate_runnable_disp_vtbl.get_interface_name)
126 {
127 GATE_INTERFACE_VTBL(gate_runnable) const local_vtbl =
128 {
129 &disp_get_interface_name,
130 &disp_release,
131 &disp_retain,
132 &disp_run
133 };
134 1 gate_runnable_disp_vtbl = local_vtbl;
135 }
136 2 }
137
138 2 gate_runnable_t* gate_runnable_dispatcher_create(void* ptr_target_func, gate_size_t func_size,
139 gate_runnable_dispatcher_t dispatcher,
140 ...)
141 {
142 2 gate_runner_disp_t* ret = NULL;
143
144 do
145 {
146 gate_size_t param_count;
147 gate_size_t param_size;
148 gate_va_list_carrier_t carrier;
149 gate_size_t total_size;
150
151 2 va_start(carrier.data, dispatcher);
152 2 gate_runnable_count_sizes(&param_count, &param_size, &carrier);
153 2 va_end(carrier.data);
154
155 2 total_size = sizeof(gate_runner_disp_t) + sizeof(void*) * param_count + param_size;
156 2 ret = (gate_runner_disp_t*)gate_mem_alloc(total_size);
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret == NULL)
158 {
159 break;
160 }
161 2 gate_mem_clear(ret, total_size);
162
163 2 gate_init_runnable_disp_vtbl();
164 2 ret->vtbl = &gate_runnable_disp_vtbl;
165 2 gate_atomic_int_init(&ret->ref_counter, 1);
166
167 2 gate_mem_copy(&ret->store.function_storage, &ptr_target_func, func_size);
168 2 ret->store.dispatcher = dispatcher;
169
170 2 va_start(carrier.data, dispatcher);
171 2 gate_runnable_copy_params(&ret->store.parameters.params[0], &ret->store.parameters.params[param_count], &carrier);
172 2 va_end(carrier.data);
173
174 } while (0);
175
176 2 return (gate_runnable_t*)ret;
177 }
178
179
180
181
182
183
184
185 typedef struct gate_taskimpl_class
186 {
187 GATE_INTERFACE_VTBL(gate_task) const* vtbl;
188
189 gate_atomic_int_t ref_counter;
190 gate_atomic_int_t status;
191 gate_result_t task_result;
192 gate_syncevent_t signal;
193 gate_task_storage_t store;
194 } gate_taskimpl_t;
195
196 static char const* gate_taskimpl_get_interface_name(void* obj)
197 {
198 GATE_UNUSED_ARG(obj);
199 return GATE_INTERFACE_NAME_TASK;
200 }
201
202 2 static void gate_taskimpl_destroy(gate_taskimpl_t* impl)
203 {
204 /* TODO */
205 2 gate_syncevent_destroy(&impl->signal);
206 2 gate_mem_dealloc(impl);
207 2 }
208
209 2 static void gate_taskimpl_release(void* obj)
210 {
211 2 gate_taskimpl_t* const impl = (gate_taskimpl_t*)obj;
212
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (impl != NULL)
213 {
214
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (0 == gate_atomic_int_dec(&impl->ref_counter))
215 {
216 2 gate_taskimpl_destroy(impl);
217 }
218 }
219 2 }
220 static int gate_taskimpl_retain(void* obj)
221 {
222 gate_taskimpl_t* const impl = (gate_taskimpl_t*)obj;
223 if (impl)
224 {
225 return gate_atomic_int_inc(&impl->ref_counter);
226 }
227 return 0;
228 }
229 2 static gate_bool_t gate_taskimpl_update_state(gate_taskimpl_t* impl, int from_state, int to_state)
230 {
231 2 gate_int32_t old_state = gate_atomic_int_xchg_if(&impl->status, from_state, to_state);
232 2 return old_state == from_state;
233 }
234
235 1 static gate_result_t gate_taskimpl_run(void* obj)
236 {
237 1 gate_result_t ret = GATE_RESULT_FAILED;
238
239 do
240 {
241 1 gate_taskimpl_t* impl = (gate_taskimpl_t*)obj;
242 gate_int32_t target_state;
243 gate_int32_t old_state;
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 do
418 {
419 gate_size_t param_count;
420 gate_size_t param_size;
421 gate_va_list_carrier_t carrier;
422 gate_size_t total_size;
423 gate_result_t result;
424 char* result_address;
425 char* param_store_address;
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