From dbd99e3927430dc4fd1a05a7ab10e6c31f4e3ed3 Mon Sep 17 00:00:00 2001 From: Rodrigo Peixoto Date: Sat, 7 Oct 2023 23:18:41 -0300 Subject: [PATCH] doc: zbus: update documentation with changes for 3.5 Add documentation for the new way to storage observers, the message subscribers, and the confirmed message sample. Signed-off-by: Rodrigo Peixoto --- .../zbus/images/zbus_observation_mask.svg | 152 ++++++++++++ doc/services/zbus/images/zbus_operations.svg | 55 ++++- .../zbus_publishing_process_example.svg | 73 +++++- .../zbus_publishing_process_example2.svg | 67 ++++++ ...us_publishing_process_example_scenario.svg | 38 ++- .../zbus/images/zbus_type_of_observers.svg | 14 ++ doc/services/zbus/index.rst | 221 +++++++++++++----- 7 files changed, 549 insertions(+), 71 deletions(-) create mode 100644 doc/services/zbus/images/zbus_observation_mask.svg create mode 100644 doc/services/zbus/images/zbus_publishing_process_example2.svg create mode 100644 doc/services/zbus/images/zbus_type_of_observers.svg diff --git a/doc/services/zbus/images/zbus_observation_mask.svg b/doc/services/zbus/images/zbus_observation_mask.svg new file mode 100644 index 000000000000000..4405a8f3e4aed37 --- /dev/null +++ b/doc/services/zbus/images/zbus_observation_mask.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/services/zbus/images/zbus_operations.svg b/doc/services/zbus/images/zbus_operations.svg index d0f96881745145f..a6ecce15c281a03 100644 --- a/doc/services/zbus/images/zbus_operations.svg +++ b/doc/services/zbus/images/zbus_operations.svg @@ -1,3 +1,52 @@ - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/services/zbus/images/zbus_publishing_process_example.svg b/doc/services/zbus/images/zbus_publishing_process_example.svg index 0e9ba3dc35162b5..72d7430a1637670 100644 --- a/doc/services/zbus/images/zbus_publishing_process_example.svg +++ b/doc/services/zbus/images/zbus_publishing_process_example.svg @@ -1,3 +1,70 @@ - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/services/zbus/images/zbus_publishing_process_example2.svg b/doc/services/zbus/images/zbus_publishing_process_example2.svg new file mode 100644 index 000000000000000..f71a3c29ca292fb --- /dev/null +++ b/doc/services/zbus/images/zbus_publishing_process_example2.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/services/zbus/images/zbus_publishing_process_example_scenario.svg b/doc/services/zbus/images/zbus_publishing_process_example_scenario.svg index 6d473a45a83e6e9..abcb27de2171188 100644 --- a/doc/services/zbus/images/zbus_publishing_process_example_scenario.svg +++ b/doc/services/zbus/images/zbus_publishing_process_example_scenario.svg @@ -1,3 +1,35 @@ - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/services/zbus/images/zbus_type_of_observers.svg b/doc/services/zbus/images/zbus_type_of_observers.svg new file mode 100644 index 000000000000000..34b1be733076b6f --- /dev/null +++ b/doc/services/zbus/images/zbus_type_of_observers.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/doc/services/zbus/index.rst b/doc/services/zbus/index.rst index 441e9f887f4a8f9..e52588caf5f9643 100644 --- a/doc/services/zbus/index.rst +++ b/doc/services/zbus/index.rst @@ -1,9 +1,9 @@ .. _zbus: -Zephyr message bus (zbus) -######################### +Zephyr Bus (ZBus) +################# -The :dfn:`Zephyr message bus - Zbus` is a lightweight and flexible message bus enabling a simple way for threads to talk to one another. +The :dfn:`Zephyr Bus - ZBus` is a lightweight and flexible software bus enabling a simple way for threads to talk to one another. .. contents:: :local: @@ -11,48 +11,64 @@ The :dfn:`Zephyr message bus - Zbus` is a lightweight and flexible message bus e Concepts ******** - -Threads can broadcast messages to all interested observers using zbus. Many-to-many communication is possible. The bus implements message-passing and publish/subscribe communication paradigms that enable threads to communicate synchronously or asynchronously through shared memory. The communication through zbus is channel-based, where threads publish and read to and from using messages. Additionally, threads can observe channels and receive notifications from the bus when the channels are modified. The figure below shows an example of a typical application using zbus in which the application logic (hardware independent) talks to other threads via message bus. Note that the threads are decoupled from each other because they only use zbus' channels and do not need to know each other to talk. +Threads can broadcast messages to all interested observers using ZBus. Many-to-many communication is possible. The bus implements message-passing and publish/subscribe communication paradigms that enable threads to communicate synchronously or asynchronously through shared memory. The communication through ZBus is channel-based, where threads publish and read to and from using messages. Additionally, threads can observe channels and receive notifications from the bus when the channels are modified. The figure below shows an example of a typical application using ZBus in which the application logic (hardware independent) talks to other threads via software bus. Note that the threads are decoupled from each other because they only use ZBus' channels and do not need to know each other to talk. .. figure:: images/zbus_overview.svg :alt: zbus usage overview :width: 75% - A typical zbus application architecture. + A typical ZBus application architecture. The bus comprises: * Set of channels that consists of a unique identifier, its control metadata information, and the message itself; * :dfn:`Virtual distributed event dispatcher` (VDED), the bus logic responsible for sending notifications to the observers. The VDED logic runs inside the publishing action in the same thread context, giving the bus an idea of a distributed execution. When a thread publishes to a channel, it also propagates the notifications to the observers; -* Threads (subscribers) and callbacks (listeners) publishing, reading, and receiving notifications from the bus. +* Threads (subscribers and message subscribers) and callbacks (listeners) publishing, reading, and receiving notifications from the bus. .. figure:: images/zbus_anatomy.svg - :alt: Zbus anatomy + :alt: ZBus anatomy :width: 70% - Zbus anatomy. + ZBus anatomy. + +The bus makes the publish, read, claim, finish, notify, and subscribe actions available over channels. Publishing, reading, claiming, and finishing are available in all RTOS thread contexts. However, it cannot run inside Interrupt Service Routines (ISR) because it uses mutexes to control channel access, and mutexes cannot work appropriately inside ISRs. The publish and read operations are simple and fast; the procedure is a mutex locking followed by a memory copy to and from a shared memory region and then a mutex unlocking. Another essential aspect of ZBus is the observers. There are three types of observers: + +.. figure:: images/zbus_type_of_observers.svg + :alt: ZBus observers type + :width: 70% -The bus makes the publish, read, and subscribe actions available over channels. Publishing and reading are available in all RTOS thread contexts. However, it cannot run inside Interrupt Service Routines (ISR) because it uses mutexes to control channels access, and mutexes cannot work appropriately inside ISRs. The publish and read operations are simple and fast; the procedure is a mutex locking followed by a memory copy to and from a shared memory region and then a mutex unlocking. Another essential aspect of zbus is the observers, which can be: + ZBus observers. -* Static; defined in compile time. It is not possible to remove it at runtime, but it is possible to suppress it by calling the :c:func:`zbus_obs_set_enable`; -* Dynamic; it can be added and removed to and from a channel at runtime. +* Listeners, a callback that the event dispatcher executes every time an observed channel is published or notified; +* Subscriber, a thread-based observer that relies internally on a message queue where the event dispatcher puts a changed channel's reference every time an observed channel is published or notified; +* Message subscribers, a thread-based observer that relies internally on a FIFO where the event dispatcher puts a copy of the message every time an observed channel is published or notified. + +Channel observation structures define the relationship between a channel and its observers. For every observation, a pair channel/observer. Developers can statically allocate observation using the :c:macro:`ZBUS_CHAN_DEFINE` or :c:macro:`ZBUS_CHAN_ADD_OBS`. There are also runtime observers, enabling developers to create runtime observations. It is possible to disable an observer entirely or observations individually. The event dispatcher will ignore disabled observers and observations. + +.. figure:: images/zbus_observation_mask.svg + :alt: ZBus observation mask. + :width: 75% + ZBus observation mask. -For illustration purposes, suppose a usual sensor-based solution in the figure below. When the timer is triggered, it pushes an action to a work queue that publishes to the ``Start trigger`` channel. As the sensor thread subscribed to the ``Start trigger`` channel, it fetches the sensor data. Notice the VDED executes the blink callback because it also listens to the ``Start trigger`` channel. When the sensor data is ready, the sensor thread publishes it to the ``Sensor data`` channel. The core thread, as a ``Sensor data`` channel subscriber, processes the sensor data and stores it in an internal sample buffer. It repeats until the sample buffer is full; when it happens, the core thread aggregates the sample buffer information, prepares a package, and publishes that to the ``Payload`` channel. The Lora thread receives that because it is a ``Payload`` channel subscriber and sends the payload to the cloud. When it completes the transmission, the Lora thread publishes to the ``Transmission done`` channel. The VDED executes the blink callback again since it listens to the ``Transmission done`` channel. +The above figure illustrates some states, from (a) to (d), for channels from ``C1`` to ``C5``, ``Subscriber 1``, and the observations. The last two are in orange to indicate they are dynamically allocated (runtime observation). (a) shows that the observer and all observations are enabled. (b) shows the observer is disabled, so the event dispatcher will ignore it. (c) shows the observer enabled. However, there is one static observer disabled. The event dispatcher will only stop sending notifications from channel ``C3``. In (d), the event dispatcher will stop sending notifications from channels ``C3`` and ``C5`` to ``Subscriber 1``. + + +For illustration purposes, suppose a usual sensor-based solution in the figure below. When the timer is triggered, it pushes an action to a work queue that publishes to the ``Trigger`` channel. As the sensor thread subscribed to the ``Trigger`` channel, it fetches the sensor data. Notice the VDED executes the ``Blink`` because it also listens to the ``Trigger`` channel. When the sensor data is ready, the sensor thread publishes it to the ``Sensor data`` channel. The core thread, as a ``Sensor data`` channel message subscriber, receives the message, processes the sensor data and stores it in an internal sample buffer. It repeats until the sample buffer is full; when it happens, the core thread aggregates the sample buffer information, prepares a package, and publishes that to the ``Payload`` channel. The Lora thread receives that because it is a ``Payload`` channel message subscriber and sends the payload to the cloud. When it completes the transmission, the Lora thread publishes to the ``Transmission done`` channel. The VDED executes the ``Blink`` again since it listens to the ``Transmission done`` channel. .. figure:: images/zbus_operations.svg - :alt: Zbus sensor-based application - :width: 80% + :alt: ZBus sensor-based application + :width: 85% - Zbus sensor-based application. + ZBus sensor-based application. -This way of implementing the solution makes the application more flexible, enabling us to change things independently. For example, we want to change the trigger from a timer to a button press. We can do that, and the change does not affect other parts of the system. Likewise, we would like to change the communication interface from LoRa to Bluetooth; we only need to change the LoRa thread. No other change is required in order to make that work. Thus, the developer would do that for every block of the image. Based on that, there is a sign zbus promotes decoupling in the system architecture. +This way of implementing the solution makes the application more flexible, enabling us to change things independently. For example, we want to change the trigger from a timer to a button press. We can do that, and the change does not affect other parts of the system. Likewise, we would like to change the communication interface from LoRa to Bluetooth; we only need to change the LoRa thread. No other change is required in order to make that work. Thus, the developer would do that for every block of the image. Based on that, there is a sign ZBus promotes decoupling in the system architecture. -Another important aspect of using zbus is the reuse of system modules. If a code portion with well-defined behaviors (we call that module) only uses zbus channels and not hardware interfaces, it can easily be reused in other solutions. The new solution must implement the interfaces (set of channels) the module needs to work. That indicates zbus could improve the module reuse. +Another important aspect of using ZBus is the reuse of system modules. If a code portion with well-defined behaviors (we call that module) only uses ZBus channels and not hardware interfaces, it can easily be reused in other solutions. The new solution must implement the interfaces (set of channels) the module needs to work. That indicates ZBus could improve the module reuse. -The last important note is the zbus solution reach. We can count on many ways of using zbus to enable the developer to be as free as possible to create what they need. For example, messages can be dynamic or static allocated; notifications can be synchronous or asynchronous; the developer can control the channel in so many different ways claiming the channel, developers can add their metadata information to a channel by using the user-data field, the discretionary use of a validator enables the systems to be accurate over message format, and so on. Those characteristics increase the solutions that can be done with zbus and make it a good fit as an open-source community tool. +The last important note is the ZBus solution reach. We can count on many ways of using ZBus to enable the developer to be as free as possible to create what they need. For example, messages can be dynamic or static allocated; notifications can be synchronous or asynchronous; the developer can control the channel in so many different ways claiming the channel, developers can add their metadata information to a channel by using the user-data field, the discretionary use of a validator enables the systems to be accurate over message format, and so on. Those characteristics increase the solutions that can be done with ZBus and make it a good fit as an open-source community tool. .. _Virtual Distributed Event Dispatcher: @@ -65,30 +81,30 @@ The VDED execution always happens in the publishing's (thread) context. So it ca * The channel mutex is acquired; * The channel receives the new message via direct copy (by a raw :c:func:`memcpy`); -* The event dispatcher logic executes the listeners and pushes the channel's reference to the subscribers' notification message queue in the same sequence they appear on the channel observers' list. The listeners can perform non-copy quick access to the constant message reference directly (via the :c:func:`zbus_chan_const_msg` function) since the channel is still locked; +* The event dispatcher logic executes the listeners, sends a copy of the message to the message subscribers, and pushes the channel's reference to the subscribers' notification message queue in the same sequence they appear on the channel observers' list. The listeners can perform non-copy quick access to the constant message reference directly (via the :c:func:`zbus_chan_const_msg` function) since the channel is still locked; * At last, the publishing function unlocks the channel. -To illustrate the VDED execution, consider the example illustrated below. We have four threads in ascending priority T1, T2, T3, and T4 (the highest priority); two listeners, L1 and L2; and channel A. Supposing L1, L2, T2, T3, and T4 observer channel A. +To illustrate the VDED execution, consider the example illustrated below. We have four threads in ascending priority ``S1``, ``MS2``, ``MS1``, and ``T1`` (the highest priority); two listeners, ``L1`` and ``L2``; and channel A. Supposing ``L1``, ``L2``, ``MS1``, ``MS2``, and ``S1`` observer channel A. .. figure:: images/zbus_publishing_process_example_scenario.svg - :alt: Zbus example scenario - :width: 55% + :alt: ZBus example scenario + :width: 45% - Zbus VDED execution example scenario. + ZBus VDED execution example scenario. The following code implements channel A. Note the ``struct a_msg`` is illustrative only. .. code-block:: c - ZBUS_CHAN_DEFINE(a_chan, /* Name */ - struct a_msg, /* Message type */ + ZBUS_CHAN_DEFINE(a_chan, /* Name */ + struct a_msg, /* Message type */ - NULL, /* Validator */ - NULL, /* User Data */ - ZBUS_OBSERVERS(L1, L2, T2, T3, T4), /* observers */ - ZBUS_MSG_INIT(0) /* Initial value {0} */ + NULL, /* Validator */ + NULL, /* User Data */ + ZBUS_OBSERVERS(L1, L2, MS1, MS2, S1), /* observers */ + ZBUS_MSG_INIT(0) /* Initial value {0} */ ); @@ -96,24 +112,24 @@ In the figure below, the letters indicate some action related to the VDED execut .. figure:: images/zbus_publishing_process_example.svg - :alt: Zbus publish processing detail + :alt: ZBus publish processing detail :width: 85% - Zbus VDED execution detail. + ZBus VDED execution detail for priority T1 > MS1 > MS2 > S1. -The figure above illustrates the actions performed during the VDED execution when T1 publishes to channel A. Thus, the figure below describes the actions (represented by a letter) of the VDED execution. +The figure above illustrates the actions performed during the VDED execution when T1 publishes to channel A. Thus, the figure below describes the activities (represented by a letter) of the VDED execution. The scenario considers the following priorities: T1 > MS1 > MS2 > S1. T1 has the highest priority. -.. list-table:: VDED execution steps in detail. +.. list-table:: VDED execution steps in detail for priority T1 > MS1 > MS2 > S1. :widths: 5 65 :header-rows: 1 * - Actions - Description * - a - - T1 starts and at some point, publishes to channel A. + - T1 starts and, at some point, publishes to channel A. * - b - The publishing (VDED) process starts. The VDED locks the channel A's mutex. * - c @@ -122,44 +138,101 @@ The figure above illustrates the actions performed during the VDED execution whe * - d, e - The VDED executes L1 and L2 in the respective sequence. Inside the listeners, usually, there is a call to the :c:func:`zbus_chan_const_msg` function, which provides a direct constant reference to channel A's message. It is quick, and no copy is needed here. - * - f, g, h - - The VDED pushes the notification message to queues of T2, T3, and T4 sequentially. Notice the threads get ready to execute right after receiving the notification. However, they go to a pending state because they cannot access the channel since it is still locked. At that moment, the T1 thread gets its priority elevated (priority inheritance due to the mutex) to the highest pending thread (caused by channel A unavailability). In that case, T4's priority. It ensures the T1 will finish the VDED execution as quickly as possible without preemption from threads with priority below the engaged ones. + * - f, g + - The VDED copies the message and sends that to MS1 and MS2 sequentially. Notice the threads get ready to execute right after receiving the notification. However, they go to a pending state because they have less priority than T1. + * - h + - The VDED pushes the notification message to the queue of S1. Notice the thread gets ready to execute right after receiving the notification. However, it goes to a pending state because it cannot access the channel since it is still locked. At that moment, the T1 thread gets its priority elevated (priority inheritance due to the mutex) to the highest pending thread (caused by channel A unavailability). In that case, S1's priority. It ensures the T1 will finish the VDED execution as quickly as possible without preemption from threads with priority below the engaged ones. * - i - VDED finishes the publishing by unlocking channel A. - * - j, k - - The T4 leaves the pending state since channel A is not locked. It gets in the CPU again and starts executing. As it did receive a notification from channel A, it performs a channel read (as simple as lock, memory copy, unlock), continues its execution, and goes out the CPU. + * - j, k, l + - The S1 leaves the pending state since channel A is not locked. It gets in the CPU again and starts executing. As it did receive a notification from channel A, it performs a channel read (as simple as lock, memory copy, unlock), continues its execution, and goes out the CPU. + * - m + - S1 goes out of the MCU. + + * - n, o + - MS2 and MS1 execute and finish their workload. + + +The figure below illustrates the actions performed during the VDED execution when T1 publishes to channel A. Thus, the figure below describes the activities (represented by a letter) of the VDED execution. The scenario considers the following priorities: T1 < MS1 < MS2 < S1. + +.. figure:: images/zbus_publishing_process_example2.svg + :alt: ZBus publish processing detail + :width: 85% + + ZBus VDED execution detail for priority T1 < MS1 < MS2 < S1. + +.. list-table:: VDED execution steps in detail for priority T1 < MS1 < MS2 < S1. + :widths: 5 65 + :header-rows: 1 + + * - Actions + - Description + * - a + - T1 starts and, at some point, publishes to channel A. + * - b + - The publishing (VDED) process starts. The VDED locks the channel A's mutex. + * - c + - The VDED copies the T1 message to the channel A message. + + * - d, e + - The VDED executes L1 and L2 in the respective sequence. Inside the listeners, usually, there is a call to the :c:func:`zbus_chan_const_msg` function, which provides a direct constant reference to channel A's message. It is quick, and no copy is needed here. - * - l,m, n - - Now, T3 can access the channel. It repeats the same steps from T4 (j and k). T2 does the same. That is the end of the VDED execution! + * - f + - The VDED copies the message and sends that to MS1. MS1 preempts T1 and starts working. + After that, the T1 regain MCU. + + * - g + - The VDED copies the message and sends that to MS2. MS2 preempts T1 and starts working. + After that, the T1 regain MCU. + + * - h + - The VDED pushes the notification message to the queue of S1. Notice the thread gets ready to execute right after receiving the notification. However, it goes to a pending state because it cannot access the channel since it is still locked. At that moment, the T1 thread gets its priority elevated (priority inheritance due to the mutex) to the highest pending thread (caused by channel A unavailability). In that case, S1's priority. It ensures the T1 will finish the VDED execution as quickly as possible without preemption from threads with priority below the engaged ones. + + * - i + - VDED finishes the publishing by unlocking channel A. + + * - j, k, l + - The S1 leaves the pending state since channel A is not locked. It gets in the CPU again and starts executing. As it did receive a notification from channel A, it performs a channel read (as simple as lock, memory copy, unlock), continues its execution, and goes out the CPU. Limitations =========== -Based on the fact that developers can use zbus to solve many different problems, some challenges arise. Zbus will not solve every problem, so it is necessary to analyze the situation to be sure zbus is applicable. For instance, based on the zbus benchmark, it would not be well suited to a high-speed stream of bytes between threads. The `Pipe` kernel object solves this kind of need. +Based on the fact that developers can use ZBus to solve many different problems, some challenges arise. ZBus will not solve every problem, so it is necessary to analyze the situation to be sure ZBus is applicable. For instance, based on the ZBus benchmark, it would not be well suited to a high-speed stream of bytes between threads. The `Pipe` kernel object solves this kind of need. Delivery guarantees ------------------- -Zbus always delivers the messages to the listeners. However, there are no message delivery guarantees for subscribers because zbus only sends the notification, but the message reading depends on the subscriber's implementation. This is because channels have a mutex protected singleton objects for which message transfer is used. In other words, it can be seen as a single size queue where publishers always overwrite if queue is full. It is possible to increase the delivery rate by following design tips: +ZBus always delivers the messages to the listeners and message subscribers. However, there are no message delivery guarantees for subscribers because ZBus only sends the notification, but the message reading depends on the subscriber's implementation. This is because channels have a mutex protected singleton objects for which message transfer is used. In other words, it can be seen as a single size queue where publishers always overwrite if queue is full. It is possible to increase the delivery rate by following design tips: * Keep the listeners quick-as-possible (deal with them as ISRs). If some processing is needed, consider submitting a work to a work-queue; * Try to give producers a high priority to avoid losses; * Leave spare CPU for observers to consume data produced; * Consider using message queues or pipes for intensive byte transfers. +.. warning:: + ZBus uses :zephyr_file:`include/zephyr/net/buf.h` (network buffers) to exchange data with message subscribers. So, chose carefully the + configurations :c:macro:`CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_SIZE` and :c:macro:`CONFIG_HEAP_MEM_POOL_SIZE`. + They are crucial to a proper VDED execution (delivery garantee) considering message subscribers. Message delivery sequence ------------------------- -The listeners (synchronous observers) will follow the channel definition sequence as the notification and message consumption sequence. However, the subscribers, as they have an asynchronous nature, will all receive the notification as the channel definition sequence but only will consume the data when they execute again. Hence, the delivery respects the order, but the priority assigned to the subscribers will define the reaction sequence. The delivery sequence is first the static observers and then the runtime ones. +The message delivery will follow the precedency: + +#. Observers defined in a channel using the :c:macro:`ZBUS_CHAN_DEFINE` (in the definition sequence); +#. Observers defined using the :c:macro:`ZBUS_CHAN_ADD_OBS` based on the sequence priority (parameter of the macro); +#. The latest is the runtime observers in the addition sequence using the :c:func:`zbus_chan_add_obs`. + +.. note:: + The VDED will ignore all disabled observers or observations. Usage ***** -Zbus operation depends on channels and observers. Therefore, it is necessary to determine its message and observers list during the channel definition. A message is a regular C struct; the observer can be a subscriber (asynchronous) or a listener (synchronous). +ZBus operation depends on channels and observers. Therefore, it is necessary to determine its message and observers list during the channel definition. A message is a regular C struct; the observer can be a subscriber (asynchronous) or a listener (synchronous). The following code defines and initializes a regular channel and its dependencies. This channel exchanges accelerometer data, for example. @@ -176,7 +249,8 @@ The following code defines and initializes a regular channel and its dependencie NULL, /* Validator */ NULL, /* User Data */ - ZBUS_OBSERVERS(my_listener, my_subscriber), /* observers */ + ZBUS_OBSERVERS(my_listener, my_subscriber, + my_msg_subscriber), /* observers */ ZBUS_MSG_INIT(.x = 0, .y = 0, .z = 0) /* Initial value */ ); @@ -212,6 +286,23 @@ The following code defines and initializes a regular channel and its dependencie } K_THREAD_DEFINE(subscriber_task_id, 512, subscriber_task, NULL, NULL, NULL, 3, 0, 0); + ZBUS_MSG_SUBSCRIBER_DEFINE(my_msg_subscriber); + static void msg_subscriber_task(void *sub) + { + const struct zbus_channel *chan; + + struct acc_msg acc = {0}; + + while (!zbus_sub_wait_msg(&my_msg_subscriber, &chan, &acc, K_FOREVER)) { + if (&acc_chan == chan) { + LOG_INF("From msg subscriber -> Acc x=%d, y=%d, z=%d", acc.x, acc.y, acc.z); + } + } + } + K_THREAD_DEFINE(msg_subscriber_task_id, 1024, msg_subscriber_task, NULL, NULL, NULL, 3, 0, 0); + + + It is possible to add static observers to a channel using the :c:macro:`ZBUS_CHAN_ADD_OBS`. We call that a post-definition static observer. The command enables us to indicate an initialization priority that affects the observers' initialization order. The priority param only affects the post-definition static observers. There is no possibility to overwrite the execution sequence of the static observers. .. note:: @@ -243,7 +334,7 @@ Channels can have a ``validator function`` that enables a channel to accept only ZBUS_MSG_INIT(.move = 0) /* Initial value */ ); -The following sections describe in detail how to use zbus features. +The following sections describe in detail how to use ZBus features. .. _publishing to a channel: @@ -251,7 +342,7 @@ The following sections describe in detail how to use zbus features. Publishing to a channel ======================= -Messages are published to a channel in zbus by calling :c:func:`zbus_chan_pub`. For example, the following code builds on the examples above and publishes to channel ``acc_chan``. The code is trying to publish the message ``acc1`` to channel ``acc_chan``, and it will wait up to one second for the message to be published. Otherwise, the operation fails. As can be inferred from the code sample, it's OK to use stack allocated messages since VDED copies the data internally. +Messages are published to a channel in ZBus by calling :c:func:`zbus_chan_pub`. For example, the following code builds on the examples above and publishes to channel ``acc_chan``. The code is trying to publish the message ``acc1`` to channel ``acc_chan``, and it will wait up to one second for the message to be published. Otherwise, the operation fails. As can be inferred from the code sample, it's OK to use stack allocated messages since VDED copies the data internally. .. code-block:: c @@ -266,7 +357,7 @@ Messages are published to a channel in zbus by calling :c:func:`zbus_chan_pub`. Reading from a channel ====================== -Messages are read from a channel in zbus by calling :c:func:`zbus_chan_read`. So, for example, the following code tries to read the channel ``acc_chan``, which will wait up to 500 milliseconds to read the message. Otherwise, the operation fails. +Messages are read from a channel in ZBus by calling :c:func:`zbus_chan_read`. So, for example, the following code tries to read the channel ``acc_chan``, which will wait up to 500 milliseconds to read the message. Otherwise, the operation fails. .. code-block:: c @@ -282,7 +373,7 @@ Messages are read from a channel in zbus by calling :c:func:`zbus_chan_read`. So Forcing channel notification ============================ -It is possible to force zbus to notify a channel's observers by calling :c:func:`zbus_chan_notify`. For example, the following code builds on the examples above and forces a notification for the channel ``acc_chan``. Note this can send events with no message, which does not require any data exchange. See the code example under `Claim and finish a channel`_ where this may become useful. +It is possible to force ZBus to notify a channel's observers by calling :c:func:`zbus_chan_notify`. For example, the following code builds on the examples above and forces a notification for the channel ``acc_chan``. Note this can send events with no message, which does not require any data exchange. See the code example under `Claim and finish a channel`_ where this may become useful. .. code-block:: c @@ -294,7 +385,7 @@ It is possible to force zbus to notify a channel's observers by calling :c:func: Declaring channels and observers ================================ -For accessing channels or observers from files other than its defining files, it is necessary to declare them by calling :c:macro:`ZBUS_CHAN_DECLARE` and :c:macro:`ZBUS_OBS_DECLARE`. In other words, zbus channel definitions and declarations with the same channel names in different files would point to the same (global) channel. Thus, developers should be careful about existing channels, and naming new channels or linking will fail. It is possible to declare more than one channel or observer on the same call. The following code builds on the examples above and displays the defined channels and observers. +For accessing channels or observers from files other than its defining files, it is necessary to declare them by calling :c:macro:`ZBUS_CHAN_DECLARE` and :c:macro:`ZBUS_OBS_DECLARE`. In other words, ZBus channel definitions and declarations with the same channel names in different files would point to the same (global) channel. Thus, developers should be careful about existing channels, and naming new channels or linking will fail. It is possible to declare more than one channel or observer on the same call. The following code builds on the examples above and displays the defined channels and observers. .. code-block:: c @@ -305,7 +396,7 @@ For accessing channels or observers from files other than its defining files, it Iterating over channels and observers ===================================== -Zbus subsystem also implements :ref:`Iterable Sections ` for channels and observers, for which there are supporting APIs like :c:func:`zbus_iterate_over_channels`, :c:func:`zbus_iterate_over_channels_with_user_data`, :c:func:`zbus_iterate_over_observers` and :c:func:`zbus_iterate_over_observers_with_user_data`. This feature enables developers to call a procedure over all declared channels, where the procedure parameter is a :c:struct:`zbus_channel`. The execution sequence is in the alphabetical name order of the channels (see :ref:`Iterable Sections ` documentation for details). Zbus also implements this feature for :c:struct:`zbus_observer`. +ZBus subsystem also implements :ref:`Iterable Sections ` for channels and observers, for which there are supporting APIs like :c:func:`zbus_iterate_over_channels`, :c:func:`zbus_iterate_over_channels_with_user_data`, :c:func:`zbus_iterate_over_observers` and :c:func:`zbus_iterate_over_observers_with_user_data`. This feature enables developers to call a procedure over all declared channels, where the procedure parameter is a :c:struct:`zbus_channel`. The execution sequence is in the alphabetical name order of the channels (see :ref:`Iterable Sections ` documentation for details). ZBus also implements this feature for :c:struct:`zbus_observer`. .. code-block:: c @@ -389,7 +480,7 @@ The code will log the following output: Advanced channel control ======================== -Zbus was designed to be as flexible and extensible as possible. Thus, there are some features designed to provide some control and extensibility to the bus. +ZBus was designed to be as flexible and extensible as possible. Thus, there are some features designed to provide some control and extensibility to the bus. Listeners message access ------------------------ @@ -421,7 +512,7 @@ Claim and finish a channel To take more control over channels, two functions were added :c:func:`zbus_chan_claim` and :c:func:`zbus_chan_finish`. With these functions, it is possible to access the channel's metadata safely. When a channel is claimed, no actions are available to that channel. After finishing the channel, all the actions are available again. .. warning:: - Never change the fields of the channel struct directly. It may cause zbus behavior inconsistencies and scheduling issues. + Never change the fields of the channel struct directly. It may cause ZBus behavior inconsistencies and scheduling issues. .. warning:: Do not use these functions inside an ISR. @@ -463,7 +554,7 @@ The following code has the exact behavior of the code in :ref:`reading from a ch Runtime observer registration ----------------------------- -It is possible to add observers to channels in runtime. This feature uses the heap to allocate the nodes dynamically. The heap size limits the number of dynamic observers Zbus can create. Therefore, set the :kconfig:option:`CONFIG_ZBUS_RUNTIME_OBSERVERS` to enable the feature. It is possible to adjust the heap size by changing the configuration :kconfig:option:`CONFIG_HEAP_MEM_POOL_SIZE`. The following example illustrates the runtime registration usage. +It is possible to add observers to channels in runtime. This feature uses the heap to allocate the nodes dynamically. The heap size limits the number of dynamic observers ZBus can create. Therefore, set the :kconfig:option:`CONFIG_ZBUS_RUNTIME_OBSERVERS` to enable the feature. It is possible to adjust the heap size by changing the configuration :kconfig:option:`CONFIG_HEAP_MEM_POOL_SIZE`. The following example illustrates the runtime registration usage. @@ -482,35 +573,41 @@ It is possible to add observers to channels in runtime. This feature uses the he Samples ******* -For a complete overview of zbus usage, take a look at the samples. There are the following samples available: +For a complete overview of ZBus usage, take a look at the samples. There are the following samples available: * :zephyr:code-sample:`zbus-hello-world` illustrates the code used above in action; * :zephyr:code-sample:`zbus-work-queue` shows how to define and use different kinds of observers. Note there is an example of using a work queue instead of executing the listener as an execution option; -* :zephyr:code-sample:`zbus-dyn-channel` demonstrates how to use dynamically allocated exchanging data in zbus; +* :zephyr:code-sample:`zbus-msg-subscriber` illustrates how to use message subscribers. +* :zephyr:code-sample:`zbus-dyn-channel` demonstrates how to use dynamically allocated exchanging data in ZBus; * :zephyr:code-sample:`zbus-uart-bridge` shows an example of sending the operation of the channel to a host via serial; * :zephyr:code-sample:`zbus-remote-mock` illustrates how to implement an external mock (on the host) to send and receive messages to and from the bus. * :zephyr:code-sample:`zbus-runtime-obs-registration` illustrates a way of using the runtime observer registration feature; +* :zephyr:code-sample:`zbus-confirmed-channel` implements a way of implement confirmed channel only with subscribers. * :zephyr:code-sample:`zbus-benchmark` implements a benchmark with different combinations of inputs. Suggested Uses ************** -Use zbus to transfer data (messages) between threads in one-to-one, one-to-many, and many-to-many synchronously or asynchronously. Choosing the proper observer type is crucial. Use subscribers for scenarios that can tolerate message losses and duplications; when they cannot, use listeners. In addition to the listener, another asynchronous message processing mechanism (like :ref:`message queues `) may be necessary to retain the pending message until it gets processed. +Use ZBus to transfer data (messages) between threads in one-to-one, one-to-many, and many-to-many synchronously or asynchronously. Choosing the proper observer type is crucial. Use subscribers for scenarios that can tolerate message losses and duplications; when they cannot, use message subscribers (if you need a thread) or listeners (if you need to be lean and fast). In addition to the listener, another asynchronous message processing mechanism (like :ref:`message queues `) may be necessary to retain the pending message until it gets processed. .. note:: - Zbus can be used to transfer streams from the producer to the consumer. However, this can increase zbus' communication latency. So maybe consider a Pipe a good alternative for this communication topology. + ZBus can be used to transfer streams from the producer to the consumer. However, this can increase ZBus' communication latency. So maybe consider a Pipe a good alternative for this communication topology. Configuration Options ********************* -For enabling zbus, it is necessary to enable the :kconfig:option:`CONFIG_ZBUS` option. +For enabling ZBus, it is necessary to enable the :kconfig:option:`CONFIG_ZBUS` option. Related configuration options: - +* :kconfig:option:`CONFIG_ZBUS_CHANNELS_SYS_INIT_PRIORITY` determine the :c:macro:`SYS_INIT` priority used by ZBus to organize the channels observations by channel. * :kconfig:option:`CONFIG_ZBUS_CHANNEL_NAME` enables the name of channels to be available inside the channels metadata. The log uses this information to show the channels' names; * :kconfig:option:`CONFIG_ZBUS_OBSERVER_NAME` enables the name of observers to be available inside the channels metadata; +* :kconfig:option:`CONFIG_ZBUS_MSG_SUBSCRIBER` enables the message subscriber observer type; +* :kconfig:option:`CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_DYNAMIC` uses the heap to allocate message buffers; +* :kconfig:option:`CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC` uses the stack to allocate message buffers; +* :kconfig:option:`CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_SIZE` the available number of message buffers to be used simultaneously; +* :kconfig:option:`CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_STATIC_DATA_SIZE` the biggest message of ZBus channels to be transported into a message buffer; * :kconfig:option:`CONFIG_ZBUS_RUNTIME_OBSERVERS` enables the runtime observer registration. -* :kconfig:option:`CONFIG_ZBUS_CHANNELS_SYS_INIT_PRIORITY` determine the :c:macro:`SYS_INIT` priority used by Zbus to organize the channels observations by channel. API Reference *************