|
| | __attribute__ ((noreturn)) void nros_platform_panic(const char *msg |
| |
| void * | nros_platform_alloc (size_t size) |
| |
| static bool | nros_platform_atomic_load_bool (const bool *ptr) |
| |
| static void | nros_platform_atomic_store_bool (bool *ptr, bool value) |
| |
| uint64_t | nros_platform_clock_ns (void) |
| |
| uint64_t | nros_platform_clock_resolution_ns (void) |
| |
| int8_t | nros_platform_condvar_drop (void *cv) |
| |
| int8_t | nros_platform_condvar_init (void *cv) |
| |
| int8_t | nros_platform_condvar_signal (void *cv) |
| |
| int8_t | nros_platform_condvar_signal_all (void *cv) |
| |
| int8_t | nros_platform_condvar_signal_from_isr (void *cv) |
| |
| size_t | nros_platform_condvar_storage_align (void) |
| |
| size_t | nros_platform_condvar_storage_size (void) |
| |
| int8_t | nros_platform_condvar_wait (void *cv, void *m) |
| |
| int8_t | nros_platform_condvar_wait_until (void *cv, void *m, uint64_t abstime) |
| |
| uint32_t | nros_platform_critical_section_acquire (void) |
| |
| void | nros_platform_critical_section_release (uint32_t token) |
| |
| void | nros_platform_dealloc (void *ptr) |
| |
| uint64_t | nros_platform_epoch_us (void) |
| |
| static void | nros_platform_free (void *ptr) |
| |
| size_t | nros_platform_heap_total_bytes (void) |
| |
| size_t | nros_platform_heap_used_bytes (void) |
| |
| void | nros_platform_log_flush (void) |
| |
| void | nros_platform_log_write (uint8_t severity, const uint8_t *name_ptr, uintptr_t name_len, const uint8_t *msg_ptr, uintptr_t msg_len) |
| |
| static void * | nros_platform_malloc (size_t size) |
| |
| int8_t | nros_platform_mutex_drop (void *m) |
| |
| int8_t | nros_platform_mutex_init (void *m) |
| |
| int8_t | nros_platform_mutex_lock (void *m) |
| |
| int8_t | nros_platform_mutex_rec_drop (void *m) |
| |
| int8_t | nros_platform_mutex_rec_init (void *m) |
| |
| int8_t | nros_platform_mutex_rec_lock (void *m) |
| |
| size_t | nros_platform_mutex_rec_storage_align (void) |
| |
| size_t | nros_platform_mutex_rec_storage_size (void) |
| |
| int8_t | nros_platform_mutex_rec_try_lock (void *m) |
| |
| int8_t | nros_platform_mutex_rec_unlock (void *m) |
| |
| size_t | nros_platform_mutex_storage_align (void) |
| |
| size_t | nros_platform_mutex_storage_size (void) |
| |
| int8_t | nros_platform_mutex_try_lock (void *m) |
| |
| int8_t | nros_platform_mutex_unlock (void *m) |
| |
| void | nros_platform_random_fill (void *buf, size_t len) |
| |
| uint16_t | nros_platform_random_u16 (void) |
| |
| uint32_t | nros_platform_random_u32 (void) |
| |
| uint64_t | nros_platform_random_u64 (void) |
| |
| uint8_t | nros_platform_random_u8 (void) |
| |
| void * | nros_platform_realloc (void *ptr, size_t size) |
| |
| void | nros_platform_register_log_writer (nros_platform_log_writer_fn_t writer, nros_platform_log_flush_fn_t flusher) |
| |
| void | nros_platform_sleep_ms (size_t ms) |
| |
| void | nros_platform_sleep_s (size_t s) |
| |
| void | nros_platform_sleep_us (size_t us) |
| |
| void | nros_platform_task_attr_init (nros_platform_task_attr_t *attr) |
| |
| int8_t | nros_platform_task_cancel (void *task) |
| |
| int8_t | nros_platform_task_detach (void *task) |
| |
| void | nros_platform_task_exit (void) |
| |
| void | nros_platform_task_free (void **task) |
| |
| int8_t | nros_platform_task_init (void *task, void *attr, void *(*entry)(void *), void *arg) |
| |
| int8_t | nros_platform_task_join (void *task) |
| |
| size_t | nros_platform_task_stack_unused_bytes (void) |
| |
| size_t | nros_platform_task_storage_align (void) |
| |
| size_t | nros_platform_task_storage_size (void) |
| |
| uint64_t | nros_platform_time_now_ns (void) |
| |
| int8_t | nros_platform_wake_drop (void *w) |
| |
| int8_t | nros_platform_wake_init (void *w) |
| |
| int8_t | nros_platform_wake_signal (void *w) |
| |
| int8_t | nros_platform_wake_signal_from_isr (void *w) |
| |
| size_t | nros_platform_wake_storage_align (void) |
| |
| size_t | nros_platform_wake_storage_size (void) |
| |
| int8_t | nros_platform_wake_wait_ms (void *w, uint32_t timeout_ms) |
| |
| void | nros_platform_yield_now (void) |
| |
| const char * | nros_runtime_locator_override (void) |
| |
Canonical C ABI for the nros platform abstraction.
RFC-0042 D1 / phase-241 wave B — this is THE single canonical platform header, owned by nros-platform-api (the lowest crate, no deps). nros-c and nros-platform-cffi re-export it, so neither package's consumers need the other's include dir (it breaks the historical nros-c↔cffi header tangle, and there is exactly one file named <nros/platform.h> — no include-order race).
A platform implementor supplies the symbols declared here. Every nros binary links exactly one platform implementation; resolution is at link time — no runtime registration. Implementations may be any language with a C ABI; for Rust platform crates, nros-platform-cffi re-exports the Rust impl as #[unsafe(no_mangle)] extern "C" symbols matching the names below (its src/lib.rs extern block is the hand-written mirror, guarded byte-for-byte by c_stub_platform.rs).
Companion to the canonical-C-ABI RMW vtable (<nros/rmw_vtable.h>); the platform layer sits one tier below RMW.
Return-value conventions
int8_t returns: 0 = success, non-zero = error.
- Pointer returns:
NULL = allocation failure or not-implemented; non-NULL is the resource handle.
clock_* / time_* returns are absolute / monotonic counters and never error. If the platform has no clock, return 0.
Threading
All symbols must be safe to invoke from any thread. mutex_* / condvar_* must be safe under concurrent callers. mutex_rec_* must support same-thread re-entry (zenoh-pico re-enters the same mutex).
RTOS yields (yield_now) are not ISR-safe. Bare-metal yields built on core::hint::spin_loop() are.
| uint64_t nros_platform_clock_ns |
( |
void |
| ) |
|
Monotonic nanoseconds since a platform-defined epoch (boot, program start, …). Never decreases. Wraps after ~584 years.
Must be backed by a hardware counter or the OS tick — never by a software counter that only advances when polled.
Available immediately after platform init, before any other nros subsystem. SHOULD be callable from an ISR; a port whose clock is not ISR-safe must say so in its port documentation.
RFC-0073: this replaced the former clock_ms / clock_us pair. Ports that can convert without a runtime division should — where the counter frequency divides 1e9 (25/50/100/125/200/250 MHz) a compile-time ns-per-cycle multiply is ~2.5x cheaper than the divide it replaces.
| uint64_t nros_platform_clock_resolution_ns |
( |
void |
| ) |
|
Granularity of nros_platform_clock_ns, in nanoseconds: the smallest non-zero difference two successive reads can report.
Examples: 1000000 for a 1 kHz tick, 40 for a 25 MHz cycle counter, 1000 for a microsecond hardware timer.
Must be non-zero, and constant for the lifetime of the program after platform init. A port whose underlying rate is only known at runtime returns the resolved value; one whose rate can change under it returns the COARSEST value it may exhibit. There is no "unknown" encoding — a port that cannot answer honestly is reporting a clock it cannot honestly offer.
| int8_t nros_platform_condvar_signal_from_isr |
( |
void * |
cv | ) |
|
Phase 124.B.7.a — ISR-safe signal.
Callable from interrupt context. nros_platform_condvar_signal is NOT ISR-safe on every platform (POSIX pthread_cond_signal isn't on the async-signal-safe function list; RTOS condvar primitives often require thread context). Backends MUST use this variant when triggering from an ISR or POSIX signal handler.
Per-platform implementation:
- POSIX:
pipe write — async-signal-safe; a runtime worker thread forwards to the underlying condvar. (Linux may use eventfd instead, which is NOT POSIX — signalfd/eventfd are Linux syscalls, which is why nros-node's worker is target_os = "linux"-gated.)
- Zephyr:
k_sem_give on the wake semaphore (ISR-safe).
- FreeRTOS:
xSemaphoreGiveFromISR + portYIELD_FROM_ISR on the wake semaphore.
- NuttX:
sem_post (POSIX-safe under NuttX) on the wake sem.
- ThreadX:
tx_event_flags_set on the wake event flag group (ISR-safe).
- Bare-metal: atomic flag store +
__SEV() (Cortex-M).
Returns non-zero on error (e.g. ISR-unsafe call on a backend that mandates ISR-context-only via a separate primitive). Backends without an ISR-safe path return non-zero so callers can fall back to thread-context signal (with the obvious latency cost).
| uint64_t nros_platform_epoch_us |
( |
void |
| ) |
|
Microseconds since the UNIX EPOCH (1970-01-01T00:00:00Z), or 0 when this platform has no wall-clock source.
READ THIS BEFORE REACHING FOR nros_platform_clock_ns (issue 0758). The two clocks in this header differ by one word in their names and by the only property that matters for interop:
nros_platform_clock_ns is MONOTONIC and boot-relative. Use it for durations, deadlines, spin gaps, timeouts — anything comparing two readings from THIS image. It is meaningless to a peer.
nros_platform_epoch_us is ABSOLUTE. Use it for message stamps and anything a peer will compare against its own clock.
Reaching for the wrong one does not fail to build and does not fail locally; it fails at a peer, which is the expensive place to find out. The concrete case is the consumer this exists for: an embedded island stamped control commands from its boot epoch and Autoware's vehicle_cmd_gate rejected every one as stale, so autonomous mode could never actuate.
0 MEANS "NO WALL CLOCK", not "the epoch". Per this header's clock rule (see the top of file: "If the platform has no clock, return `0`") this never errors. A caller that gets 0 knows the image cannot stamp absolute time and should keep publishing boot-relative stamps knowingly, rather than publishing a confidently wrong absolute one. 1970 is not a plausible reading, so the sentinel costs no real value.
Not required to be monotonic: a platform that acquires its epoch after boot (SNTP, RTC handoff) will JUMP when it does, and may jump backwards. Callers needing monotonicity use nros_platform_clock_ns.
Need not be ISR-safe, and unlike nros_platform_clock_ns need not be available immediately after platform init — a port that acquires its epoch over the network necessarily answers 0 until it has.
| size_t nros_platform_task_stack_unused_bytes |
( |
void |
| ) |
|
Smallest number of bytes ever left unused on the CALLING task's stack, or 0 if this port cannot report it.
HEADROOM, not usage, because that is what both kernels natively track and it is the number a safety argument needs: how close the worst observed excursion came to the end of the stack.
The heap has had nros_platform_heap_used_bytes since RFC-0034 D7; the stack had nothing, and stack overflow is the classic way one component corrupts another's state. ISO 26262 treats spatial freedom from interference as a first-class requirement and AUTOSAR pairs memory protection with stack monitoring for exactly this reason. Without a portable probe the only recourse is a per-RTOS one: this repo's Zephyr lane greps thread_analyzer printk output in CI, which is a text scrape of a debug facility standing in for a platform capability, and reports nothing on any other port.
SELF only, deliberately. Both kernels answer for the calling task with no handle (uxTaskGetStackHighWaterMark(NULL), k_thread_stack_space_get with k_current_get()), whereas answering for an arbitrary task needs a native handle this ABI does not carry – on Zephyr the task storage is a pthread_t and the mapping to k_thread * is not public. A task reporting its own headroom is also the shape the callers want: each tier says how much of its own stack it has ever needed.
0 means "this port does not instrument it", matching nros_platform_heap_used_bytes. It is not a claim that the stack is full.
| uint64_t nros_platform_time_now_ns |
( |
void |
| ) |
|
Wall-clock nanoseconds since the Unix epoch, or 0 if the platform has no real-time clock.
ONE symbol for one fact, mirroring what RFC-0073 / phase-352 did for the monotonic clock. It replaced time_now_ms + time_since_epoch_secs + time_since_epoch_nanos (issue 0532 item 5).
Why the split had to go, beyond tidiness: the ABI spent ONE INSTANT over two symbols, and each call sampled the clock separately (the POSIX port issued its own clock_gettime in each). A second boundary landing between the two reads paired the OLD second with the NEW sub-second remainder — a timestamp that jumped a full second BACKWARDS, rarely and silently. Both nros-core and nros-node carried a bounded re-read loop to paper over it; a single read cannot tear, so those loops are gone.
u64 ns spans ~584 years from 1970, so it also retires the uint32_t seconds field, which overflowed in 2106.
Ports convert from whatever they have — a port with only seconds returns secs * 1000000000ULL, and one with no RTC returns 0. Callers wanting milliseconds divide by 1000000; wanting a (secs, nanos) pair, divide and remainder by 1000000000.