| .. _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 :cpp:func:`k_poll()`, which is very similar |
| in concept to the POSIX :cpp: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 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 :cpp: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 :cpp:func:`k_poll()` returns, if they were fulfilled before |
| :cpp: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 figured 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 :cpp:func:`k_poll()` returns and |
| the poll event states that the semaphore is available, the caller of |
| :cpp:func:`k_poll()` must then invoke :cpp: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 :cpp:func:`k_sem_give()` is |
| called. |
| |
| Implementation |
| ************** |
| |
| Using k_poll() |
| ============== |
| |
| The main API is :cpp:func:`k_poll()`, which operates on an array of poll events |
| of type :c:type:`struct k_poll_event`. Each entry in the array represents one |
| event a call to :cpp:func:`k_poll()` will wait for its condition to be |
| fulfilled. |
| |
| They can be initialized using either the runtime initializers |
| :c:macro:`K_POLL_EVENT_INITIALIZER()` or :cpp: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:macro:`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:type:`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 K_POLL_TYPE_IGNORE. |
| |
| .. code-block:: c |
| |
| struct k_poll_event events[2] = { |
| 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), |
| }; |
| |
| or at runtime |
| |
| .. code-block:: c |
| |
| struct k_poll_event events[2]; |
| 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); |
| |
| // tags are left uninitialized if unused |
| } |
| |
| |
| After the events are initialized, the array can be passed to |
| :cpp: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. |
| |
| Only one thread can poll on a semaphore or a FIFO at a time. If a second thread |
| tries to poll on the same semaphore or FIFO, :cpp:func:`k_poll()` immediately |
| returns with the return value :c:macro:`-EADDRINUSE`. In that case, if other |
| conditions passed to :cpp:func:`k_poll` were met, their state will be set in |
| the corresponding poll event. |
| |
| In case of success, :cpp: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, 2, 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 { |
| // handle timeout |
| } |
| } |
| |
| When :cpp: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, 2, K_FOREVER); |
| 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 |
| } |
| events[0].state = K_POLL_STATE_NOT_READY; |
| events[1].state = K_POLL_STATE_NOT_READY; |
| } |
| } |
| |
| Using k_poll_signal() |
| ===================== |
| |
| 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:type:`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 |
| :cpp: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 :cpp:func:`k_poll_signal()` 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); |
| |
| if (events.signal->result == 0x1337) { |
| // A-OK! |
| } else { |
| // weird error |
| } |
| } |
| |
| // thread B |
| void signal_do_stuff(void) |
| { |
| k_poll_signal(&signal, 0x1337); |
| } |
| |
| If the signal is to be polled in a loop, *both* its event state and its |
| **signaled** field *must* be 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); |
| |
| if (events[0].signal->result == 0x1337) { |
| // A-OK! |
| } else { |
| // weird error |
| } |
| |
| events[0].signal->signaled = 0; |
| events[0].state = K_POLL_STATE_NOT_READY; |
| } |
| } |
| |
| Suggested Uses |
| ************** |
| |
| Use :cpp: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: |
| |
| * :option:`CONFIG_POLL` |
| |
| APIs |
| **** |
| |
| The following polling APIs are provided by :file:`kernel.h`: |
| |
| * :c:macro:`K_POLL_EVENT_INITIALIZER` |
| * :c:macro:`K_POLL_EVENT_STATIC_INITIALIZER` |
| * :cpp:func:`k_poll_event_init()` |
| * :cpp:func:`k_poll()` |
| * :cpp:func:`k_poll_signal_init()` |
| * :cpp:func:`k_poll_signal()` |