zephyr/doc/kernel/services/polling.rst

344 lines
12 KiB
ReStructuredText

.. _polling_v2:
Polling API
###########
The polling API is used to wait concurrently for any one of multiple conditions
to be fulfilled.
.. contents::
:local:
:depth: 2
Concepts
********
The polling API's main function is :c:func:`k_poll`, which is very similar
in concept to the POSIX :c:func:`poll` function, except that it operates on
kernel objects rather than on file descriptors.
The polling API allows a single thread to wait concurrently for one or more
conditions to be fulfilled without actively looking at each one individually.
There is a limited set of such conditions:
- a semaphore becomes available
- a kernel FIFO contains data ready to be retrieved
- a kernel message queue contains data ready to be retrieved
- a kernel pipe contains data ready to be retrieved
- a poll signal is raised
A thread that wants to wait on multiple conditions must define an array of
**poll events**, one for each condition.
All events in the array must be initialized before the array can be polled on.
Each event must specify which **type** of condition must be satisfied so that
its state is changed to signal the requested condition has been met.
Each event must specify what **kernel object** it wants the condition to be
satisfied.
Each event must specify which **mode** of operation is used when the condition
is satisfied.
Each event can optionally specify a **tag** to group multiple events together,
to the user's discretion.
Apart from the kernel objects, there is also a **poll signal** pseudo-object
type that be directly signaled.
The :c:func:`k_poll` function returns as soon as one of the conditions it
is waiting for is fulfilled. It is possible for more than one to be fulfilled
when :c:func:`k_poll` returns, if they were fulfilled before
:c:func:`k_poll` was called, or due to the preemptive multi-threading
nature of the kernel. The caller must look at the state of all the poll events
in the array to figure out which ones were fulfilled and what actions to take.
Currently, there is only one mode of operation available: the object is not
acquired. As an example, this means that when :c:func:`k_poll` returns and
the poll event states that the semaphore is available, the caller of
:c:func:`k_poll()` must then invoke :c:func:`k_sem_take` to take
ownership of the semaphore. If the semaphore is contested, there is no
guarantee that it will be still available when :c:func:`k_sem_take` is
called.
Implementation
**************
Using k_poll()
==============
The main API is :c:func:`k_poll`, which operates on an array of poll events
of type :c:struct:`k_poll_event`. Each entry in the array represents one
event a call to :c:func:`k_poll` will wait for its condition to be
fulfilled.
Poll events can be initialized using either the runtime initializers
:c:macro:`K_POLL_EVENT_INITIALIZER()` or :c:func:`k_poll_event_init`, or
the static initializer :c:macro:`K_POLL_EVENT_STATIC_INITIALIZER()`. An object
that matches the **type** specified must be passed to the initializers. The
**mode** *must* be set to :c:enumerator:`K_POLL_MODE_NOTIFY_ONLY`. The state
*must* be set to :c:macro:`K_POLL_STATE_NOT_READY` (the initializers take care
of this). The user **tag** is optional and completely opaque to the API: it is
there to help a user to group similar events together. Being optional, it is
passed to the static initializer, but not the runtime ones for performance
reasons. If using runtime initializers, the user must set it separately in the
:c:struct:`k_poll_event` data structure. If an event in the array is to be
ignored, most likely temporarily, its type can be set to
:c:macro:`K_POLL_TYPE_IGNORE`.
.. code-block:: c
struct k_poll_event events[4] = {
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_sem, 0),
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_fifo, 0),
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_MSGQ_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_msgq, 0),
K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_PIPE_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_pipe, 0),
};
or at runtime
.. code-block:: c
struct k_poll_event events[4];
void some_init(void)
{
k_poll_event_init(&events[0],
K_POLL_TYPE_SEM_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_sem);
k_poll_event_init(&events[1],
K_POLL_TYPE_FIFO_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_fifo);
k_poll_event_init(&events[2],
K_POLL_TYPE_MSGQ_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_msgq);
k_poll_event_init(&events[3],
K_POLL_TYPE_PIPE_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&my_pipe);
// tags are left uninitialized if unused
}
After the events are initialized, the array can be passed to
:c:func:`k_poll`. A timeout can be specified to wait only for a specified
amount of time, or the special values :c:macro:`K_NO_WAIT` and
:c:macro:`K_FOREVER` to either not wait or wait until an event condition is
satisfied and not sooner.
A list of pollers is offered on each semaphore or FIFO and as many events
can wait in it as the app wants.
Notice that the waiters will be served in first-come-first-serve order,
not in priority order.
In case of success, :c:func:`k_poll` returns 0. If it times out, it returns
-:c:macro:`EAGAIN`.
.. code-block:: c
// assume there is no contention on this semaphore and FIFO
// -EADDRINUSE will not occur; the semaphore and/or data will be available
void do_stuff(void)
{
rc = k_poll(events, ARRAY_SIZE(events), K_MSEC(1000));
if (rc == 0) {
if (events[0].state == K_POLL_STATE_SEM_AVAILABLE) {
k_sem_take(events[0].sem, 0);
} else if (events[1].state == K_POLL_STATE_FIFO_DATA_AVAILABLE) {
data = k_fifo_get(events[1].fifo, 0);
// handle data
} else if (events[2].state == K_POLL_STATE_MSGQ_DATA_AVAILABLE) {
ret = k_msgq_get(events[2].msgq, buf, K_NO_WAIT);
// handle data
} else if (events[3].state == K_POLL_STATE_PIPE_DATA_AVAILABLE) {
ret = k_pipe_get(events[3].pipe, buf, bytes_to_read, &bytes_read, min_xfer, K_NO_WAIT);
// handle data
}
} else {
// handle timeout
}
}
When :c:func:`k_poll` is called in a loop, the events state must be reset
to :c:macro:`K_POLL_STATE_NOT_READY` by the user.
.. code-block:: c
void do_stuff(void)
{
for(;;) {
rc = k_poll(events, ARRAY_SIZE(events), K_FOREVER);
if (events[0].state == K_POLL_STATE_SEM_AVAILABLE) {
k_sem_take(events[0].sem, 0);
}
if (events[1].state == K_POLL_STATE_FIFO_DATA_AVAILABLE) {
data = k_fifo_get(events[1].fifo, 0);
// handle data
}
if (events[2].state == K_POLL_STATE_MSGQ_DATA_AVAILABLE) {
ret = k_msgq_get(events[2].msgq, buf, K_NO_WAIT);
// handle data
}
if (events[3].state == K_POLL_STATE_PIPE_DATA_AVAILABLE) {
ret = k_pipe_get(events[3].pipe, buf, bytes_to_read, &bytes_read, min_xfer, K_NO_WAIT);
// handle data
}
events[0].state = K_POLL_STATE_NOT_READY;
events[1].state = K_POLL_STATE_NOT_READY;
events[2].state = K_POLL_STATE_NOT_READY;
events[3].state = K_POLL_STATE_NOT_READY;
}
}
Using k_poll_signal_raise()
===========================
One of the types of events is :c:macro:`K_POLL_TYPE_SIGNAL`: this is a "direct"
signal to a poll event. This can be seen as a lightweight binary semaphore only
one thread can wait for.
A poll signal is a separate object of type :c:struct:`k_poll_signal` that
must be attached to a k_poll_event, similar to a semaphore or FIFO. It must
first be initialized either via :c:macro:`K_POLL_SIGNAL_INITIALIZER()` or
:c:func:`k_poll_signal_init`.
.. code-block:: c
struct k_poll_signal signal;
void do_stuff(void)
{
k_poll_signal_init(&signal);
}
It is signaled via the :c:func:`k_poll_signal_raise` function. This function
takes a user **result** parameter that is opaque to the API and can be used to
pass extra information to the thread waiting on the event.
.. code-block:: c
struct k_poll_signal signal;
// thread A
void do_stuff(void)
{
k_poll_signal_init(&signal);
struct k_poll_event events[1] = {
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
K_POLL_MODE_NOTIFY_ONLY,
&signal),
};
k_poll(events, 1, K_FOREVER);
int signaled, result;
k_poll_signal_check(&signal, &signaled, &result);
if (signaled && (result == 0x1337)) {
// A-OK!
} else {
// weird error
}
}
// thread B
void signal_do_stuff(void)
{
k_poll_signal_raise(&signal, 0x1337);
}
If the signal is to be polled in a loop, *both* its event state must be
reset to :c:macro:`K_POLL_STATE_NOT_READY` *and* its ``result`` must be
reset using :c:func:`k_poll_signal_reset()` on each iteration if it has
been signaled.
.. code-block:: c
struct k_poll_signal signal;
void do_stuff(void)
{
k_poll_signal_init(&signal);
struct k_poll_event events[1] = {
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
K_POLL_MODE_NOTIFY_ONLY,
&signal),
};
for (;;) {
k_poll(events, 1, K_FOREVER);
int signaled, result;
k_poll_signal_check(&signal, &signaled, &result);
if (signaled && (result == 0x1337)) {
// A-OK!
} else {
// weird error
}
k_poll_signal_reset(signal);
events[0].state = K_POLL_STATE_NOT_READY;
}
}
Note that poll signals are not internally synchronized. A :c:func:`k_poll` call
that is passed a signal will return after any code in the system calls
:c:func:`k_poll_signal_raise()`. But if the signal is being
externally managed and reset via :c:func:`k_poll_signal_init()`, it is
possible that by the time the application checks, the event state may
no longer be equal to :c:macro:`K_POLL_STATE_SIGNALED`, and a (naive)
application will miss events. Best practice is always to reset the
signal only from within the thread invoking the :c:func:`k_poll` loop, or else
to use some other event type which tracks event counts: semaphores and
FIFOs are more error-proof in this sense because they can't "miss"
events, architecturally.
Suggested Uses
**************
Use :c:func:`k_poll` to consolidate multiple threads that would be pending
on one object each, saving possibly large amounts of stack space.
Use a poll signal as a lightweight binary semaphore if only one thread pends on
it.
.. note::
Because objects are only signaled if no other thread is waiting for them to
become available and only one thread can poll on a specific object, polling
is best used when objects are not subject of contention between multiple
threads, basically when a single thread operates as a main "server" or
"dispatcher" for multiple objects and is the only one trying to acquire
these objects.
Configuration Options
*********************
Related configuration options:
* :kconfig:option:`CONFIG_POLL`
API Reference
*************
.. doxygengroup:: poll_apis