RTE OSAdapter POSIX 0.1.0 build 41b3eb3
POSIX/Linux OAL OSAdapter for Platform_RTE
Loading...
Searching...
No Matches
rte_posix_osadapter_timer.c
Go to the documentation of this file.
1#define _POSIX_C_SOURCE 200809L
2
117
118#include <errno.h>
119#include <limits.h>
120#include <pthread.h>
121#include <sched.h>
122#include <signal.h>
123#include <stdbool.h>
124#include <stddef.h>
125#include "rte/oal/memory/rte_mem_util.h"
126#include <time.h>
127
129/* Fixed stack size of every timer thread, in bytes (OSA rule R1): 8 MiB. That is the default thread stack glibc derives
130 * from an 8192 KiB RLIMIT_STACK (`ulimit -s` in the RBC Docker images), so the Linux timer stack there is unchanged; on
131 * macOS it is larger than the 512 KiB default of a secondary thread (virtual size, pages are committed on use).
132 * Overridable at compile time like RTE_POSIX_MEM_ARENA_SIZE; a value below PTHREAD_STACK_MIN is raised to it. */
133#ifndef RTE_POSIX_TIMER_STACK_SIZE
134#define RTE_POSIX_TIMER_STACK_SIZE ((size_t)8388608U)
135#endif
136
138typedef struct
139{
140 pthread_t thread;
141 rte_timer_config_t config;
142 bool joinable; /* controlling thread only, see file header */
143 volatile sig_atomic_t should_stop; /* the only field both threads write, see file header */
144 rte_timer_handle_t self_handle;
146
147/* C99-compatible static assert: posix_timer_state_t must fit inside
148 * rte_timer_storage_t's reserved bytes (a negative array size is a
149 * compile error). */
150typedef char posix_timer_storage_fits_[(sizeof(posix_timer_state_t) <= sizeof(rte_timer_storage_t)) ? 1 : -1];
151
153static void create_timer_key(void);
154static bool timer_key_ready(void);
155static void *timer_thread_main(void *arg);
156static void run_timer_loop(posix_timer_state_t *state);
157static bool is_timer_thread(const posix_timer_state_t *state);
158static rte_status_t join_timer_thread(posix_timer_state_t *state);
159static bool set_creator_sched_attributes(pthread_attr_t *attr);
160static rte_status_t create_timer_thread(posix_timer_state_t *state);
161static rte_status_t osadapter_create(rte_timer_storage_t *storage, const rte_timer_config_t *config,
162 rte_timer_handle_t *out_handle);
163static rte_status_t osadapter_stop(rte_timer_handle_t handle);
164static rte_status_t osadapter_start(rte_timer_handle_t handle);
165static rte_status_t osadapter_destroy(rte_timer_handle_t handle);
166static rte_status_t osadapter_now(rte_timestamp_ms_t *out_now_ms);
167
169static const rte_osadapter_timer_t s_posix_timer_osadapter = { osadapter_create, osadapter_start, osadapter_stop,
170 osadapter_destroy, osadapter_now };
171
172/* The thread-specific-data key of the self check (file header): created once per process by
173 * create_timer_key() through the pthread_once() in timer_key_ready(), never deleted. s_timer_key_ready is
174 * written only inside pthread_once() and read after it returned. */
175static pthread_key_t s_timer_key;
176static bool s_timer_key_ready = false;
177
179
180
182const rte_osadapter_timer_t *rte_posix_osadapter_timer(void)
183{
185}
186
187
188/****Local functions ****/
189
192static void create_timer_key(void)
193{
194 s_timer_key_ready = (pthread_key_create(&s_timer_key, NULL) == 0);
195}
196
199static bool timer_key_ready(void)
200{
201 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
202 bool ready = false;
203
204 if (pthread_once(&key_once, create_timer_key) == 0)
205 {
206 ready = s_timer_key_ready;
207 }
208 return ready;
209}
210
213static void *timer_thread_main(void *arg)
214{
216
217 if (pthread_setspecific(s_timer_key, state) == 0)
218 {
219 run_timer_loop(state);
220 }
221 else
222 {
223 /* OSA rule R2/R6: no self check possible - a callback calling stop/start on its own timer could not be told
224 * apart from another thread. Report and run no callback; the controlling thread still joins this thread. */
225 POSIX_OSA_REPORT_RT_ATTRIBUTE("timer thread-specific data not set - timer not run");
226 }
227 /* The controlling thread learns that this thread ended from pthread_join(). */
228 return NULL;
229}
230
234{
235 struct timespec deadline;
236 bool running = (posix_period_init(&deadline) == RTE_STATUS_OK);
237 bool clock_failed = !running;
238
239 while (running)
240 {
241 posix_period_advance(&deadline, state->config.period_ms);
242 if (posix_period_sleep_until(&deadline, &state->should_stop) != RTE_STATUS_OK)
243 {
244 clock_failed = true;
245 running = false;
246 }
247 else if (state->should_stop != 0)
248 {
249 running = false;
250 }
251 else
252 {
253 state->config.callback(state->self_handle, state->config.user_ctx);
254
255 /* A stop during the callback (from the callback itself or from another thread) ends the loop now, not one
256 * period later: no further callback either way, and the joining thread does not wait for a period. */
257 if ((state->config.mode != RTE_TIMER_MODE_PERIODIC) || (state->should_stop != 0))
258 {
259 running = false;
260 }
261 }
262 }
263
264 if (clock_failed)
265 {
266 /* OSA rule R3/R6: no monotonic time base - never pace from a guessed time 0. Report once; the controlling
267 * thread still joins this thread. */
268 POSIX_OSA_REPORT_RT_ATTRIBUTE("timer monotonic clock failed - timer stopped");
269 }
270}
271
274static bool is_timer_thread(const posix_timer_state_t *state)
275{
276 return (pthread_getspecific(s_timer_key) == (const void *)state);
277}
278
282static rte_status_t join_timer_thread(posix_timer_state_t *state)
283{
284 rte_status_t status = RTE_STATUS_OK;
285
286 if (state->joinable)
287 {
288 posix_period_wake_all(&state->should_stop);
289 if (pthread_join(state->thread, NULL) == 0)
290 {
291 state->joinable = false;
292 }
293 else
294 {
295 status = RTE_STATUS_INTERNAL_ERROR;
296 }
297 }
298 return status;
299}
300
305static bool set_creator_sched_attributes(pthread_attr_t *attr)
306{
307 bool applied = false;
308 struct sched_param sched;
309 int policy = 0;
310
311 rte_mem_set(&sched, 0, sizeof(sched));
312 if (pthread_getschedparam(pthread_self(), &policy, &sched) == 0)
313 {
314 if (pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED) == 0)
315 {
316 if (pthread_attr_setschedpolicy(attr, policy) == 0)
317 {
318 if (pthread_attr_setschedparam(attr, &sched) == 0)
319 {
320 applied = true;
321 }
322 }
323 }
324 }
325 return applied;
326}
327
331static rte_status_t create_timer_thread(posix_timer_state_t *state)
332{
333 rte_status_t status = RTE_STATUS_INTERNAL_ERROR;
334 pthread_attr_t attr;
335
336 if (pthread_attr_init(&attr) == 0)
337 {
338 bool inherit_fallback = false;
339 int rc = -1;
340 size_t stack_size = (size_t)RTE_POSIX_TIMER_STACK_SIZE;
341
342#if defined(PTHREAD_STACK_MIN)
343 if (stack_size < (size_t)PTHREAD_STACK_MIN)
344 {
345 stack_size = (size_t)PTHREAD_STACK_MIN;
346 }
347#endif
348 if (pthread_attr_setstacksize(&attr, stack_size) != 0)
349 {
350 /* OSA rule R1: fixed stack size - report that it was refused; the default stack is used. */
351 POSIX_OSA_REPORT_RT_ATTRIBUTE("timer stack size not applied");
352 }
353
355 {
356 rc = pthread_create(&state->thread, &attr, timer_thread_main, state);
357 if (rc == EPERM)
358 {
359 /* The explicit-sched attributes were refused; no thread was created. */
360 inherit_fallback = true;
361 }
362 }
363 else
364 {
365 inherit_fallback = true;
366 }
367
368 if (inherit_fallback)
369 {
370 /* OSA rule R1/R6: scheduling not fixed at creation - report once per start, then create the thread under
371 * inherited scheduling (ADR-018 fallback, the behaviour before attributes were set); stack size kept. */
372 POSIX_OSA_REPORT_RT_ATTRIBUTE("timer scheduling not applied");
373 rc = -1;
374 if (pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED) == 0)
375 {
376 rc = pthread_create(&state->thread, &attr, timer_thread_main, state);
377 }
378 }
379
380 if (rc == 0)
381 {
382 status = RTE_STATUS_OK;
383 }
384 (void)pthread_attr_destroy(&attr);
385 }
386 return status;
387}
388
389static rte_status_t osadapter_create(rte_timer_storage_t *storage, const rte_timer_config_t *config,
390 rte_timer_handle_t *out_handle)
391{
392 posix_timer_state_t *state;
393 bool ready;
394
395 if ((storage == NULL) || (config == NULL) || (out_handle == NULL) || (config->callback == NULL)
396 || (config->period_ms == 0U))
397 {
398 return RTE_STATUS_INVALID_PARAM;
399 }
400 ready = timer_key_ready();
401 if (ready)
402 {
403 ready = posix_period_wake_ready();
404 }
405 if (!ready)
406 {
407 /* The self-check key or the period wake object could not be created (file header): no timer without them;
408 * storage left untouched. */
409 return RTE_STATUS_INTERNAL_ERROR;
410 }
411
412 state = (posix_timer_state_t *)(void *)storage;
413 rte_mem_set(state, 0, sizeof(*state));
414 state->config = *config;
415 state->self_handle = (rte_timer_handle_t)(void *)storage;
416 *out_handle = state->self_handle;
417 return RTE_STATUS_OK;
418}
419
420static rte_status_t osadapter_stop(rte_timer_handle_t handle)
421{
422 posix_timer_state_t *state = (posix_timer_state_t *)(void *)handle;
423 rte_status_t status;
424
425 if (state == NULL)
426 {
427 return RTE_STATUS_INVALID_PARAM;
428 }
429 /* Set (and the waiting thread woken) before the self check, so the timer loop ends whichever branch is taken. On
430 * the join branch join_timer_thread() sets it and wakes once more, which is harmless. */
431 posix_period_wake_all(&state->should_stop);
432 if (is_timer_thread(state))
433 {
434 /* Self-call rule (file header): the timer thread cannot join itself. It ends after this callback; the next
435 * stop/start/destroy from another thread joins it. */
436 status = RTE_STATUS_OK;
437 }
438 else
439 {
440 status = join_timer_thread(state);
441 }
442 return status;
443}
444
445static rte_status_t osadapter_start(rte_timer_handle_t handle)
446{
447 posix_timer_state_t *state = (posix_timer_state_t *)(void *)handle;
448 rte_status_t status;
449
450 if (state == NULL)
451 {
452 return RTE_STATUS_INVALID_PARAM;
453 }
454
455 if (is_timer_thread(state))
456 {
457 /* Self-call rule (file header): a restart from the timer's own callback would leave this thread running next
458 * to the new one. Refused, nothing changed. */
459 status = RTE_STATUS_NOT_SUPPORTED;
460 }
461 else
462 {
463 /* rte_timer_start() on a timer whose thread exists (running, or a one-shot that has fired) means "restart":
464 * join the old thread before starting a fresh one. */
465 status = join_timer_thread(state);
466 if (status == RTE_STATUS_OK)
467 {
468 state->should_stop = 0;
469 status = create_timer_thread(state);
470 if (status == RTE_STATUS_OK)
471 {
472 state->joinable = true;
473 }
474 }
475 }
476 return status;
477}
478
479static rte_status_t osadapter_destroy(rte_timer_handle_t handle)
480{
481 return osadapter_stop(handle);
482}
483
484static rte_status_t osadapter_now(rte_timestamp_ms_t *out_now_ms)
485{
486 struct timespec ts;
487
488 if (out_now_ms == NULL)
489 {
490 return RTE_STATUS_INVALID_PARAM;
491 }
492 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
493 {
494 *out_now_ms = 0U;
495 return RTE_STATUS_INTERNAL_ERROR;
496 }
497 *out_now_ms = (rte_timestamp_ms_t)(((uint64_t)ts.tv_sec * 1000ULL) + ((uint64_t)ts.tv_nsec / 1000000ULL));
498 return RTE_STATUS_OK;
499}
Real POSIX/Linux OSAdapter for every OAL service (ADR-018).
const rte_osadapter_timer_t * rte_posix_osadapter_timer(void)
The POSIX rte_timer OSAdapter (one pthread per timer).
static rte_status_t osadapter_create(rte_ipc_storage_t *storage, const rte_ipc_config_t *config, rte_ipc_handle_t *out_handle)
rte_status_t posix_period_init(struct timespec *out_deadline)
Starts a period: reads CLOCK_MONOTONIC into the deadline.
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.
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.
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).
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).
static bool is_timer_thread(const posix_timer_state_t *state)
static void * timer_thread_main(void *arg)
#define RTE_POSIX_TIMER_STACK_SIZE
static bool set_creator_sched_attributes(pthread_attr_t *attr)
static void create_timer_key(void)
static const rte_osadapter_timer_t s_posix_timer_osadapter
static void run_timer_loop(posix_timer_state_t *state)
static bool timer_key_ready(void)
static rte_status_t create_timer_thread(posix_timer_state_t *state)
static rte_status_t join_timer_thread(posix_timer_state_t *state)