GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/microservices/timer_service.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 0 127 0.0%
Functions: 0 14 0.0%
Branches: 0 40 0.0%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger |
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 #include "gate/tech/microservices/timer_service.h"
29 #include "gate/synchronization.h"
30 #include "gate/threading.h"
31 #include "gate/results.h"
32 #include "gate/times.h"
33
34 typedef struct timerservice_config_interval_class
35 {
36 gate_struct_t struct_base;
37
38 gate_int32_t seconds;
39 gate_int32_t alignment;
40 gate_string_t msg_id;
41 gate_string_t msg_content;
42
43 } timerservice_config_interval_t;
44
45
46 #define TIMERSERVICE_ROOT_NAME "timer_service"
47
48 #define TIMERSERVICE_CONFIG_INTERVAL_NAME GATE_STRUCT_ROOT_NAME GATE_STRUCT_SEPARATOR TIMERSERVICE_ROOT_NAME GATE_STRUCT_SEPARATOR "config_interval"
49
50 static gate_struct_item_t const timerservice_config_interval_members[] =
51 {
52 GATE_STRUCT_ITEM(timerservice_config_interval_t, GATE_TYPE_I32, seconds),
53 GATE_STRUCT_ITEM(timerservice_config_interval_t, GATE_TYPE_I32, alignment),
54 GATE_STRUCT_ITEM(timerservice_config_interval_t, GATE_TYPE_STRING, msg_id),
55 GATE_STRUCT_ITEM(timerservice_config_interval_t, GATE_TYPE_STRING, msg_content)
56 };
57
58 static gate_struct_descriptor_t const timerservice_config_interval_descriptor =
59 {
60 TIMERSERVICE_CONFIG_INTERVAL_NAME,
61 timerservice_config_interval_members,
62 sizeof(timerservice_config_interval_members) / sizeof(timerservice_config_interval_members[0]),
63 sizeof(timerservice_config_interval_t)
64 };
65
66 static gate_result_t timerservice_config_interval_ctor(void* target, void const* src)
67 {
68 gate_result_t result;
69
70 gate_mem_clear(target, sizeof(timerservice_config_interval_t));
71 if (src)
72 {
73 result = gate_struct_copy((gate_struct_t*)target, (gate_struct_t const*)src);
74 }
75 else
76 {
77 result = gate_struct_init((gate_struct_t*)target, &timerservice_config_interval_descriptor);
78 }
79 return result;
80 }
81
82
83 typedef struct timerservice_config_class
84 {
85 gate_struct_t struct_base;
86
87 gate_arraylist_t intervals;
88
89 } timerservice_config_t;
90
91 #define TIMERSERVICE_CONFIG_NAME GATE_STRUCT_ROOT_NAME GATE_STRUCT_SEPARATOR TIMERSERVICE_ROOT_NAME GATE_STRUCT_SEPARATOR "config"
92
93 static gate_struct_item_t const timerservice_config_members[] =
94 {
95 GATE_STRUCT_ITEM(timerservice_config_t, GATE_TYPE_ARRAYLIST_STRUCT, intervals)
96 };
97
98 static gate_struct_descriptor_t const timerservice_config_descriptor =
99 {
100 TIMERSERVICE_CONFIG_NAME,
101 timerservice_config_members,
102 sizeof(timerservice_config_members) / sizeof(timerservice_config_members[0]),
103 sizeof(timerservice_config_t)
104 };
105
106 static gate_result_t timerservice_config_ctor(void* target, void const* src)
107 {
108 gate_result_t result;
109 timerservice_config_t const* ptr_src = (timerservice_config_t const*)src;
110 timerservice_config_t* ptr_target = (timerservice_config_t*)target;
111
112 gate_mem_clear(target, sizeof(timerservice_config_t));
113 if (src)
114 {
115 result = gate_struct_copy(&ptr_target->struct_base, &ptr_src->struct_base);
116 }
117 else
118 {
119 result = gate_struct_init(&ptr_target->struct_base, &timerservice_config_descriptor);
120 if (GATE_SUCCEEDED(result))
121 {
122 ptr_target->intervals = gate_arraylist_create(sizeof(timerservice_config_interval_t), NULL, 0,
123 &timerservice_config_interval_ctor, &gate_struct_destructor);
124 if (!ptr_target->intervals)
125 {
126 gate_struct_destructor(&ptr_target->struct_base);
127 result = GATE_RESULT_OUTOFMEMORY;
128 }
129 }
130 }
131 return result;
132 }
133
134
135 typedef struct timer_service_data_class
136 {
137 gate_thread_t timer_thread;
138 gate_thread_id_t timer_thread_id;
139 gate_syncevent_t timer_event;
140 gate_atomic_flag_t running;
141 timerservice_config_t config;
142 } gate_timer_service_data_t;
143
144
145 typedef struct timer_service_task
146 {
147 gate_timestamp_t trigger_time;
148 gate_string_t msg_id;
149 gate_string_t msg_content;
150 } timer_service_task_t;
151
152 static void timer_service_task_create(timer_service_task_t* task, gate_timestamp_t timestamp, gate_string_t const* id, gate_string_t const* content)
153 {
154 gate_mem_clear(task, sizeof(timer_service_task_t));
155 task->trigger_time = timestamp;
156 gate_string_clone(&task->msg_id, id);
157 gate_string_clone(&task->msg_content, content);
158 }
159 static void timer_service_task_destroy(timer_service_task_t* task)
160 {
161 gate_string_release(&task->msg_id);
162 gate_string_release(&task->msg_content);
163 }
164
165 static void timer_service_tasks_destroy(timer_service_task_t* task, gate_size_t count)
166 {
167 while (count-- != 0)
168 {
169 timer_service_task_destroy(task++);
170 }
171 }
172
173 static gate_size_t get_next_tasks(timerservice_config_t const* ptr_cfg, gate_timestamp_t now,
174 timer_service_task_t* tasks, gate_size_t tasks_max)
175 {
176 gate_size_t tasks_used = 0;
177 gate_size_t len = gate_arraylist_length(ptr_cfg->intervals);
178 gate_size_t ndx;
179 timerservice_config_interval_t const* ptr_interval;
180 gate_timestamp_t us_interval;
181 gate_timestamp_t us_alignment;
182 timer_service_task_t* ptr_task;
183
184 for (ndx = 0; (ndx != len) && (tasks_used < tasks_max); ++ndx)
185 {
186 ptr_interval = gate_arraylist_get(ptr_cfg->intervals, ndx);
187 if (ptr_interval)
188 {
189 us_interval = (gate_timestamp_t)ptr_interval->seconds * GATE_TYPE_CONST_LONGLONG(1000000);
190 us_alignment = (gate_timestamp_t)ptr_interval->alignment * GATE_TYPE_CONST_LONGLONG(1000000);
191
192 ptr_task = &tasks[tasks_used];
193 timer_service_task_create(ptr_task, now + us_interval, &ptr_interval->msg_id, &ptr_interval->msg_content);
194 ptr_task->trigger_time -= ptr_task->trigger_time % us_alignment;
195 ++tasks_used;
196 }
197 }
198 return tasks_used;
199 }
200
201 static gate_result_t timer_worker_thread(void* ptr)
202 {
203 gate_result_t ret = GATE_RESULT_OK;
204 gate_microservice_base_t* self = (gate_microservice_base_t*)ptr;
205 gate_timer_service_data_t* data = (gate_timer_service_data_t*)self->data_ptr;
206 static gate_string_t const msg_error_wait = GATE_STRING_INIT_STATIC("Failed to wait for synchronization event");
207 static gate_string_t const msg_exit = GATE_STRING_INIT_STATIC("Exiting timer worker thread");
208 static gate_string_t const msg_shutdown = GATE_STRING_INIT_STATIC("Shutdown signal received");
209 static gate_string_t const msg_trigger = GATE_STRING_INIT_STATIC("Time trigger");
210
211 gate_time_t now = GATE_INIT_EMPTY;
212 timer_service_task_t next_tasks[64];
213 gate_size_t next_tasks_count = 0;
214 gate_size_t ndx;
215 gate_bool_t trigger_executed = false;
216
217 gate_time_now(&now);
218 next_tasks_count = get_next_tasks(&data->config, now.timestamp, next_tasks, sizeof(next_tasks) / sizeof(next_tasks[0]));
219
220 while (gate_atomic_flag_set(&data->running) == true)
221 {
222 ret = gate_syncevent_timed_wait(&data->timer_event, 1000);
223 if (ret == GATE_RESULT_OK)
224 {
225 /* shutdown event detected */
226 gate_microhost_log(self->host, GATE_MICROSERVICE_LOGTYPE_DEBUG, ret, 0, &self->address, &msg_shutdown);
227 break;
228 }
229 else if (ret != GATE_RESULT_TIMEOUT)
230 {
231 /* internal error detected */
232 gate_microhost_log(self->host, GATE_MICROSERVICE_LOGTYPE_ERROR, ret, 0, &self->address, &msg_error_wait);
233 break;
234 }
235
236 gate_time_now(&now);
237 trigger_executed = false;
238 for (ndx = 0; ndx != next_tasks_count; ++ndx)
239 {
240 if (now.timestamp >= next_tasks[ndx].trigger_time)
241 {
242 gate_microhost_publish_message(self->host, &self->address, NULL, &next_tasks[ndx].msg_id, &next_tasks[ndx].msg_content);
243 trigger_executed = true;
244 }
245 }
246
247 if (trigger_executed)
248 {
249 timer_service_tasks_destroy(next_tasks, next_tasks_count);
250 next_tasks_count = get_next_tasks(&data->config, now.timestamp + GATE_TYPE_CONST_LONGLONG(1000),
251 next_tasks, sizeof(next_tasks) / sizeof(next_tasks[0]));
252 }
253
254 }
255
256 timer_service_tasks_destroy(next_tasks, next_tasks_count);
257
258 gate_microhost_log(self->host, GATE_MICROSERVICE_LOGTYPE_DEBUG, ret, 0, &self->address, &msg_exit);
259 return GATE_RESULT_OK;
260 }
261
262
263 static void timer_on_release(gate_microservice_base_t* self)
264 {
265 gate_timer_service_data_t* data = (gate_timer_service_data_t*)self->data_ptr;
266 gate_struct_release(&data->config.struct_base);
267 }
268 static gate_result_t timer_on_start(gate_microservice_base_t* self)
269 {
270 gate_result_t ret;
271 gate_timer_service_data_t* data = (gate_timer_service_data_t*)self->data_ptr;
272
273 do
274 {
275 /* export parameters from property storage to native config struct */
276 gate_property_export(&self->parameters, GATE_TYPE_STRUCT, &data->config.struct_base);
277
278 ret = gate_syncevent_create(&data->timer_event, true);
279 GATE_BREAK_IF_FAILED(ret);
280
281 gate_atomic_flag_set(&data->running);
282 ret = gate_thread_start_code(&timer_worker_thread, self, &data->timer_thread, &data->timer_thread_id);
283 if (GATE_FAILED(ret))
284 {
285 gate_syncevent_destroy(&data->timer_thread);
286 break;
287 }
288 } while (0);
289
290 if (GATE_FAILED(ret))
291 {
292 gate_atomic_flag_clear(&data->running);
293 }
294
295 return ret;
296
297 }
298 static gate_result_t timer_on_stop(gate_microservice_base_t* self)
299 {
300 gate_result_t ret;
301 gate_result_t thread_result = GATE_RESULT_OK;
302 gate_timer_service_data_t* data = (gate_timer_service_data_t*)self->data_ptr;
303 static gate_string_t error_msg_join = GATE_STRING_INIT_STATIC("Failed to join timer thread");
304 static gate_string_t error_msg_sync = GATE_STRING_INIT_STATIC("Failed to destroy synchronization object");
305
306 gate_atomic_flag_clear(&data->running);
307
308 ret = gate_thread_join(&data->timer_thread, &thread_result);
309 if (GATE_FAILED(ret))
310 {
311 gate_microhost_log(self->host, GATE_MICROSERVICE_LOGTYPE_WARNING, ret, 1,
312 &self->address, &error_msg_join);
313 }
314 ret = gate_syncevent_destroy(&data->timer_event);
315 if (GATE_FAILED(ret))
316 {
317 gate_microhost_log(self->host, GATE_MICROSERVICE_LOGTYPE_WARNING, ret, 1,
318 &self->address, &error_msg_sync);
319 }
320
321 gate_struct_release(&data->config.struct_base);
322
323 gate_mem_clear(data, sizeof(gate_timer_service_data_t));
324 return GATE_RESULT_OK;
325 }
326 static gate_result_t timer_on_message_received(gate_microservice_base_t* self,
327 gate_string_t const* source, gate_string_t const* destination,
328 gate_string_t const* msg_id, gate_string_t const* message)
329 {
330 GATE_UNUSED_ARG(self);
331 GATE_UNUSED_ARG(source);
332 GATE_UNUSED_ARG(destination);
333 GATE_UNUSED_ARG(msg_id);
334 GATE_UNUSED_ARG(message);
335
336 return GATE_RESULT_NOTSUPPORTED;
337 }
338 static gate_result_t timer_on_object_received(gate_microservice_base_t* self,
339 gate_string_t const* source, gate_string_t const* destination,
340 gate_string_t const* obj_id, gate_object_t* obj)
341 {
342 GATE_UNUSED_ARG(self);
343 GATE_UNUSED_ARG(source);
344 GATE_UNUSED_ARG(destination);
345 GATE_UNUSED_ARG(obj_id);
346 GATE_UNUSED_ARG(obj);
347 return GATE_RESULT_NOTSUPPORTED;
348 }
349 static gate_result_t timer_on_invoke(gate_microservice_base_t* self, gate_string_t const* method,
350 gate_struct_t const* request, gate_struct_t* response)
351 {
352 GATE_UNUSED_ARG(self);
353 GATE_UNUSED_ARG(method);
354 GATE_UNUSED_ARG(request);
355 GATE_UNUSED_ARG(response);
356 return GATE_RESULT_NOTSUPPORTED;
357 }
358
359
360 gate_microservice_t* gate_microservice_timer_create()
361 {
362 gate_microservice_base_t* timer_service = gate_microservice_base_create(sizeof(gate_timer_service_data_t), NULL);
363 gate_timer_service_data_t* timer_data = NULL;
364
365 if (timer_service)
366 {
367 timer_service->on_start = &timer_on_start;
368 timer_service->on_stop = &timer_on_stop;
369 timer_service->on_release = &timer_on_release;
370 timer_service->on_message_received = &timer_on_message_received;
371 timer_service->on_object_received = &timer_on_object_received;
372 timer_service->on_invoke = timer_on_invoke;
373
374 timer_data = (gate_timer_service_data_t*)timer_service->data_ptr;
375 timerservice_config_ctor(&timer_data->config, NULL);
376
377 gate_property_create_object(&timer_service->parameters);
378 gate_property_import(&timer_service->parameters, GATE_TYPE_STRUCT, &timer_data->config.struct_base);
379 timer_service->ptr_parameters = &timer_data->config.struct_base;
380 }
381 return (gate_microservice_t*)timer_service;
382 }
383
384