zephyr/doc/kernel_v2/synchronization/events.rst

237 lines
6.8 KiB
ReStructuredText

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