| .. _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()` |