SafeAPI Backend POSIX
POSIX/Linux OAL backend for safeAPIFreamwork
Loading...
Searching...
No Matches
sapi_posix_backend_timer.c
Go to the documentation of this file.
1#define _POSIX_C_SOURCE 200809L
2
18
19#include <errno.h>
20#include <pthread.h>
21#include <signal.h>
22#include "safeapi/oal/memory/sapi_mem_util.h"
23#include <time.h>
24
26
28typedef struct
29{
30 pthread_t thread;
31 sapi_timer_config_t config;
32 volatile sig_atomic_t running;
33 volatile sig_atomic_t should_stop;
34 sapi_timer_handle_t self_handle;
36
37/* C99-compatible static assert: posix_timer_state_t must fit inside
38 * sapi_timer_storage_t's reserved bytes (a negative array size is a
39 * compile error). */
40typedef char posix_timer_storage_fits_[(sizeof(posix_timer_state_t) <= sizeof(sapi_timer_storage_t)) ? 1 : -1];
41
43#if !defined(__linux__)
44static void ms_to_timespec(sapi_duration_ms_t ms, struct timespec *out_ts);
45#endif
46static void *timer_thread_main(void *arg);
47static sapi_status_t backend_create(sapi_timer_storage_t *storage, const sapi_timer_config_t *config,
48 sapi_timer_handle_t *out_handle);
49static sapi_status_t backend_stop(sapi_timer_handle_t handle);
50static sapi_status_t backend_start(sapi_timer_handle_t handle);
51static sapi_status_t backend_destroy(sapi_timer_handle_t handle);
52static sapi_status_t backend_now(sapi_timestamp_ms_t *out_now_ms);
53
55static const sapi_timer_backend_t s_posix_timer_backend = { backend_create, backend_start, backend_stop,
56 backend_destroy, backend_now };
57
59
60
62const sapi_timer_backend_t *sapi_posix_backend_timer(void)
63{
65}
66
67
68/****Local functions ****/
69
70#if !defined(__linux__)
71static void ms_to_timespec(sapi_duration_ms_t ms, struct timespec *out_ts)
72{
73 out_ts->tv_sec = (time_t)(ms / 1000U);
74 out_ts->tv_nsec = (long)((ms % 1000U) * 1000000L);
75}
76#endif
77
78#if defined(__linux__)
79static void timespec_add_ms(struct timespec *ts, sapi_duration_ms_t ms)
80{
81 ts->tv_sec += (time_t)(ms / 1000U);
82 ts->tv_nsec += (long)((ms % 1000U) * 1000000L);
83 if (ts->tv_nsec >= 1000000000L)
84 {
85 ts->tv_sec += 1;
86 ts->tv_nsec -= 1000000000L;
87 }
88}
89#endif
90
91static void *timer_thread_main(void *arg)
92{
94#if defined(__linux__)
95 struct timespec next_wake_ts;
96
97 if (clock_gettime(CLOCK_MONOTONIC, &next_wake_ts) != 0)
98 {
99 next_wake_ts.tv_sec = 0;
100 next_wake_ts.tv_nsec = 0;
101 }
102#else
103 struct timespec period_ts;
104 ms_to_timespec(state->config.period_ms, &period_ts);
105#endif
106
107 for (;;)
108 {
109#if defined(__linux__)
110 int rc;
111 timespec_add_ms(&next_wake_ts, state->config.period_ms);
112 do
113 {
114 rc = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_wake_ts, NULL);
115 } while ((rc == EINTR) && (state->should_stop == 0));
116#else
117 struct timespec remaining = period_ts;
118 int rc;
119
120 do
121 {
122 rc = nanosleep(&remaining, &remaining);
123 } while ((rc != 0) && (errno == EINTR) && (state->should_stop == 0));
124#endif
125
126 if (state->should_stop != 0)
127 {
128 break;
129 }
130
131 state->config.callback(state->self_handle, state->config.user_ctx);
132
133 if (state->config.mode != SAPI_TIMER_MODE_PERIODIC)
134 {
135 break;
136 }
137 }
138
139 state->running = 0;
140 return NULL;
141}
142
143static sapi_status_t backend_create(sapi_timer_storage_t *storage, const sapi_timer_config_t *config,
144 sapi_timer_handle_t *out_handle)
145{
146 posix_timer_state_t *state;
147
148 if ((storage == NULL) || (config == NULL) || (out_handle == NULL) || (config->callback == NULL)
149 || (config->period_ms == 0U))
150 {
151 return SAPI_STATUS_INVALID_PARAM;
152 }
153
154 state = (posix_timer_state_t *)(void *)storage;
155 sapi_mem_set(state, 0, sizeof(*state));
156 state->config = *config;
157 state->self_handle = (sapi_timer_handle_t)(void *)storage;
158 *out_handle = state->self_handle;
159 return SAPI_STATUS_OK;
160}
161
162static sapi_status_t backend_stop(sapi_timer_handle_t handle)
163{
164 posix_timer_state_t *state = (posix_timer_state_t *)(void *)handle;
165
166 if (state == NULL)
167 {
168 return SAPI_STATUS_INVALID_PARAM;
169 }
170 if (state->running != 0)
171 {
172 state->should_stop = 1;
173 (void)pthread_join(state->thread, NULL);
174 state->running = 0;
175 }
176 return SAPI_STATUS_OK;
177}
178
179static sapi_status_t backend_start(sapi_timer_handle_t handle)
180{
181 posix_timer_state_t *state = (posix_timer_state_t *)(void *)handle;
182 sapi_status_t stop_status;
183
184 if (state == NULL)
185 {
186 return SAPI_STATUS_INVALID_PARAM;
187 }
188
189 /* sapi_timer_start() on an already-running timer means "restart":
190 * stop the old thread cleanly before starting a fresh one. */
191 stop_status = backend_stop(handle);
192 if (stop_status != SAPI_STATUS_OK)
193 {
194 return stop_status;
195 }
196
197 state->should_stop = 0;
198 if (pthread_create(&state->thread, NULL, timer_thread_main, state) != 0)
199 {
200 return SAPI_STATUS_INTERNAL_ERROR;
201 }
202 state->running = 1;
203 return SAPI_STATUS_OK;
204}
205
206static sapi_status_t backend_destroy(sapi_timer_handle_t handle)
207{
208 return backend_stop(handle);
209}
210
211static sapi_status_t backend_now(sapi_timestamp_ms_t *out_now_ms)
212{
213 struct timespec ts;
214
215 if (out_now_ms == NULL)
216 {
217 return SAPI_STATUS_INVALID_PARAM;
218 }
219 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
220 {
221 *out_now_ms = 0U;
222 return SAPI_STATUS_INTERNAL_ERROR;
223 }
224 *out_now_ms = (sapi_timestamp_ms_t)(((uint64_t)ts.tv_sec * 1000ULL) + ((uint64_t)ts.tv_nsec / 1000000ULL));
225 return SAPI_STATUS_OK;
226}
Real POSIX/Linux backend for every OAL service (ADR-018).
const sapi_timer_backend_t * sapi_posix_backend_timer(void)
The POSIX sapi_timer backend (one pthread per timer).
static const sapi_timer_backend_t s_posix_timer_backend