344 lines
12 KiB
ReStructuredText
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
|