zephyr/doc/kernel_v2/synchronization/alerts.rst

237 lines
6.8 KiB
ReStructuredText

.. _alerts_v2:
Alerts
######
An :dfn:`alert` is a kernel object that allows an application to perform
asynchronous signalling when a condition of interest occurs.
.. contents::
:local:
:depth: 2
Concepts
********
Any number of alerts can be defined. Each alert is referenced by
its memory address.
An alert has the following key properties:
* An **alert handler**, which specifies the action to be taken
when the alert is signalled. The action may instruct the system workqueue
to execute a function to process the alert, mark the alert as pending
so it can be processed later by a thread, or ignore the alert.
* An **alert pending flag**, which is set if the alert is signalled
and an alert handler function has not processed the signal.
An alert must be initialized before it can be used. This establishes
its alert handler and clears the alert pending flag.
Alert Lifecycle
===============
An ISR or a thread signals an alert by **sending** the alert
when a condition of interest occurs that cannot be handled by the detector.
Each time an alert is sent, the kernel examines its alert handler
to determine what action to take.
* :c:macro:`K_ALERT_IGNORE` causes the alert to be ignored.
* :c:macro:`K_ALERT_DEFAULT` causes the alert pending flag to be set.
* Any other value is assumed to be the address of an alert handler function,
and is invoked by the system workqueue thread. If the function returns
zero, the signal is deemed to have been consumed; otherwise, the alert
pending flag is set.
The kernel ensures that the alert handler function is executed once
for each time an alert is sent, even if the alert is sent multiple times
in rapid succession.
An alert whose alert pending flag becomes set remains pending until
the alert is accepted by a thread. This clears the alert pending flag.
A thread accepts a pending alert by **receiving** the alert.
If the alert's pending flag is currently clear, the thread may choose
to wait for the alert to become pending.
Any number of threads may wait for a pending alert simultaneously;
when the alert is pended it is accepted by the highest priority thread
that has waited longest.
.. note::
A thread that accepts an alert cannot directly determine how many times
the alert pending flag was set since the alert was last accepted.
Comparison to Unix-style Signals
================================
Zephyr alerts are somewhat akin to Unix-style signals, but have a number of
significant differences. The most notable of these are:
* A Zephyr alert cannot be blocked; it is always delivered to its alert
handler immediately.
* A Zephyr alert pends *after* it has been delivered to its alert handler,
and only if an alert handler function does not consume the alert.
* Zephyr has no pre-defined alerts or actions. All alerts are application
defined, and all have a default action that pends the alert.
Implementation
**************
Defining an Alert
=================
An alert is defined using a variable of type :c:type:`struct k_alert`.
It must then be initialized by calling :cpp:func:`k_alert_init()`.
The following code defines and initializes an alert.
.. code-block:: c
extern int my_alert_handler(struct k_alert *alert);
struct k_alert my_alert;
k_alert_init(&my_alert, my_alert_handler);
Alternatively, an alert can be defined and initialized at compile time
by calling :c:macro:`K_ALERT_DEFINE()`.
The following code has the same effect as the code segment above.
.. code-block:: c
extern int my_alert_handler(struct k_alert *alert);
K_ALERT_DEFINE(my_alert, my_alert_handler);
Signaling an Alert
==================
An alert is signalled by calling :cpp:func:`k_alert_send()`.
The following code illustrates how an ISR can signal an alert
to indicate that a key press has occurred.
.. code-block:: c
extern int my_alert_handler(struct k_alert *alert);
K_ALERT_DEFINE(my_alert, my_alert_handler);
void keypress_interrupt_handler(void *arg)
{
...
k_alert_send(&my_alert);
...
}
Handling an Alert
=================
An alert handler function is used when a signalled alert should not be ignored
or immediately pended. It has the following form:
.. code-block:: c
int <function_name>(struct k_alert *alert)
{
/* catch the alert signal; return zero if the signal is consumed, */
/* or non-zero to let the alert pend */
...
}
The following code illustrates an alert handler function that processes
key presses detected by an ISR (as shown in the previous section).
.. code-block:: c
int my_alert_handler(struct k_alert *alert_id_is_unused)
{
/* determine what key was pressed */
char c = get_keypress();
/* do complex processing of the keystroke */
...
/* signalled alert has been consumed */
return 0;
}
Accepting an Alert
==================
A pending alert is accepted by a thread by calling :cpp:func:`k_alert_recv()`.
The following code is an alternative to the code in the previous section.
It uses a dedicated thread to do very complex processing
of key presses that would otherwise monopolize the system workqueue.
The alert handler function is now used only to filter out unwanted key press
alerts, allowing the dedicated thread to wake up and process key press alerts
only when a numeric key is pressed.
.. code-block:: c
int my_alert_handler(struct k_alert *alert_id_is_unused)
{
/* determine what key was pressed */
char c = get_keypress();
/* signal thread only if key pressed was a digit */
if ((c >= '0') && (c <= '9')) {
/* save key press information */
...
/* signalled alert should be pended */
return 1;
} else {
/* signalled alert has been consumed */
return 0;
}
}
void keypress_thread(void *unused1, void *unused2, void *unused3)
{
/* consume numeric key presses */
while (1) {
/* wait for a key press alert to pend */
k_alert_recv(&my_alert, K_FOREVER);
/* process saved key press, which must be a digit */
...
}
}
Suggested Uses
**************
Use an alert to minimize ISR processing by deferring interrupt-related
work to a thread to reduce the amount of time interrupts are locked.
Use an alert to allow the kernel's system workqueue to handle an alert,
rather than defining an application thread to handle it.
Use an alert to allow the kernel's system workqueue to pre-process an alert,
prior to letting an application thread handle it.
Configuration Options
*********************
Related configuration options:
* None.
APIs
****
The following alert APIs are provided by :file:`kernel.h`:
* :cpp:func:`k_alert_handler_set()`
* :cpp:func:`k_alert_send()`
* :cpp:func:`k_alert_recv()`