GCC Code Coverage Report


Directory: src/gate/
File: src/gate/queues.c
Date: 2026-10-19 17:06:30
Exec Total Coverage
Lines: 324 405 80.0%
Functions: 32 35 91.4%
Branches: 98 174 56.3%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright (c) 2018-2026, 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/queues.h"
30 #include "gate/threading.h"
31 #include "gate/synchronization.h"
32 #include "gate/results.h"
33 #include "gate/atomics.h"
34 #include "gate/arrays.h"
35 #include "gate/hashes.h"
36 #include "gate/maps.h"
37 #include "gate/comparers.h"
38
39
40 /**************************************
41 ** queueitembuffer implementation **
42 **************************************/
43
44 struct gate_queueitembuffer_class
45 {
46 gate_size_t max_length;
47 gate_mutex_t mutex;
48 gate_synccondition_t cond;
49 gate_arraylist_t buffer;
50 };
51
52 1 gate_queueitembuffer_t gate_queueitembuffer_create(gate_size_t max_length, gate_size_t workitem_size,
53 gate_mem_copyctor_t workitem_cctor, gate_mem_dtor_t workitem_dtor)
54 {
55 1 gate_queueitembuffer_t ret = NULL;
56 1 gate_queueitembuffer_t impl = NULL;
57 gate_result_t result;
58 do
59 {
60 1 impl = (gate_queueitembuffer_t)gate_mem_alloc(sizeof(struct gate_queueitembuffer_class));
61
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!impl)
62 {
63 ✗ break;
64 }
65 1 gate_mem_clear(impl, sizeof(struct gate_queueitembuffer_class));
66 1 impl->max_length = max_length;
67 1 result = gate_mutex_create(&impl->mutex);
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
69 {
70 ✗ break;
71 }
72 1 result = gate_synccondition_create(&impl->cond);
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
74 {
75 ✗ gate_mutex_destroy(&impl->mutex);
76 ✗ break;
77 }
78 1 impl->buffer = gate_arraylist_create(workitem_size, NULL, 0, workitem_cctor, workitem_dtor);
79
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (gate_arraylist_is_null(impl->buffer))
80 {
81 ✗ gate_synccondition_destroy(&impl->cond);
82 ✗ gate_mutex_destroy(&impl->mutex);
83 ✗ break;
84 }
85
86 1 ret = impl;
87 1 impl = NULL;
88 } while (0);
89
90
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (impl != NULL)
91 {
92 ✗ gate_mem_dealloc(impl);
93 }
94
95 1 return ret;
96 }
97
98 1 gate_result_t gate_queueitembuffer_destroy(gate_queueitembuffer_t buffer)
99 {
100
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (buffer)
101 {
102 1 gate_mutex_destroy(&buffer->mutex);
103 1 gate_synccondition_destroy(&buffer->cond);
104 1 gate_arraylist_release(&buffer->buffer);
105 1 gate_mem_dealloc(buffer);
106 }
107 1 return GATE_RESULT_OK;
108 }
109
110 3 gate_size_t gate_queueitembuffer_length(gate_queueitembuffer_t buffer)
111 {
112 3 gate_size_t ret = 0;
113 gate_result_t result;
114 do
115 {
116 3 result = gate_mutex_acquire(&buffer->mutex);
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(result);
118 3 ret = gate_arraylist_length(buffer->buffer);
119 3 gate_mutex_release(&buffer->mutex);
120 } while (0);
121 3 return ret;
122 }
123
124 3 gate_result_t gate_queueitembuffer_push(gate_queueitembuffer_t buffer, void const* item_input)
125 {
126 3 gate_result_t ret = GATE_RESULT_FAILED;
127 gate_size_t curlen;
128 do
129 {
130 3 ret = gate_mutex_acquire(&buffer->mutex);
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(ret);
132
133 3 curlen = gate_arraylist_length(buffer->buffer);
134
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
3 if ((buffer->max_length > 0) && (curlen >= buffer->max_length))
135 {
136 1 ret = GATE_RESULT_BUFFERTOOSMALL;
137 }
138
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 else if (NULL == gate_arraylist_add(buffer->buffer, item_input))
139 {
140 ✗ ret = GATE_RESULT_OUTOFMEMORY;
141 }
142 else
143 {
144 2 ret = GATE_RESULT_OK;
145 }
146
147 3 gate_mutex_release(&buffer->mutex);
148
149
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (GATE_SUCCEEDED(ret))
150 {
151 2 gate_synccondition_signal_all(&buffer->cond);
152 }
153 } while (0);
154 3 return ret;
155 }
156 4 gate_result_t gate_queueitembuffer_pop(gate_queueitembuffer_t buffer, gate_uint32_t timeout_ms, void* item_output, gate_size_t item_size)
157 {
158 4 gate_result_t ret = GATE_RESULT_FAILED;
159 gate_size_t sz;
160
161 do
162 {
163 4 ret = gate_mutex_acquire(&buffer->mutex);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(ret);
165 do
166 {
167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (timeout_ms != 0)
168 {
169 ✗ if (gate_arraylist_length(buffer->buffer) == 0)
170 {
171 ✗ if (timeout_ms == GATE_QUEUEITEMBUFFER_WAIT_INFINITE)
172 {
173 ✗ ret = gate_synccondition_wait(&buffer->cond, &buffer->mutex);
174 }
175 else
176 {
177 ✗ ret = gate_synccondition_timed_wait(&buffer->cond, &buffer->mutex, timeout_ms);
178 }
179 }
180 ✗ GATE_BREAK_IF_FAILED(ret);
181 }
182
183
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (gate_arraylist_length(buffer->buffer) == 0)
184 {
185 2 ret = GATE_RESULT_TIMEOUT;
186 }
187 else
188 {
189 2 sz = gate_arraylist_get_value(buffer->buffer, 0, item_output, item_size);
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (sz == 0)
191 {
192 ✗ ret = GATE_RESULT_OUTOFMEMORY;
193 }
194 else
195 {
196 2 gate_arraylist_remove(buffer->buffer, 0, 1);
197 2 ret = GATE_RESULT_OK;
198 }
199 }
200 } while (0);
201 4 gate_mutex_release(&buffer->mutex);
202 } while (0);
203 4 return ret;
204 }
205
206
207
208 115 int gate_compare_channel_id(void const* item1, void const* item2)
209 {
210 115 gate_channel_id_t const* ch1 = (gate_channel_id_t const*)item1;
211 115 gate_channel_id_t const* ch2 = (gate_channel_id_t const*)item2;
212
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 74 times.
115 if (*ch1 == *ch2)
213 {
214 41 return 0;
215 }
216
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 72 times.
74 else if (*ch1 < *ch2)
217 {
218 2 return -1;
219 }
220 else
221 {
222 72 return +1;
223 }
224 }
225
226
227
228
229 /*******************************
230 ** exequeue implementation **
231 *******************************/
232
233 typedef struct gate_exequeue_item_class
234 {
235 gate_entrypoint_t func;
236 void* param;
237 gate_mem_dtor_t param_destructor;
238
239 } gate_exequeue_item_t;
240
241
242
243 typedef struct gate_exequeue_impl_class
244 {
245 GATE_INTERFACE_VTBL(gate_runnable) const* vtbl;
246 gate_atomic_int_t ref_counter;
247 gate_thread_t threadhandle;
248 gate_syncevent_t syncevent;
249 gate_mutex_t mutex;
250 gate_arraylist_t items;
251 gate_atomic_flag_t running;
252 gate_entrypoint_t thread_init;
253 gate_entrypoint_t thread_shutdown;
254 void* thread_param;
255 } gate_exequeue_impl_t;
256
257 3 static void gate_exequeue_impl_release(void* disp)
258 {
259 3 gate_exequeue_impl_t* ptr = (gate_exequeue_impl_t*)disp;
260
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 if (gate_atomic_int_dec(&ptr->ref_counter) == 0)
261 {
262 1 gate_bool_t is_current = false;
263 1 gate_result_t result = gate_thread_is_current(&ptr->threadhandle, &is_current);
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
265 {
266 /* assume worst case: we are releasing the object inside the worker thread itself */
267 ✗ is_current = true;
268 }
269
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (is_current)
270 {
271 /* detaching the thread handle is the only action we can perform */
272 1 gate_thread_detach(&ptr->threadhandle);
273 }
274 else
275 {
276 /* we are in the main or another controlling thread -> await worker shutdown*/
277 ✗ gate_thread_join(&ptr->threadhandle, &result);
278 }
279
280 1 gate_mutex_destroy(&ptr->mutex);
281 1 gate_syncevent_destroy(&ptr->syncevent);
282 1 gate_arraylist_release(&ptr->items);
283
284 1 gate_mem_dealloc(ptr);
285 }
286 3 }
287 1 static int gate_exequeue_impl_retain(void* disp)
288 {
289 1 gate_exequeue_impl_t* ptr = (gate_exequeue_impl_t*)disp;
290 1 return (int)gate_atomic_int_inc(&ptr->ref_counter);
291 }
292 ✗ static char const* gate_exequeue_impl_get_interface_name(void* disp)
293 {
294 (void)disp;
295 ✗ return GATE_INTERFACE_NAME_RUNNABLE;
296 }
297
298 1 static gate_result_t gate_exequeue_impl_run(void* disp)
299 {
300 1 gate_exequeue_impl_t* ptr = (gate_exequeue_impl_t*)disp;
301 1 gate_result_t ret = GATE_RESULT_OK;
302 1 gate_bool_t has_next_item = false;
303 1 gate_exequeue_item_t item = GATE_INIT_EMPTY;
304
305
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr->thread_init)
306 {
307 1 ptr->thread_init(ptr->thread_param);
308 }
309
310
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
8 while (gate_atomic_flag_set(&ptr->running) == true)
311 {
312 7 ret = gate_mutex_acquire(&ptr->mutex);
313
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (GATE_FAILED(ret))
314 {
315 ✗ break;
316 }
317
318 7 has_next_item = (0 != gate_arraylist_length(ptr->items));
319
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if (has_next_item)
320 {
321 5 item = *((gate_exequeue_item_t*)gate_arraylist_get(ptr->items, 0));
322 5 gate_arraylist_remove(ptr->items, 0, 1);
323 }
324
325 7 gate_mutex_release(&ptr->mutex);
326
327
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if (has_next_item)
328 {
329
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (item.func != NULL)
330 {
331 5 item.func(item.param);
332 }
333
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (item.param_destructor != NULL)
334 {
335 1 item.param_destructor(item.param);
336 }
337 }
338 else
339 {
340 2 ret = gate_syncevent_wait(&ptr->syncevent);
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(ret))
342 {
343 ✗ break;
344 }
345 }
346 }
347
348
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr->thread_shutdown)
349 {
350 1 ptr->thread_shutdown(ptr->thread_param);
351 }
352
353 1 gate_object_release(ptr);
354
355 1 return ret;
356 }
357
358 static GATE_INTERFACE_VTBL(gate_runnable) gate_exequeue_impl_vtbl;
359 1 static void gate_init_exequeue_impl_vtbl()
360 {
361
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!gate_exequeue_impl_vtbl.get_interface_name)
362 {
363 GATE_INTERFACE_VTBL(gate_runnable) const local_vtbl =
364 {
365 &gate_exequeue_impl_get_interface_name,
366 &gate_exequeue_impl_release,
367 &gate_exequeue_impl_retain,
368
369 &gate_exequeue_impl_run
370 };
371
372 1 gate_exequeue_impl_vtbl = local_vtbl;
373 }
374 1 }
375
376
377 1 gate_result_t gate_exequeue_create(gate_exequeue_t* queue,
378 gate_entrypoint_t init_task,
379 gate_entrypoint_t shutdown_task,
380 void* task_param)
381 {
382 gate_result_t ret;
383 1 gate_exequeue_impl_t* impl = (gate_exequeue_impl_t*)gate_mem_alloc(sizeof(gate_exequeue_impl_t));
384 do
385 {
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (impl == NULL)
387 {
388 ✗ ret = GATE_RESULT_OUTOFMEMORY;
389 ✗ break;
390 }
391 1 gate_mem_clear(impl, sizeof(gate_exequeue_impl_t));
392 1 gate_init_exequeue_impl_vtbl();
393 1 impl->vtbl = &gate_exequeue_impl_vtbl;
394 1 gate_atomic_flag_set(&impl->running);
395 1 gate_atomic_int_init(&impl->ref_counter, 2);
396
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (GATE_FAILED(ret = gate_syncevent_create(&impl->syncevent, true)))
397 {
398 ✗ gate_mem_dealloc(impl);
399 ✗ break;
400 }
401
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (GATE_FAILED(ret = gate_mutex_create(&impl->mutex)))
402 {
403 ✗ gate_syncevent_destroy(&impl->syncevent);
404 ✗ gate_mem_dealloc(impl);
405 ✗ break;
406 }
407 1 impl->items = gate_arraylist_create(sizeof(gate_exequeue_item_t), NULL, 0, NULL, NULL);
408
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (gate_arraylist_is_null(impl->items))
409 {
410 ✗ gate_mutex_destroy(&impl->mutex);
411 ✗ gate_syncevent_destroy(&impl->syncevent);
412 ✗ gate_mem_dealloc(impl);
413 ✗ ret = GATE_RESULT_OUTOFMEMORY;
414 ✗ break;
415 }
416 1 impl->thread_init = init_task;
417 1 impl->thread_shutdown = shutdown_task;
418 1 impl->thread_param = task_param;
419 1 ret = gate_thread_start((gate_runnable_t*)impl, &impl->threadhandle, NULL);
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
421 {
422 ✗ gate_arraylist_release(&impl->items);
423 ✗ gate_mutex_destroy(&impl->mutex);
424 ✗ gate_syncevent_destroy(&impl->syncevent);
425 ✗ gate_mem_dealloc(impl);
426 ✗ break;
427 }
428 1 *queue = (gate_exequeue_t)impl;
429 } while (0);
430
431 1 return ret;
432 }
433 1 gate_result_t gate_exequeue_destroy(gate_exequeue_t queue)
434 {
435 1 gate_exequeue_impl_t* impl = (gate_exequeue_impl_t*)queue;
436 gate_result_t ret;
437 1 gate_atomic_flag_clear(&impl->running);
438 1 ret = gate_syncevent_set(&impl->syncevent);
439
440 1 gate_object_release(impl);
441 1 return ret;
442 }
443 5 gate_result_t gate_exequeue_push(gate_exequeue_t queue, gate_entrypoint_t functions, void* parameter, gate_mem_dtor_t paramdestructor)
444 {
445 5 gate_exequeue_impl_t* impl = (gate_exequeue_impl_t*)queue;
446 gate_result_t ret;
447 gate_exequeue_item_t item;
448 5 item.func = functions;
449 5 item.param = parameter;
450 5 item.param_destructor = paramdestructor;
451
452 5 ret = gate_mutex_acquire(&impl->mutex);
453
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (GATE_SUCCEEDED(ret))
454 {
455
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (NULL == gate_arraylist_add(impl->items, &item))
456 {
457 ✗ ret = GATE_RESULT_OUTOFMEMORY;
458 }
459
460 5 gate_mutex_release(&impl->mutex);
461 }
462
463
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (GATE_SUCCEEDED(ret))
464 {
465 5 ret = gate_syncevent_set(&impl->syncevent);
466 }
467
468 5 return ret;
469 }
470 1 gate_result_t gate_exequeue_itemcount(gate_exequeue_t queue, gate_size_t* items)
471 {
472 1 gate_exequeue_impl_t* impl = (gate_exequeue_impl_t*)queue;
473 gate_result_t ret;
474
475 1 ret = gate_mutex_acquire(&impl->mutex);
476
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(ret))
477 {
478
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (items != NULL)
479 {
480 1 *items = gate_arraylist_length(impl->items);
481 }
482 1 gate_mutex_release(&impl->mutex);
483 }
484
485 1 return ret;
486 }
487
488
489 /*******************************
490 ** msgqueue implementation **
491 *******************************/
492
493
494 typedef struct gate_msgqueue_subscription_class
495 {
496 gate_msgqueue_receiver_t receiver;
497 void* user_param;
498 } gate_msgqueue_subscription_t;
499
500 typedef struct gate_msgqueue_target_class
501 {
502 gate_string_t address;
503 gate_map_t subscriptions; /* maps: gate_msgqueue_subscriber_t -> gate_msgqueue_subscription_t */
504
505 } gate_msgqueue_target_t;
506
507 2 static void GATE_CALL msgqueue_target_dtor(void* dst)
508 {
509 2 gate_msgqueue_target_t* ptr = (gate_msgqueue_target_t*)dst;
510 2 gate_string_release(&ptr->address);
511 2 gate_map_destroy(&ptr->subscriptions);
512 2 }
513
514 2 static gate_result_t GATE_CALL msgqueue_target_cctor(void* dst, void const* src)
515 {
516 2 gate_result_t ret = GATE_RESULT_OUTOFMEMORY;
517 2 gate_msgqueue_target_t* ptr_dst = (gate_msgqueue_target_t*)dst;
518 2 gate_msgqueue_target_t const* ptr_src = (gate_msgqueue_target_t const*)src;
519 do
520 {
521 2 gate_mem_clear(ptr_dst, sizeof(gate_msgqueue_target_t));
522
523
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (NULL == src)
524 {
525 1 gate_string_create_empty(&ptr_dst->address);
526 1 gate_map_create(&ptr_dst->subscriptions, gate_compare_uintptr,
527 sizeof(gate_msgqueue_subscription_id_t), NULL, NULL,
528 sizeof(gate_msgqueue_subscription_t), NULL, NULL);
529 }
530 else
531 {
532
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_string_clone(&ptr_dst->address, &ptr_src->address))
533 {
534 ✗ break;
535 }
536
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_map_copy(&ptr_dst->subscriptions, &ptr_src->subscriptions))
537 {
538 ✗ break;
539 }
540 }
541 2 ret = GATE_RESULT_OK;
542 } while (0);
543
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(ret))
545 {
546 ✗ msgqueue_target_dtor(ptr_dst);
547 }
548 2 return ret;
549 }
550
551 typedef struct gate_msgqueue_impl
552 {
553 GATE_INTERFACE_VTBL(gate_msgqueue)* vtbl;
554
555 gate_atomic_int_t ref_counter;
556 gate_atomic_int_t state;
557 gate_atomic_int_t next_subscriber_id;
558 gate_mutex_t mutex;
559 gate_map_t targets; /* maps: gate_msgqueue_target_id_t -> gate_msgqueue_target_t */
560 } gate_msgqueue_impl_t;
561
562 2 static gate_result_t msgqueue_init(gate_msgqueue_impl_t* self)
563 {
564 gate_result_t ret;
565 do
566 {
567 2 gate_atomic_int_init(&self->ref_counter, 1);
568 2 gate_atomic_int_init(&self->state, GATE_MSGQUEUE_STATUS_OFFLINE);
569 2 gate_atomic_int_init(&self->next_subscriber_id, 0);
570
571 2 ret = gate_mutex_create(&self->mutex);
572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
573
574
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_map_create(&self->targets, gate_compare_uintptr,
575 sizeof(gate_msgqueue_target_id_t), NULL, NULL,
576 sizeof(gate_msgqueue_target_t), &msgqueue_target_cctor, &msgqueue_target_dtor))
577 {
578 ✗ ret = GATE_RESULT_OUTOFMEMORY;
579 ✗ break;
580 }
581
582 } while (0);
583 2 return ret;
584 }
585
586 2 static void msgqueue_destroy(gate_msgqueue_impl_t* self)
587 {
588 2 gate_mutex_destroy(&self->mutex);
589 2 gate_map_destroy(&self->targets);
590 2 }
591
592 ✗ static char const* msgqueue_get_interface_name(void* obj)
593 {
594 (void)obj;
595 ✗ return GATE_INTERFACE_NAME_MSGQUEUE;
596 }
597 4 static void msgqueue_release(void* obj)
598 {
599 4 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
600
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (0 == gate_atomic_int_dec(&self->ref_counter))
601 {
602 2 msgqueue_destroy(self);
603 2 gate_mem_dealloc(self);
604 }
605 4 }
606 2 static int msgqueue_retain(void* obj)
607 {
608 2 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
609 2 return gate_atomic_int_inc(&self->ref_counter);
610 }
611
612 5 static gate_bool_t update_state(gate_atomic_int_t* atom, gate_int32_t from, gate_int32_t to)
613 {
614 5 return from == gate_atomic_int_xchg_if(atom, from, to);
615 }
616
617 1 static gate_result_t msgqueue_start(void* obj)
618 {
619 1 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
620
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!update_state(&self->state, GATE_MSGQUEUE_STATUS_OFFLINE, GATE_MSGQUEUE_STATUS_STARTING))
621 {
622 ✗ return GATE_RESULT_INVALIDSTATE;
623 }
624
625 1 gate_atomic_int_set(&self->state, GATE_MSGQUEUE_STATUS_ONLINE);
626 1 return GATE_RESULT_OK;
627 }
628 1 static gate_result_t msgqueue_stop(void* obj)
629 {
630 1 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
631
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!update_state(&self->state, GATE_MSGQUEUE_STATUS_ONLINE, GATE_MSGQUEUE_STATUS_STOPPING))
632 {
633 ✗ return GATE_RESULT_INVALIDSTATE;
634 }
635
636 1 gate_atomic_int_set(&self->state, GATE_MSGQUEUE_STATUS_OFFLINE);
637 1 return GATE_RESULT_OK;
638 }
639 3 static gate_enumint_t msgqueue_get_status(void* obj)
640 {
641 3 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
642 3 return (gate_enumint_t)gate_atomic_int_get(&self->state);
643 }
644
645 1 static gate_result_t msgqueue_add_target(void* obj, gate_string_t const* target_address, gate_msgqueue_target_id_t* ptr_target_id)
646 {
647 1 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
648 1 gate_result_t ret = GATE_RESULT_FAILED;
649 gate_msgqueue_target_id_t target_id;
650 gate_map_iterator_t iter;
651 1 gate_msgqueue_target_t target = GATE_INIT_EMPTY;
652
653 1 target_id = gate_string_hash(target_address);
654
655 do
656 {
657 1 ret = gate_mutex_acquire(&self->mutex);
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
659 do
660 {
661 1 iter = gate_map_get(&self->targets, &target_id);
662
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (gate_map_iterator_valid(iter))
663 {
664 ✗ ret = GATE_RESULT_ALREADYEXISTS;
665 ✗ break;
666 }
667
668 1 ret = msgqueue_target_cctor(&target, NULL);
669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
670
671
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_string_create_copy(&target.address, target_address))
672 {
673 ✗ ret = GATE_RESULT_OUTOFMEMORY;
674 ✗ break;
675 }
676
677
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_map_add(&self->targets, &target_id, &target))
678 {
679 ✗ ret = GATE_RESULT_OUTOFMEMORY;
680 ✗ break;
681 }
682
683
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_target_id)
684 {
685 1 *ptr_target_id = target_id;
686 }
687
688 } while (0);
689 1 gate_mutex_release(&self->mutex);
690
691 } while (0);
692
693 1 msgqueue_target_dtor(&target);
694
695 1 return ret;
696
697 }
698 1 static gate_result_t msgqueue_get_target(void* obj, gate_msgqueue_target_id_t target_id, gate_string_t* ptr_target_address)
699 {
700 1 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
701 1 gate_result_t ret = GATE_RESULT_FAILED;
702 gate_msgqueue_target_t const* ptr_target;
703 do
704 {
705 1 ret = gate_mutex_acquire(&self->mutex);
706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
707 do
708 {
709 1 ptr_target = (gate_msgqueue_target_t const*)gate_map_get_value(&self->targets, &target_id);
710
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ptr_target)
711 {
712 ✗ ret = GATE_RESULT_NOMATCH;
713 ✗ break;
714 }
715
716
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_target_address)
717 {
718
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_string_clone(ptr_target_address, &ptr_target->address))
719 {
720 ✗ ret = GATE_RESULT_OUTOFMEMORY;
721 ✗ break;
722 }
723 }
724 1 ret = GATE_RESULT_OK;
725 } while (0);
726 1 gate_mutex_release(&self->mutex);
727
728 } while (0);
729
730 1 return ret;
731 }
732 1 static gate_result_t msgqueue_resolve_target(void* obj, gate_string_t const* target_address, gate_msgqueue_target_id_t* ptr_target_id)
733 {
734 1 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
735 1 gate_result_t ret = GATE_RESULT_FAILED;
736 gate_map_iterator_t iter;
737 gate_msgqueue_target_id_t target_id;
738
739 do
740 {
741 1 target_id = (gate_msgqueue_target_id_t)gate_string_hash(target_address);
742
743 1 ret = gate_mutex_acquire(&self->mutex);
744
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
745 do
746 {
747 1 iter = gate_map_get(&self->targets, &target_id);
748
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!gate_map_iterator_valid(iter))
749 {
750 ✗ ret = GATE_RESULT_NOMATCH;
751 ✗ break;
752 }
753
754
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_target_id)
755 {
756 1 *ptr_target_id = target_id;
757 }
758 1 ret = GATE_RESULT_OK;
759 } while (0);
760 1 gate_mutex_release(&self->mutex);
761
762 } while (0);
763
764 1 return ret;
765 }
766 ✗ static gate_result_t msgqueue_remove_target(void* obj, gate_msgqueue_target_id_t target_id)
767 {
768 ✗ gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
769 ✗ gate_result_t ret = GATE_RESULT_FAILED;
770 do
771 {
772 ✗ ret = gate_mutex_acquire(&self->mutex);
773 ✗ GATE_BREAK_IF_FAILED(ret);
774 do
775 {
776 ✗ if (!gate_map_remove(&self->targets, &target_id))
777 {
778 ✗ ret = GATE_RESULT_NOMATCH;
779 ✗ break;
780 }
781 ✗ ret = GATE_RESULT_OK;
782 } while (0);
783 ✗ gate_mutex_release(&self->mutex);
784 } while (0);
785 ✗ return ret;
786 }
787
788 3 static gate_result_t msgqueue_publish(void* obj, gate_msgqueue_target_id_t target_id, gate_uint32_t message_type, void const* data, gate_size_t length)
789 {
790 3 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
791 3 gate_result_t ret = GATE_RESULT_FAILED;
792 gate_msgqueue_target_t const* ptr_target;
793 gate_msgqueue_subscription_t subscriptions[64];
794 3 gate_size_t subscriptions_count = 0;
795 gate_size_t ndx;
796 3 gate_size_t const subscriptions_max = sizeof(subscriptions) / sizeof(subscriptions[0]);
797 gate_msgqueue_subscription_t const* ptr_subscription;
798 gate_map_iterator_t iter;
799
800 do
801 {
802
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 if (!update_state(&self->state, GATE_MSGQUEUE_STATUS_ONLINE, GATE_MSGQUEUE_STATUS_ONLINE))
803 {
804 1 ret = GATE_RESULT_INVALIDSTATE;
805 1 break;
806 }
807
808 2 ret = gate_mutex_acquire(&self->mutex);
809
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
810 do
811 {
812 2 ptr_target = (gate_msgqueue_target_t const*)gate_map_get_value(&self->targets, &target_id);
813
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!ptr_target)
814 {
815 ✗ ret = GATE_RESULT_INVALIDARG;
816 ✗ break;
817 }
818
819
2/2
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
4 GATE_MAP_FOREACH(iter, &ptr_target->subscriptions)
820 {
821 2 ptr_subscription = (gate_msgqueue_subscription_t const*)gate_map_iterator_value(iter);
822
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ptr_subscription->receiver)
823 {
824 2 subscriptions[subscriptions_count] = *ptr_subscription;
825 2 ++subscriptions_count;
826
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (subscriptions_count >= subscriptions_max)
827 {
828 ✗ break;
829 }
830 }
831 }
832 2 ret = GATE_RESULT_OK;
833 } while (0);
834 2 gate_mutex_release(&self->mutex);
835
836
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
837
838
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for (ndx = 0; ndx != subscriptions_count; ++ndx)
839 {
840 2 subscriptions[ndx].receiver((gate_msgqueue_t*)self, target_id, message_type, data, length, subscriptions[ndx].user_param);
841 }
842 } while (0);
843
844 3 return ret;
845 }
846 1 static gate_result_t msgqueue_subscribe(void* obj, gate_msgqueue_target_id_t target_id, gate_msgqueue_receiver_t receiver, void* user_param, gate_msgqueue_subscription_id_t* ptr_subscriber)
847 {
848 1 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
849 1 gate_result_t ret = GATE_RESULT_FAILED;
850 gate_msgqueue_subscription_id_t subscriber_id;
851 gate_msgqueue_subscription_t subscription;
852 gate_msgqueue_target_t* ptr_target;
853 do
854 {
855 1 ret = gate_mutex_acquire(&self->mutex);
856
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
857 do
858 {
859 1 ptr_target = (gate_msgqueue_target_t*)gate_map_get_value(&self->targets, &target_id);
860
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ptr_target)
861 {
862 ✗ ret = GATE_RESULT_INVALIDARG;
863 ✗ break;
864 }
865
866 1 subscriber_id = gate_atomic_int_inc(&self->next_subscriber_id);
867 1 subscription.receiver = receiver;
868 1 subscription.user_param = user_param;
869
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_map_add(&ptr_target->subscriptions, &subscriber_id, &subscription))
870 {
871 ✗ ret = GATE_RESULT_OUTOFMEMORY;
872 ✗ break;
873 }
874
875
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_subscriber)
876 {
877 1 *ptr_subscriber = subscriber_id;
878 }
879 1 ret = GATE_RESULT_OK;
880 } while (0);
881 1 gate_mutex_release(&self->mutex);
882 } while (0);
883
884 1 return ret;
885 }
886 1 static gate_result_t msgqueue_unsubscribe(void* obj, gate_msgqueue_subscription_id_t subscriber)
887 {
888 1 gate_msgqueue_impl_t* self = (gate_msgqueue_impl_t*)obj;
889 1 gate_result_t ret = GATE_RESULT_FAILED;
890 gate_map_iterator_t iter;
891 gate_msgqueue_target_t* ptr_target;
892 do
893 {
894 1 ret = gate_mutex_acquire(&self->mutex);
895
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
896 do
897 {
898 1 ret = GATE_RESULT_NOMATCH;
899
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 GATE_MAP_FOREACH(iter, &self->targets)
900 {
901 1 ptr_target = (gate_msgqueue_target_t*)gate_map_iterator_value(iter);
902
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (gate_map_remove(&ptr_target->subscriptions, &subscriber))
903 {
904 1 ret = GATE_RESULT_OK;
905 1 break;
906 }
907 }
908 } while (0);
909 1 gate_mutex_release(&self->mutex);
910 } while (0);
911
912 1 return ret;
913
914 }
915
916 static GATE_INTERFACE_VTBL(gate_msgqueue) gate_msgqueue_vtbl;
917 2 static void gate_init_msgqueue_vtbl()
918 {
919
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!gate_msgqueue_vtbl.get_interface_name)
920 {
921 GATE_INTERFACE_VTBL(gate_msgqueue) const local_vtbl =
922 {
923 &msgqueue_get_interface_name,
924 &msgqueue_release,
925 &msgqueue_retain,
926
927 &msgqueue_start,
928 &msgqueue_stop,
929 &msgqueue_get_status,
930
931 &msgqueue_add_target,
932 &msgqueue_get_target,
933 &msgqueue_resolve_target,
934 &msgqueue_remove_target,
935
936 &msgqueue_publish,
937 &msgqueue_subscribe,
938 &msgqueue_unsubscribe
939 };
940 1 gate_msgqueue_vtbl = local_vtbl;
941 }
942 2 }
943
944 2 gate_msgqueue_t* gate_memory_msgqueue_create()
945 {
946 2 gate_msgqueue_t* ret = NULL;
947 2 gate_msgqueue_impl_t* impl = NULL;
948 gate_result_t result;
949 do
950 {
951 2 impl = gate_mem_alloc(sizeof(gate_msgqueue_impl_t));
952
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!impl)
953 {
954 ✗ break;
955 }
956 2 gate_mem_clear(impl, sizeof(gate_msgqueue_impl_t));
957 2 gate_init_msgqueue_vtbl();
958 2 impl->vtbl = &gate_msgqueue_vtbl;
959 2 result = msgqueue_init(impl);
960
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(result);
961
962 2 ret = (gate_msgqueue_t*)impl;
963 2 impl = NULL;
964 } while (0);
965
966
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (impl != NULL)
967 {
968 ✗ msgqueue_destroy(impl);
969 ✗ gate_mem_dealloc(impl);
970 }
971 2 return ret;
972 }
973
974
975
976
977
978