RTE OSAdapter POSIX 0.1.0 build 41b3eb3
POSIX/Linux OAL OSAdapter for Platform_RTE
Loading...
Searching...
No Matches
rte_posix_osadapter_period.c
Go to the documentation of this file.
1/* _POSIX_C_SOURCE: glibc declares clock_gettime(), pthread_condattr_setclock() and PTHREAD_PRIO_INHERIT only under a
2 * POSIX feature-test macro (this product builds strict C99), same as the other src/ files. _DARWIN_C_SOURCE (macOS
3 * only): with _POSIX_C_SOURCE set, macOS's <pthread.h> declares pthread_cond_timedwait_relative_np() only under it. */
4#define _POSIX_C_SOURCE 200809L
5#if defined(__APPLE__)
6#define _DARWIN_C_SOURCE
7#endif
8
24
25#include <errno.h>
26#include <pthread.h>
27#include <signal.h>
28#include <stdbool.h>
29#include <stddef.h>
30#include <stdint.h>
31#include <time.h>
32
34#define POSIX_PERIOD_NS_PER_MS 1000000L
35#define POSIX_PERIOD_NS_PER_S 1000000000L
36/* Result of a wait step that failed outside pthread_cond_*wait() (clock read, mutex release); any value other than 0
37 * and ETIMEDOUT ends the wait with RTE_STATUS_INTERNAL_ERROR. */
38#define POSIX_PERIOD_WAIT_FAILED (-1)
39/* Longest wait of posix_period_wake_all() for the wake mutex, in ms (OSA rule R2). The holders only check a flag,
40 * broadcast or start a wait, so this is far above a hold time; on expiry the stop falls back to the deadline (see the
41 * header). Overridable at compile time like RTE_POSIX_MEM_LOCK_TIMEOUT_MS. */
42#ifndef RTE_POSIX_PERIOD_WAKE_LOCK_TIMEOUT_MS
43#define RTE_POSIX_PERIOD_WAKE_LOCK_TIMEOUT_MS 50U
44#endif
45
47
49static void create_wake_object(void);
50static bool set_wake_clock(pthread_condattr_t *attr);
51static bool time_left_ns(const struct timespec *deadline, int64_t *out_ns);
52static rte_duration_ms_t ns_to_ms_rounded_up(int64_t ns);
53static rte_status_t wait_to_deadline(const struct timespec *deadline, const volatile sig_atomic_t *should_stop);
54static rte_status_t wait_locked(const struct timespec *deadline, const volatile sig_atomic_t *should_stop,
55 bool *out_waiting);
56static int wait_for_wake(const struct timespec *deadline);
57
59/* The process-wide wake object (see the header): created once by create_wake_object() through the pthread_once() in
60 * posix_period_wake_ready(), never destroyed after a successful create. s_wake_ready is written only inside
61 * pthread_once() and read after it returned. */
62static pthread_mutex_t s_wake_lock;
63static pthread_cond_t s_wake_cond;
64static bool s_wake_ready = false;
65
67
68
70rte_status_t posix_period_init(struct timespec *out_deadline)
71{
72 rte_status_t status = RTE_STATUS_INVALID_PARAM;
73
74 if (out_deadline != NULL)
75 {
76 struct timespec now;
77
78 /* Read into a local: a failed read leaves *out_deadline unwritten, it is never used as time 0. */
79 if (clock_gettime(CLOCK_MONOTONIC, &now) == 0)
80 {
81 *out_deadline = now;
82 status = RTE_STATUS_OK;
83 }
84 else
85 {
86 status = RTE_STATUS_INTERNAL_ERROR;
87 }
88 }
89 return status;
90}
91
92void posix_period_advance(struct timespec *deadline, rte_duration_ms_t period_ms)
93{
94 if (deadline != NULL)
95 {
96 rte_duration_ms_t whole_s = period_ms / 1000U;
97 rte_duration_ms_t rest_ms = period_ms % 1000U;
98
99 deadline->tv_sec += (time_t)whole_s;
100 deadline->tv_nsec += (long)rest_ms * POSIX_PERIOD_NS_PER_MS;
101 if (deadline->tv_nsec >= POSIX_PERIOD_NS_PER_S)
102 {
103 deadline->tv_sec += 1;
104 deadline->tv_nsec -= POSIX_PERIOD_NS_PER_S;
105 }
106 }
107}
108
109rte_status_t posix_period_sleep_until(const struct timespec *deadline, const volatile sig_atomic_t *should_stop)
110{
111 rte_status_t status = RTE_STATUS_INVALID_PARAM;
112
113 if ((deadline != NULL) && (should_stop != NULL))
114 {
116 {
117 status = wait_to_deadline(deadline, should_stop);
118 }
119 else
120 {
121 status = RTE_STATUS_INTERNAL_ERROR; /* no wake object: never wait on an uninitialised mutex */
122 }
123 }
124 return status;
125}
126
128{
129 static pthread_once_t wake_once = PTHREAD_ONCE_INIT;
130 bool ready = false;
131
132 if (pthread_once(&wake_once, create_wake_object) == 0)
133 {
134 ready = s_wake_ready;
135 }
136 return ready;
137}
138
139void posix_period_wake_all(volatile sig_atomic_t *should_stop)
140{
141 if (should_stop != NULL)
142 {
143 bool locked = false;
144
146 {
147 locked = (posix_osa_lock_bounded(&s_wake_lock, RTE_POSIX_PERIOD_WAKE_LOCK_TIMEOUT_MS) == RTE_STATUS_OK);
148 }
149 /* Stored under the mutex when it is held, so the waiter's check-then-wait cannot fall between this store and
150 * the broadcast (header: why no wake-up is lost). Without the mutex the store still ends the thread at its
151 * next deadline. */
152 *should_stop = 1;
153 if (locked)
154 {
155 /* A failed broadcast or release leaves the deadline fallback; nothing to report to the stopping caller. */
156 (void)pthread_cond_broadcast(&s_wake_cond);
157 (void)pthread_mutex_unlock(&s_wake_lock);
158 }
159 }
160}
161
162
163/****Local functions ****/
164
167static void create_wake_object(void)
168{
169 pthread_mutexattr_t mutex_attr;
170 pthread_condattr_t cond_attr;
171 bool mutex_created = false;
172 bool created = false;
173
174 /* OSA rule R2 (REQ-OAL-MUTEX-004): priority inheritance requested at run time on every host, exactly as mutex.c's
175 * create does (no _POSIX_THREAD_PRIO_INHERIT compile gate); no plain-mutex fallback. */
176 if (pthread_mutexattr_init(&mutex_attr) == 0)
177 {
178 if (pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT) == 0)
179 {
180 mutex_created = (pthread_mutex_init(&s_wake_lock, &mutex_attr) == 0);
181 }
182 (void)pthread_mutexattr_destroy(&mutex_attr);
183 }
184
185 if (mutex_created)
186 {
187 if (pthread_condattr_init(&cond_attr) == 0)
188 {
189 if (set_wake_clock(&cond_attr))
190 {
191 created = (pthread_cond_init(&s_wake_cond, &cond_attr) == 0);
192 }
193 (void)pthread_condattr_destroy(&cond_attr);
194 }
195 if (!created)
196 {
197 (void)pthread_mutex_destroy(&s_wake_lock);
198 }
199 }
200
201 s_wake_ready = created;
202 if (!created)
203 {
204 /* OSA rule R2/R6: no bounded, wakeable wait possible - no timer or task is created without it. Reported once
205 * (pthread_once()); every create then returns RTE_STATUS_INTERNAL_ERROR. */
206 POSIX_OSA_REPORT_RT_ATTRIBUTE("period wake object refused - timer/task create failed");
207 }
208}
209
210#if defined(__APPLE__)
213static bool set_wake_clock(pthread_condattr_t *attr)
214{
215 (void)attr;
216 return true;
217}
218#else
221static bool set_wake_clock(pthread_condattr_t *attr)
222{
223 return (pthread_condattr_setclock(attr, CLOCK_MONOTONIC) == 0);
224}
225#endif
226
229static bool time_left_ns(const struct timespec *deadline, int64_t *out_ns)
230{
231 bool read = false;
232 struct timespec now;
233
234 if (clock_gettime(CLOCK_MONOTONIC, &now) == 0)
235 {
236 *out_ns = (((int64_t)deadline->tv_sec - (int64_t)now.tv_sec) * POSIX_PERIOD_NS_PER_S) +
237 ((int64_t)deadline->tv_nsec - (int64_t)now.tv_nsec);
238 read = true;
239 }
240 return read;
241}
242
245static rte_duration_ms_t ns_to_ms_rounded_up(int64_t ns)
246{
247 int64_t ms = (ns + (POSIX_PERIOD_NS_PER_MS - 1L)) / POSIX_PERIOD_NS_PER_MS;
248 rte_duration_ms_t result = (rte_duration_ms_t)UINT32_MAX;
249
250 if (ms < (int64_t)UINT32_MAX)
251 {
252 result = (rte_duration_ms_t)ms;
253 }
254 return result;
255}
256
258static rte_status_t wait_to_deadline(const struct timespec *deadline, const volatile sig_atomic_t *should_stop)
259{
260 rte_status_t status = RTE_STATUS_OK;
261 bool waiting = (*should_stop == 0);
262
263 while (waiting)
264 {
265 int64_t remaining_ns = 0;
266
267 if (!time_left_ns(deadline, &remaining_ns))
268 {
269 status = RTE_STATUS_INTERNAL_ERROR; /* no time base - never wait against a guess */
270 waiting = false;
271 }
272 else if (remaining_ns <= 0)
273 {
274 waiting = false; /* deadline reached or already passed (overrun): no wait */
275 }
276 else
277 {
278 rte_status_t lock_status = posix_osa_lock_bounded(&s_wake_lock, ns_to_ms_rounded_up(remaining_ns));
279
280 if (lock_status == RTE_STATUS_OK)
281 {
282 status = wait_locked(deadline, should_stop, &waiting);
283 }
284 else if (lock_status == RTE_STATUS_TIMEOUT)
285 {
286 /* Not an error: the mutex stayed busy for the time left. The clock is read again at the top of the
287 * loop, so the deadline (not the lock bound, which Linux measures on CLOCK_REALTIME) decides. */
288 waiting = (*should_stop == 0);
289 }
290 else
291 {
292 status = RTE_STATUS_INTERNAL_ERROR; /* a failed lock clock read or an error from the pthread call */
293 waiting = false;
294 }
295 }
296 }
297 return status;
298}
299
303static rte_status_t wait_locked(const struct timespec *deadline, const volatile sig_atomic_t *should_stop,
304 bool *out_waiting)
305{
306 rte_status_t status = RTE_STATUS_OK;
307 int rc = 0;
308
309 /* Checked under the mutex the waker holds while it stores the flag and broadcasts: no wake-up can be lost. */
310 if (*should_stop == 0)
311 {
312 rc = wait_for_wake(deadline);
313 }
314 *out_waiting = (*should_stop == 0);
315 if (pthread_mutex_unlock(&s_wake_lock) != 0)
316 {
317 rc = POSIX_PERIOD_WAIT_FAILED;
318 }
319 if ((rc != 0) && (rc != ETIMEDOUT))
320 {
321 status = RTE_STATUS_INTERNAL_ERROR;
322 *out_waiting = false;
323 }
324 return status;
325}
326
327#if defined(__APPLE__)
332static int wait_for_wake(const struct timespec *deadline)
333{
334 int rc = ETIMEDOUT;
335 int64_t remaining_ns = 0;
336
337 if (!time_left_ns(deadline, &remaining_ns))
338 {
339 rc = POSIX_PERIOD_WAIT_FAILED;
340 }
341 else if (remaining_ns > 0)
342 {
343 struct timespec relative;
344 int64_t whole_s = remaining_ns / POSIX_PERIOD_NS_PER_S;
345 int64_t rest_ns = remaining_ns % POSIX_PERIOD_NS_PER_S;
346
347 relative.tv_sec = (time_t)whole_s;
348 relative.tv_nsec = (long)rest_ns;
349 rc = pthread_cond_timedwait_relative_np(&s_wake_cond, &s_wake_lock, &relative);
350 }
351 else
352 {
353 /* deadline passed while the mutex was taken: rc stays ETIMEDOUT */
354 }
355 return rc;
356}
357#else
360static int wait_for_wake(const struct timespec *deadline)
361{
362 return pthread_cond_timedwait(&s_wake_cond, &s_wake_lock, deadline);
363}
364#endif
rte_status_t posix_osa_lock_bounded(pthread_mutex_t *mutex, rte_duration_ms_t timeout_ms)
Waits at most timeout_ms for a pthread mutex.
Private helper: a bounded wait for a pthread mutex (OSA rule R2: every wait bounded).
rte_status_t posix_period_init(struct timespec *out_deadline)
Starts a period: reads CLOCK_MONOTONIC into the deadline.
static rte_status_t wait_locked(const struct timespec *deadline, const volatile sig_atomic_t *should_stop, bool *out_waiting)
static pthread_mutex_t s_wake_lock
static int wait_for_wake(const struct timespec *deadline)
static rte_status_t wait_to_deadline(const struct timespec *deadline, const volatile sig_atomic_t *should_stop)
void posix_period_wake_all(volatile sig_atomic_t *should_stop)
Requests a stop of a periodic thread and wakes it from its wait.
bool posix_period_wake_ready(void)
Creates the process-wide wake object on the first call; true once it exists.
static bool time_left_ns(const struct timespec *deadline, int64_t *out_ns)
rte_status_t posix_period_sleep_until(const struct timespec *deadline, const volatile sig_atomic_t *should_stop)
Waits until the absolute CLOCK_MONOTONIC deadline, or until a stop is requested.
static rte_duration_ms_t ns_to_ms_rounded_up(int64_t ns)
static bool set_wake_clock(pthread_condattr_t *attr)
#define POSIX_PERIOD_NS_PER_MS
void posix_period_advance(struct timespec *deadline, rte_duration_ms_t period_ms)
Moves the deadline one period later, keeping tv_nsec in [0, 1 s).
static void create_wake_object(void)
Private helper: pacing of a periodic thread by an absolute CLOCK_MONOTONIC deadline (OSA rule R3),...
Private helper: reports a real-time attribute this OSAdapter could not apply (OSA rules R1/R2/R6).