240 lines
7.0 KiB
ReStructuredText
240 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`:
|
|
|
|
* :cpp:func:`k_alert_handler_set()`
|
|
* :cpp:func:`k_alert_send()`
|
|
* :cpp:func:`k_alert_recv()`
|