zephyr/doc/kernel/synchronization/alerts.rst

241 lines
7.0 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 **pending count**, which records the number of pending alerts
that have yet to be received.
* An **count limit**, which specifies the maximum number of pending alerts
that will be recorded.
An alert must be initialized before it can be used. This establishes
its alert handler and sets the pending count to zero.
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 pending count to be incremented,
unless this would exceed the count limit.
* 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 pending
count is incremented, unless this would exceed the count limit.
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.
A thread accepts a pending alert by **receiving** the alert.
This decrements the pending count. If the pending count is currently zero,
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 must processes pending alerts one at a time. The thread
cannot receive multiple pending alerts in a single operation.
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. The alert allows
up to 10 unreceived alert signals to pend before it begins to ignore
new pending alerts.
.. 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, 10);
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, 10);
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`:
* :c:macro:`K_ALERT_DEFINE`
* :cpp:func:`k_alert_init()`
* :cpp:func:`k_alert_send()`
* :cpp:func:`k_alert_recv()`