Skip to content

system/zbus: Port the Zephyr zbus message bus to NuttX - #3743

Merged
acassis merged 1 commit into
apache:masterfrom
JorgeGzm:port_zbus
Sep 21, 2026
Merged

acassis merged 1 commit into
apache:masterfrom
JorgeGzm:port_zbus

Conversation

@JorgeGzm

Copy link
Copy Markdown
Member

Summary

This PR ports the Zephyr RTOS zbus (a many-to-many message bus with
typed channels and decoupled observers) to NuttX, preserving the
original declarative API (ZBUS_CHAN_DEFINE, ZBUS_LISTENER_DEFINE,
ZBUS_SUBSCRIBER_DEFINE, zbus_chan_pub/read/notify, ...) so that
existing Zephyr application code and documentation translate directly.
Original zbus by Rodrigo Peixoto (Apache-2.0); copyright preserved in
the derived files.

Ported features: listeners (synchronous callbacks), subscribers
(queue of channel references), message subscribers (ordered message
copies), async listeners (callback on the LP work queue), a deferred
ISR-safe publisher (ZBUS_ISR_PUBLISHER_DEFINE / zbus_isr_pub),
runtime observers, per-observation notification masks, observer
enable/disable, message validators, channel user data, publish
statistics, lookup by name/id and channel/observer iteration.

Everything is built on native NuttX primitives, with no compatibility
shim layer:

Zephyr NuttX
k_sem channel lock + priority boost (HLP) sem_t + native CONFIG_PRIORITY_INHERITANCE
k_msgq / k_fifo + net_buf pools kernel message queues (file_mq_*), lazy-opened; mq payload copy replaces net_buf entirely
k_work work_queue() (LP queue)
SYS_INIT lazy init via pthread_once()
iterable sections include/nuttx/iterable_sections.h (companion PR)
k_timeout_t milliseconds, CLOCK_MONOTONIC deadlines (ZBUS_NO_WAIT / ZBUS_FOREVER)

Board integration (why one linker-script line matters here)

In Zephyr, zbus works on every board out of the box because boards have
no linker scripts: a single common per-arch linker template already
includes the shared common-rom.ld/common-ram.ld fragments where the
zbus iterable sections are collected. In NuttX each board owns its .ld,
so an adopting board needs one of:

  • two #include <nuttx/linker/common-{rom,ram}.ld> lines in its board
    script (done for linum-stm32h753bi, the first adopter, in the
    companion PR), or
  • CONFIG_ZBUS_LINKER_INSERT=y (zero-touch INSERT AFTER mode, with
    the MEMORY-region constraint documented in the companion PR).

This is documented in the Kconfig help, in README.rst and in the
Sphinx page added by the companion PR.

Also included:

  • examples/zbus (CONFIG_EXAMPLES_ZBUS): a runnable demo with one channel,
    one listener, one subscriber, runtime masking.
  • testing/zbus (CONFIG_TESTING_ZBUS): a cmocka suite, 16 tests
    covering the full API surface: pub/read/listener/subscriber,
    multi-channel index isolation, validators, message subscribers
    (ordered copies), bit-exact float/double payload delivery
    (sensor-style message with a float-math validator rejecting
    non-finite samples), claim/finish/notify, masks, enable/disable,
    runtime observers (error paths included), async listeners (bursts),
    ISR publisher, from_name/from_id, iteration, accessors, and
    timeout/overflow semantics.

Not ported (documented in the "Not ported" section of the docs):
multi-domain proxy agent (experimental upstream), direct publishing from
ISRs (the deferred zbus_isr_pub helper covers the use case), the
priority-boost/HLP scheme (superseded by native priority inheritance)
and the net_buf pool machinery (unnecessary with mq payload copies).

Impact

  • New optional application (CONFIG_ZBUS, default n); no impact
    when disabled.
  • Requirements: FLAT build (uses kernel-side file_mq_* so queues
    survive the creating task, since mqd_t is a per-task fd in NuttX);
    CONFIG_MQ_MAXMSGSIZE >= sizeof(pointer) + CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE when message subscribers are
    enabled (checked and documented in Kconfig); board linker integration
    as described above.
  • Build process: Make and CMake supported.
  • Documentation: README.rst here; full Sphinx documentation (with
    the upstream zbus diagrams, Apache-2.0) lands with the companion
    nuttx PR.
  • License: Apache-2.0, derived from Zephyr zbus; original copyright
    retained.

Testing

Host: Ubuntu 24.04.4 x86_64, Arm GNU Toolchain 13.2.rel1
(arm-none-eabi-gcc 13.2.1).

Target: linum-stm32h753bi:zbus (STM32H753BI; board configuration
added by the companion nuttx PR): CONFIG_ZBUS with all options
enabled (message/async/ISR observers, runtime observers),
CONFIG_EXAMPLES_ZBUS, CONFIG_TESTING_ZBUS. Flashed via ST-LINK V3;
console on the ST-LINK VCP.

nsh> uname -a
NuttX 13.0.1-RC0 a1b0941bec Aug 20 2026 21:17:15 arm linum-stm32h753bi

Example (zbus): 5 messages published; listener correctly skips the
masked message #4; listener notified before the subscriber (observer
priority order):

zbus: publishing 5 messages to acc_chan
zbus:  listener: x=1 y=10 z=100
zbus:  subscriber: x=1 y=10 z=100
...
zbus: listener masked
zbus:  subscriber: x=3 y=30 z=300
zbus: listener unmasked
zbus:  subscriber: x=4 y=40 z=400
zbus:  listener: x=5 y=50 z=500
zbus:  subscriber: x=5 y=50 z=500
zbus: done

cmocka suite: 16/16 passing, run twice in the same boot (guards
against the lazy-init/fd-lifetime regression class):

nsh> cmocka_zbus_test
[==========] tests: Running 16 test(s).
...
[ RUN      ] test_float_payload
[       OK ] test_float_payload
...
[==========] tests: 16 test(s) run.
[  PASSED  ] 16 test(s).

(The could not notify observer: -42 line during test_timeouts is the
expected -ENOMSG queue-overflow path being exercised.)

Builds: Make (make -j) and CMake (cmake -GNinja + ninja) both
OK for the target config; _zbus_* iterable-section symbols verified in
the map on both. Stock build with CONFIG_ZBUS disabled: no zbus
sections/symbols (no-op). tools/checkpatch.sh -g on the commit: all
checks pass.

The companion nuttx PR adds a linum-stm32h753bi:zbus board
configuration reproducing this exact run
(./tools/configure.sh linum-stm32h753bi:zbus); since that defconfig
enables Kconfig symbols introduced here, the two PRs should land
together.

Comment thread system/zbus/Kconfig Outdated
Comment thread system/zbus/Kconfig Outdated
Comment thread system/zbus/README.rst Outdated
@JorgeGzm
JorgeGzm force-pushed the port_zbus branch 3 times, most recently from dfd91d6 to 45592db Compare August 25, 2026 21:35

@linguini1 linguini1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the Assisted-by field to your commit message to indicate AI use.

@JorgeGzm
JorgeGzm force-pushed the port_zbus branch 2 times, most recently from 0806149 to 1c29f07 Compare August 27, 2026 13:50
acassis
acassis previously approved these changes Aug 28, 2026
Comment thread include/system/zbus_macros.h Outdated
Comment thread include/system/zbus_macros.h Outdated
Comment thread include/system/zbus.h
Comment thread system/zbus/zbus_priv.h Outdated
Comment thread system/zbus/zbus_priv.h Outdated
Comment thread system/zbus/zbus.c Outdated
Comment thread system/zbus/zbus.c Outdated
Comment thread system/zbus/zbus.c Outdated
Comment thread system/zbus/zbus.c Outdated
Comment thread system/zbus/zbus.c Outdated
Comment thread system/zbus/zbus.c Outdated
Comment thread system/zbus/zbus.c Outdated
Comment thread system/zbus/zbus.c Outdated
Comment thread system/zbus/zbus.c
Comment thread system/zbus/zbus.c
Comment thread system/zbus/zbus.c
Port of the Zephyr RTOS zbus (many-to-many message bus with typed
channels and decoupled observers), built entirely on native NuttX
primitives and preserving the original declarative API
(ZBUS_CHAN_DEFINE, ZBUS_LISTENER_DEFINE, ZBUS_SUBSCRIBER_DEFINE, ...).

Features: listeners (synchronous callbacks), subscribers (queue of
channel references), message subscribers (ordered message copies),
async listeners (callback on a dedicated task), runtime observers,
per-observation notification masks, observer enable/disable, message
validators, channel user data, publish statistics, lookup by
name/numeric id and channel/observer iteration.

Mapping to NuttX primitives:
- Channel/observer registration: link-time iterable sections
  (include/nuttx/iterable_sections.h); the observers of a channel are
  named after their position in the definition, so the linker sorts the
  notification order, and the declarative macros are built on
  nuttx/macro.h (CONCATENATE, FOREACH_ARG and the FOREACH_IDX_ARG added
  in a companion nuttx commit) rather than on a private macro engine.
  Notification masks live in .bss with their initial value preserved in
  ROM and applied on lazy init.
- Channel lock: sem_t (enable CONFIG_PRIORITY_INHERITANCE instead of
  the Zephyr priority-boost/HLP); timeouts are computed with the
  clock_timespec_* helpers from nuttx/clock.h.
- Subscriber queues: kernel message queues (file_mq_*) opened lazily
  via pthread_once, usable from any task; mq payload copying replaces
  the Zephyr net_buf machinery entirely.
- Async listeners: one task per listener (task_create, priority and
  stack size configurable) blocking on the listener queue; a task
  rather than a pthread so it outlives the first API caller.
- Timeouts: milliseconds with CLOCK_MONOTONIC deadlines
  (ZBUS_NO_WAIT/ZBUS_FOREVER).

Includes a runnable example (examples/zbus, CONFIG_EXAMPLES_ZBUS) and a
cmocka test suite (testing/zbus, CONFIG_TESTING_ZBUS) covering the full
API: 17/17 tests passing on linum-stm32h753bi hardware, including
multi-channel index grouping, mask semantics, runtime observer error
paths, notification order (the observers of a channel run in the order
they are listed, and an observation bound with ZBUS_CHAN_ADD_OBS() runs
after all of them), queue overflow/timeout semantics, async listener
bursts,
bit-exact float/double payload delivery across every observer type
(sensor-style messages with a float-math validator) and an
interrupt-driven publisher (kernel timer interrupt -> signal -> sampling
thread -> zbus_chan_pub, the recommended pattern for interrupt sources).

Requirements: FLAT build; CONFIG_MQ_MAXMSGSIZE >= pointer size +
CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE for message subscribers; board
linker script including <nuttx/linker/common-rom.ld> or the generic
CONFIG_ITERABLE_SECTIONS_LINKER_INSERT mode.

Not ported: multi-domain proxy agent (experimental upstream); publishing
from interrupt handlers (userspace library: hand the data to a thread).

Documentation lives in the nuttx repository
(Documentation/applications/system/zbus).

Assisted-by: Claude Code
Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
@acassis

acassis commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

@xiaoxiang781216 according with @JorgeGzm the CI will report error because the PR I merged depends on this one here. PTAL

@acassis

acassis commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

ping @cederom

@cederom cederom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @JorgeGzm amazing work! :-)

@acassis
acassis merged commit ee20ddd into apache:master Sep 21, 2026
41 checks passed
@JorgeGzm
JorgeGzm deleted the port_zbus branch September 22, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants