Browse Source
This sample illustrates how to properly use zbus priority boost feature. Signed-off-by: Rodrigo Peixoto <rodrigopex@gmail.com>pull/67192/head
8 changed files with 617 additions and 0 deletions
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
cmake_minimum_required(VERSION 3.20.0) |
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) |
||||
project(priority_boost) |
||||
|
||||
file(GLOB app_sources src/*.c) |
||||
target_sources(app PRIVATE ${app_sources}) |
@ -0,0 +1,197 @@
@@ -0,0 +1,197 @@
|
||||
.. zephyr:code-sample:: zbus-priority-boost |
||||
:name: zbus Priority Boost |
||||
:relevant-api: zbus_apis |
||||
|
||||
Illustrates zbus priority boost feature with a priority inversion scenario. |
||||
|
||||
Overview |
||||
******** |
||||
This sample implements a simple application that illustrates the priority boost feature. The |
||||
application implements the below figure scenario. When the priority boost feature is disabled, the |
||||
execution sequence presents a priority inversion problem, which may not affect much of the |
||||
developer's application. Those who want to avoid priority inversions between message subscribers and |
||||
plain subscribers should use the priority boost strategy. |
||||
|
||||
.. code-block:: c |
||||
|
||||
ZBUS_CHAN_DEFINE(chan_a, |
||||
int, |
||||
NULL, |
||||
NULL, |
||||
ZBUS_OBSERVERS(l1, ms1, ms2, s1, l2), |
||||
0 |
||||
); |
||||
|
||||
|
||||
.. figure:: zbus_publishing_process_example_scenario.svg |
||||
:alt: ZBus priority boost scenario example. |
||||
:width: 45% |
||||
|
||||
.. note:: |
||||
|
||||
The developer must use the :c:func:`zbus_obs_attach_to_thread` function to ensure a proper |
||||
priority boost execution. |
||||
|
||||
|
||||
Building and Running |
||||
******************** |
||||
|
||||
The figure below illustrates the execution of the cited scenario but with the priority boost |
||||
disabled. |
||||
|
||||
.. figure:: without_hlp_priority_boost_feature.svg |
||||
:alt: ZBus example scenario for priority boost disabled. |
||||
:width: 70% |
||||
|
||||
|
||||
It can be built and executed on QEMU as follows: |
||||
|
||||
.. zephyr-app-commands:: |
||||
:zephyr-app: samples/subsys/zbus/priority_boost -- -DCONFIG_ZBUS_PRIORITY_BOOST=n |
||||
:host-os: unix |
||||
:board: qemu_x86 |
||||
:goals: run |
||||
|
||||
Sample Output |
||||
============= |
||||
|
||||
.. code-block:: console |
||||
|
||||
I: -------------- |
||||
I: 0 -> T1: prio before 5 |
||||
I: 0 ---> L1: T1 prio 5 |
||||
I: 0 -> MS1: T1 prio 5 |
||||
I: 0 -> MS2: T1 prio 5 |
||||
I: 0 ---> L2: T1 prio 5 |
||||
I: 0 -> T1: prio after 5 |
||||
I: N -> S1: T1 prio 5 |
||||
I: 0 -> S1: T1 prio 5 |
||||
I: -------------- |
||||
I: 1 -> T1: prio before 5 |
||||
I: 1 ---> L1: T1 prio 5 |
||||
I: 1 -> MS1: T1 prio 5 |
||||
I: 1 -> MS2: T1 prio 5 |
||||
I: 1 ---> L2: T1 prio 5 |
||||
I: 1 -> T1: prio after 5 |
||||
I: N -> S1: T1 prio 5 |
||||
I: 1 -> S1: T1 prio 5 |
||||
I: -------------- |
||||
I: 2 -> T1: prio before 5 |
||||
I: 2 ---> L1: T1 prio 5 |
||||
I: 2 -> MS1: T1 prio 5 |
||||
I: 2 ---> L2: T1 prio 5 |
||||
I: 2 -> T1: prio after 5 |
||||
I: 2 -> MS2: T1 prio 5 |
||||
I: -------------- |
||||
I: 3 -> T1: prio before 5 |
||||
I: 3 ---> L1: T1 prio 5 |
||||
I: 3 -> MS1: T1 prio 5 |
||||
I: 3 ---> L2: T1 prio 5 |
||||
I: 3 -> T1: prio after 5 |
||||
I: 3 -> MS2: T1 prio 5 |
||||
I: -------------- |
||||
I: 4 -> T1: prio before 5 |
||||
I: 4 ---> L1: T1 prio 5 |
||||
I: 4 ---> L2: T1 prio 5 |
||||
I: 4 -> T1: prio after 5 |
||||
I: 4 -> MS2: T1 prio 5 |
||||
I: -------------- |
||||
I: 5 -> T1: prio before 5 |
||||
I: 5 ---> L1: T1 prio 5 |
||||
I: 5 ---> L2: T1 prio 5 |
||||
I: 5 -> T1: prio after 5 |
||||
I: 5 -> MS2: T1 prio 5 |
||||
I: -------------- |
||||
I: 6 -> T1: prio before 5 |
||||
I: 6 ---> L1: T1 prio 5 |
||||
I: 6 -> MS1: T1 prio 5 |
||||
I: 6 -> MS2: T1 prio 5 |
||||
I: 6 ---> L2: T1 prio 5 |
||||
I: 6 -> T1: prio after 5 |
||||
I: N -> S1: T1 prio 5 |
||||
I: 6 -> S1: T1 prio 5 |
||||
I: -------------- |
||||
<continues> |
||||
|
||||
|
||||
Exit QEMU by pressing :kbd:`CTRL+A` :kbd:`x`. |
||||
|
||||
|
||||
The figure below illustrates the execution of the same scenario but with the priority boost enabled. |
||||
The developer must enable the priority boost and properly attach all the observers to their threads. |
||||
|
||||
.. figure:: with_hlp_priority_boost_feature.svg |
||||
:alt: ZBus example scenario for priority boost enabled. |
||||
:width: 75% |
||||
|
||||
To execute the sample with priority boost feature enabled, run the following command: |
||||
|
||||
.. zephyr-app-commands:: |
||||
:zephyr-app: samples/subsys/zbus/priority_boost -- -DCONFIG_ZBUS_PRIORITY_BOOST=y |
||||
:host-os: unix |
||||
:board: qemu_x86 |
||||
:goals: run |
||||
|
||||
Sample Output |
||||
============= |
||||
|
||||
.. code-block:: console |
||||
|
||||
I: -------------- |
||||
I: 0 -> T1: prio before 5 |
||||
I: 0 ---> L1: T1 prio 1 |
||||
I: 0 ---> L2: T1 prio 1 |
||||
I: N -> S1: T1 prio 5 |
||||
I: 0 -> S1: T1 prio 5 |
||||
I: 0 -> MS1: T1 prio 5 |
||||
I: 0 -> MS2: T1 prio 5 |
||||
I: 0 -> T1: prio after 5 |
||||
I: -------------- |
||||
I: 1 -> T1: prio before 5 |
||||
I: 1 ---> L1: T1 prio 1 |
||||
I: 1 ---> L2: T1 prio 1 |
||||
I: N -> S1: T1 prio 5 |
||||
I: 1 -> S1: T1 prio 5 |
||||
I: 1 -> MS1: T1 prio 5 |
||||
I: 1 -> MS2: T1 prio 5 |
||||
I: 1 -> T1: prio after 5 |
||||
I: -------------- |
||||
I: 2 -> T1: prio before 5 |
||||
I: 2 ---> L1: T1 prio 2 |
||||
I: 2 ---> L2: T1 prio 2 |
||||
I: 2 -> MS1: T1 prio 5 |
||||
I: 2 -> MS2: T1 prio 5 |
||||
I: 2 -> T1: prio after 5 |
||||
I: -------------- |
||||
I: 3 -> T1: prio before 5 |
||||
I: 3 ---> L1: T1 prio 2 |
||||
I: 3 ---> L2: T1 prio 2 |
||||
I: 3 -> MS1: T1 prio 5 |
||||
I: 3 -> MS2: T1 prio 5 |
||||
I: 3 -> T1: prio after 5 |
||||
I: -------------- |
||||
I: 4 -> T1: prio before 5 |
||||
I: 4 ---> L1: T1 prio 3 |
||||
I: 4 ---> L2: T1 prio 3 |
||||
I: 4 -> MS2: T1 prio 5 |
||||
I: 4 -> T1: prio after 5 |
||||
I: -------------- |
||||
I: 5 -> T1: prio before 5 |
||||
I: 5 ---> L1: T1 prio 3 |
||||
I: 5 ---> L2: T1 prio 3 |
||||
I: 5 -> MS2: T1 prio 5 |
||||
I: 5 -> T1: prio after 5 |
||||
I: -------------- |
||||
I: 6 -> T1: prio before 5 |
||||
I: 6 ---> L1: T1 prio 1 |
||||
I: 6 ---> L2: T1 prio 1 |
||||
I: N -> S1: T1 prio 5 |
||||
I: 6 -> S1: T1 prio 5 |
||||
I: 6 -> MS1: T1 prio 5 |
||||
I: 6 -> MS2: T1 prio 5 |
||||
I: 6 -> T1: prio after 5 |
||||
I: -------------- |
||||
<continues> |
||||
|
||||
|
||||
Exit QEMU by pressing :kbd:`CTRL+A` :kbd:`x`. |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
CONFIG_LOG=y |
||||
CONFIG_LOG_MODE_MINIMAL=y |
||||
CONFIG_ZBUS_LOG_LEVEL_INF=y |
||||
CONFIG_ZBUS=y |
||||
CONFIG_ZBUS_MSG_SUBSCRIBER=y |
||||
CONFIG_HEAP_MEM_POOL_SIZE=1024 |
@ -0,0 +1,81 @@
@@ -0,0 +1,81 @@
|
||||
sample: |
||||
name: Priority boost |
||||
tests: |
||||
sample.zbus.non_priority_boost: |
||||
harness: console |
||||
harness_config: |
||||
type: multi_line |
||||
ordered: false |
||||
regex: |
||||
- "I: 0 -> T1: prio before 5" |
||||
- "I: 0 ---> L1: T1 prio 5" |
||||
- "I: 0 ---> L2: T1 prio 5" |
||||
- "I: 0 -> T1: prio after 5" |
||||
- "I: 1 -> T1: prio before 5" |
||||
- "I: 1 ---> L1: T1 prio 5" |
||||
- "I: 1 ---> L2: T1 prio 5" |
||||
- "I: 1 -> T1: prio after 5" |
||||
- "I: 2 -> T1: prio before 5" |
||||
- "I: 2 ---> L1: T1 prio 5" |
||||
- "I: 2 ---> L2: T1 prio 5" |
||||
- "I: 2 -> T1: prio after 5" |
||||
- "I: 3 -> T1: prio before 5" |
||||
- "I: 3 ---> L1: T1 prio 5" |
||||
- "I: 3 ---> L2: T1 prio 5" |
||||
- "I: 3 -> T1: prio after 5" |
||||
- "I: 4 -> T1: prio before 5" |
||||
- "I: 4 ---> L1: T1 prio 5" |
||||
- "I: 4 ---> L2: T1 prio 5" |
||||
- "I: 4 -> T1: prio after 5" |
||||
- "I: 5 -> T1: prio before 5" |
||||
- "I: 5 ---> L1: T1 prio 5" |
||||
- "I: 5 ---> L2: T1 prio 5" |
||||
- "I: 5 -> T1: prio after 5" |
||||
- "I: 6 -> T1: prio before 5" |
||||
- "I: 6 ---> L1: T1 prio 5" |
||||
- "I: 6 ---> L2: T1 prio 5" |
||||
- "I: 6 -> T1: prio after 5" |
||||
extra_configs: |
||||
- CONFIG_ZBUS_PRIORITY_BOOST=n |
||||
tags: zbus |
||||
integration_platforms: |
||||
- qemu_x86 |
||||
sample.zbus.priority_boost: |
||||
harness: console |
||||
harness_config: |
||||
type: multi_line |
||||
ordered: false |
||||
regex: |
||||
- "I: 0 -> T1: prio before 5" |
||||
- "I: 0 ---> L1: T1 prio 1" |
||||
- "I: 0 ---> L2: T1 prio 1" |
||||
- "I: 0 -> T1: prio after 5" |
||||
- "I: 1 -> T1: prio before 5" |
||||
- "I: 1 ---> L1: T1 prio 1" |
||||
- "I: 1 ---> L2: T1 prio 1" |
||||
- "I: 1 -> T1: prio after 5" |
||||
- "I: 2 -> T1: prio before 5" |
||||
- "I: 2 ---> L1: T1 prio 2" |
||||
- "I: 2 ---> L2: T1 prio 2" |
||||
- "I: 2 -> T1: prio after 5" |
||||
- "I: 3 -> T1: prio before 5" |
||||
- "I: 3 ---> L1: T1 prio 2" |
||||
- "I: 3 ---> L2: T1 prio 2" |
||||
- "I: 3 -> T1: prio after 5" |
||||
- "I: 4 -> T1: prio before 5" |
||||
- "I: 4 ---> L1: T1 prio 3" |
||||
- "I: 4 ---> L2: T1 prio 3" |
||||
- "I: 4 -> T1: prio after 5" |
||||
- "I: 5 -> T1: prio before 5" |
||||
- "I: 5 ---> L1: T1 prio 3" |
||||
- "I: 5 ---> L2: T1 prio 3" |
||||
- "I: 5 -> T1: prio after 5" |
||||
- "I: 6 -> T1: prio before 5" |
||||
- "I: 6 ---> L1: T1 prio 1" |
||||
- "I: 6 ---> L2: T1 prio 1" |
||||
- "I: 6 -> T1: prio after 5" |
||||
extra_configs: |
||||
- CONFIG_ZBUS_PRIORITY_BOOST=y |
||||
tags: zbus |
||||
integration_platforms: |
||||
- qemu_x86 |
@ -0,0 +1,148 @@
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Rodrigo Peixoto <rodrigopex@gmail.com> |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <zephyr/kernel.h> |
||||
#include <zephyr/logging/log.h> |
||||
#include <zephyr/zbus/zbus.h> |
||||
LOG_MODULE_DECLARE(zbus, CONFIG_ZBUS_LOG_LEVEL); |
||||
|
||||
ZBUS_CHAN_DEFINE(chan_a, int, NULL, NULL, ZBUS_OBSERVERS(l1, ms1, ms2, s1, l2), 0); |
||||
|
||||
static void t1_thread(void *ptr1, void *ptr2, void *ptr3); |
||||
K_THREAD_DEFINE(t1_id, CONFIG_MAIN_STACK_SIZE, t1_thread, NULL, NULL, NULL, 5, 0, 0); |
||||
|
||||
ZBUS_SUBSCRIBER_DEFINE(s1, 4); |
||||
static void s1_thread(void *ptr1, void *ptr2, void *ptr3) |
||||
{ |
||||
ARG_UNUSED(ptr1); |
||||
ARG_UNUSED(ptr2); |
||||
ARG_UNUSED(ptr3); |
||||
|
||||
int err; |
||||
int a = 0; |
||||
const struct zbus_channel *chan; |
||||
|
||||
IF_ENABLED(CONFIG_ZBUS_PRIORITY_BOOST, (zbus_obs_attach_to_thread(&s1);)); |
||||
|
||||
while (1) { |
||||
err = zbus_sub_wait(&s1, &chan, K_FOREVER); |
||||
if (err) { |
||||
return; |
||||
} |
||||
|
||||
/* Faking some workload */ |
||||
k_busy_wait(200000); |
||||
|
||||
LOG_INF("N -> S1: T1 prio %d", k_thread_priority_get(t1_id)); |
||||
|
||||
err = zbus_chan_read(chan, &a, K_FOREVER); |
||||
if (err) { |
||||
return; |
||||
} |
||||
LOG_INF("%d -> S1: T1 prio %d", a, k_thread_priority_get(t1_id)); |
||||
} |
||||
} |
||||
K_THREAD_DEFINE(s1_id, CONFIG_MAIN_STACK_SIZE, s1_thread, NULL, NULL, NULL, 2, 0, 0); |
||||
|
||||
ZBUS_MSG_SUBSCRIBER_DEFINE(ms1); |
||||
static void ms1_thread(void *ptr1, void *ptr2, void *ptr3) |
||||
{ |
||||
ARG_UNUSED(ptr1); |
||||
ARG_UNUSED(ptr2); |
||||
ARG_UNUSED(ptr3); |
||||
|
||||
int err; |
||||
const struct zbus_channel *chan; |
||||
int a = 0; |
||||
|
||||
IF_ENABLED(CONFIG_ZBUS_PRIORITY_BOOST, (zbus_obs_attach_to_thread(&ms1);)); |
||||
|
||||
while (1) { |
||||
err = zbus_sub_wait_msg(&ms1, &chan, &a, K_FOREVER); |
||||
if (err) { |
||||
return; |
||||
} |
||||
|
||||
/* Faking some workload */ |
||||
k_busy_wait(200000); |
||||
|
||||
LOG_INF("%d -> MS1: T1 prio %d", a, k_thread_priority_get(t1_id)); |
||||
} |
||||
} |
||||
K_THREAD_DEFINE(ms1_id, CONFIG_MAIN_STACK_SIZE, ms1_thread, NULL, NULL, NULL, 3, 0, 0); |
||||
|
||||
ZBUS_MSG_SUBSCRIBER_DEFINE(ms2); |
||||
static void ms2_thread(void *ptr1, void *ptr2, void *ptr3) |
||||
{ |
||||
ARG_UNUSED(ptr1); |
||||
ARG_UNUSED(ptr2); |
||||
ARG_UNUSED(ptr3); |
||||
|
||||
int err; |
||||
const struct zbus_channel *chan; |
||||
int a = 0; |
||||
|
||||
IF_ENABLED(CONFIG_ZBUS_PRIORITY_BOOST, (zbus_obs_attach_to_thread(&ms2);)); |
||||
|
||||
while (1) { |
||||
err = zbus_sub_wait_msg(&ms2, &chan, &a, K_FOREVER); |
||||
if (err) { |
||||
return; |
||||
} |
||||
|
||||
/* Faking some workload */ |
||||
k_busy_wait(200 * USEC_PER_MSEC); |
||||
|
||||
LOG_INF("%d -> MS2: T1 prio %d", a, k_thread_priority_get(t1_id)); |
||||
} |
||||
} |
||||
K_THREAD_DEFINE(ms2_id, CONFIG_MAIN_STACK_SIZE, ms2_thread, NULL, NULL, NULL, 4, 0, 0); |
||||
|
||||
static void l1_callback(const struct zbus_channel *chan) |
||||
{ |
||||
LOG_INF("%d ---> L1: T1 prio %d", *((int *)zbus_chan_const_msg(chan)), |
||||
k_thread_priority_get(t1_id)); |
||||
} |
||||
ZBUS_LISTENER_DEFINE(l1, l1_callback); |
||||
|
||||
static void l2_callback(const struct zbus_channel *chan) |
||||
{ |
||||
LOG_INF("%d ---> L2: T1 prio %d", *((int *)zbus_chan_const_msg(chan)), |
||||
k_thread_priority_get(t1_id)); |
||||
} |
||||
ZBUS_LISTENER_DEFINE(l2, l2_callback); |
||||
|
||||
static void t1_thread(void *ptr1, void *ptr2, void *ptr3) |
||||
{ |
||||
ARG_UNUSED(ptr1); |
||||
ARG_UNUSED(ptr2); |
||||
ARG_UNUSED(ptr3); |
||||
|
||||
int err; |
||||
int a = 0; |
||||
|
||||
while (1) { |
||||
LOG_INF("--------------"); |
||||
|
||||
if (a == 2) { |
||||
zbus_obs_set_enable(&s1, false); |
||||
} else if (a == 4) { |
||||
zbus_obs_set_enable(&ms1, false); |
||||
} else if (a == 6) { |
||||
zbus_obs_set_enable(&s1, true); |
||||
zbus_obs_set_enable(&ms1, true); |
||||
} |
||||
|
||||
LOG_INF("%d -> T1: prio before %d", a, k_thread_priority_get(k_current_get())); |
||||
err = zbus_chan_pub(&chan_a, &a, K_FOREVER); |
||||
if (err) { |
||||
return; |
||||
} |
||||
LOG_INF("%d -> T1: prio after %d", a, k_thread_priority_get(k_current_get())); |
||||
++a; |
||||
|
||||
k_msleep(2000); |
||||
} |
||||
} |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 30 KiB |
Loading…
Reference in new issue