I/O observations, capacity and retained recovery¶
IO07, IO09 and IO10 use explicit caller-owned diagnostic objects. Default request, stream, slot and owner fields remain unchanged; the platform creates no global sampler, hidden thread or automatic device-recovery policy. Diagnostics observe actual state; deadlines, errors and diagnostic detach never revoke accepted borrows.
Observing a request through stable identity¶
Core’s request_observer.h depends only on Core. The caller binds a zeroed nx_request_observer_t to a stable slot and nonzero epoch. Sample first verifies the same live incarnation before accessing the request; it caches no request pointer. After slot release or reuse, the old observer returns STATE. Epochs never wrap for reuse; the existing slot contract rejects further admission at the limit.
The caller excludes concurrent sample versus slot bind/release, request prepare/reuse and observer mutation, and ensures bound storage remains valid. Core adds no IRQ mask, OS lock or multicore synchronization on the caller’s behalf. Normal device executors may still advance requests; final result/count is read only after acquire-observed SETTLED. While borrowed, outputs are result=BUSY and count=0, preventing unpublished results from appearing valid.
Timestamps share the request deadline’s monotonic clock domain. Backward time returns IO, preserving outputs and the last valid time; diagnostic counters saturate. max_gap and transitions describe differences between samples, not hardware completion time, all internal transitions or WCET. Detach removes only observation binding and is permitted during QUARANTINED, but actual device borrows must still be drained afterward.
Outputs must be separate from the complete descriptor, payload and control storage. Public Core knows only the request base size and cannot validate the full derived-descriptor range or detect freed C pointers. Owners usually release their control slot before outer SETTLED; observing that slot then returns STATE. To continue sampling after final-result publication, the caller should retain its own independent stable slot and release it after its readers exit.
Explicit owner progress diagnostics¶
Optional Nexus::OwnerObserver is called by the sole owner executor. Each call executes exactly one real nx_bus_owner_service and returns its result unchanged. BUSY is counted separately from other errors. Backward observation time sets diagnostic_result without overwriting service_result or preventing continued drain.
It then copies active ticket, request state and idle under the existing short metadata guard, without hardware calls or slot-array scans. Copies retain no request/payload references. quarantines counts observations, not failed operations. Service SUCCESS does not mean the current operation finally succeeded; final results still come through acquire-observed request results. It does not infer nonexistent errors from void device service.
Observer, owner, slots and guard/executor/clock must retain the same binding and lifecycle. Producers keep the original owner guard; diagnostics add no device hot-path vtable layer. After detach, the owner still owns all undrained work. Diagnostic outputs must not overwrite accepted operations or payloads.
Stream snapshots and capacity budgets¶
nx_stream_observe copies maintained aggregates under one short IRQ guard, with work independent of block count and no slot scans or payload reads. free_blocks is the total free count; next_free says whether the normal stream producer cursor’s slot is available. Out-of-order release can leave free blocks while that cursor remains occupied. A preposted producer has its own cursor and must determine its next target through the corresponding reservation API; next_free is not permission for the next continuous-DMA target.
nx_stream_budget_calculate is a pure cold-path check. The caller declares rate, block units, bytes per unit, maximum FIFO-frontier release delay, hardware-target count and spare count. Its calculation is:
retained = ceil(rate * release_delay_us / (block_units * 1000000))
required_blocks = hardware_targets + spare_blocks + retained
required_payload_bytes = required_blocks * block_units * unit_bytes
block_fill_us = floor(block_units * 1000000 / rate)
Rate and block use the same units. For example, multichannel ADC may use complete scans, with unit_bytes explicitly covering every channel per scan. Both uint64 multiplications and size_t addition/multiplication are overflow-checked. Invalid input or overflow preserves old output and returns INVALID/EXHAUSTED; it never saturates into an apparently valid budget. configured_blocks >= required_blocks meets declared capacity; target-replacement latency must be strictly below the rounded-down fill time. This does not create backpressure or remove FIFO blocking, CT ABA or real clock/IRQ latency. Physical qualification still needs measured rates and worst-case service gaps for the board.
External application quarantine recovery policy¶
nexus-examples/apps/uart_owner_runtime/quarantine_policy.c retains its old SDK pin and directly composes existing BusOwner APIs. Actual main owner-progress sites use this policy so service errors enter observable fault stages instead of being silently discarded with (void). BUSY is normal waiting.
Real errors close admission, publish FAULT_OBSERVED and establish explicit finite cleanup-time/step budgets. Backward time, deadline-add overflow or budget exhaustion publishes ESCALATED_RETAINED. Later calls still perform actual service; there is no worker kill, reset, automatic replay or undrained-buffer return. Actual idle eventually publishes LOAN_DRAINED, proving only owner loans ended. Producer join, IRQ detach, hardware stop, observer join and notification destruction still follow original lifecycle order.
The fault sink runs outside the guard and must be bounded and non-reentrant. Applications retain stages and fault state and wake the coordinator; product safe outputs, device reset and field handling remain external product-project responsibilities. This example establishes no safety level.
Storage and verification boundaries¶
The following sizeof fixture uses the actual Cortex-M4F GNU Arm 14.3 -fshort-enums ABI; array symbols are ARM-compiled and read with nm. It measures explicit object layout only, not whole-image code size or physical cycles.
Explicit object |
Bytes |
|---|---|
request observer / copied observation |
48 / 40 |
owner observer / copied observation |
96 / 72 |
stream copied observation |
32 |
stream budget input / result |
48 / 32 |
Measured default sizes are request 24, request slot 16, stream 40, stream slot 40, BusOwner 84 and owner slot 48 bytes. Default structures gain no fields; no sidecar is created when observers are unselected. Other compilers, enum ABIs and pointer widths require fresh measurement rather than reuse of these values.
New Google suites contain 15 observer/budget and 7 descriptor/provider state-machine cases. Native UART executes 32 sequences, SPI 16 sequences, and ADC 4 consume/stop sequences; these loops are not additional Google cases. I2C public-dispatch sequences mock external asynchronous-provider dependencies to prove public borrow/retry contracts, without claiming Native I2C asynchronous hardware. Six quarantine-policy cases were added within the external example’s 28 actual owner/lifecycle Google cases. First real stub RED and final GREEN logs, fresh XML, commands and return codes are retained in tools/testing/evidence/io-observer-iteration/. ARM32 capacity checks exposed a strict type-limits compilation error; RED/GREEN for the same production ABI and extreme-capacity regressions are retained together.
These are development software evidence. Clean-source SDK, external consumers and the complete candidate matrix bind final sources again through the unified release process. Boards are not connected; physical DMA target switching, error injection, UART timing and safe outputs have not been executed.