| .. _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 |