Based on Zephyr version 0.6
Polling API - (polling API)
The polling API is used to wait concurrently for any one of multiple conditions to be met.
1. Concepts - (Concepts)
The main functions of the polling API are k_poll() , it is conceptually very similar to the POSIX poll() function, except that it operates on kernel objects rather than file descriptors.
The polling API allows a single thread to wait concurrently for one or more conditions to be met without having to actively view each condition separately.
There is a limited set of conditions:
- Semaphore available
- The kernel FIFO contains the data to be retrieved
- A polling signal is sent
Threads that want to wait for 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.
Each event must specify which type of condition must be met in order to change its status to indicate that the requested condition has been met.
Each event must specify the kernel object that it wants to satisfy.
Each event must specify which mode of operation to use when conditions are met.
Each event can optionally specify a tag to group multiple events together, which is determined by the user.
In addition to the kernel object, there is also a pseudo object type of poll signal, which can send signals directly.
k_poll() The function returns once one of the conditions it waits for is met. If called k_poll() Completed before, or due to the priority multithreading feature of the kernel, in k_poll() There may be multiple completions on return. The caller must look at the status of all polling events in the array to determine which events were completed and what actions were taken.
Currently, only one mode of operation is available: do not get objects. For example, this means that when k_poll() Returns and the poll event indicates that the semaphore is available, k_poll() The caller of must call k_sem_take() To take ownership of the semaphore. If the semaphore is contested, there is no guarantee that it will k_sem_give() Still available when called.
2. Implementation - (Implementation)
2.1 Using k_poll()
The main API s are k_poll() , which is of type k_poll_event Operate on the polling event array of. Each entry in the array represents an event and calls k_poll() Will wait for its conditions to be met.
They can use runtime initializers K_POLL_EVENT_INITIALIZER() or k_poll_event_init() Or static initializer K_POLL_EVENT_STATIC_INITIALIZER() Initialize. An object matching the specified type must be passed to the initializer. The mode must be set to K_POLL_MODE_NOTIFY_ONLY . Status must be set to K_POLL_STATE_NOT_READY (the initializer is responsible).
User tags are optional and completely opaque to the API: they exist to help users group similar events together. Since it is optional, it is passed to the static initializer instead of the runtime initializer for performance reasons. If using the runtime initializer, the user must k_poll_event Set it separately in the data structure. If you want to ignore events in the array (most likely temporary), you can set its type to K_POLL_TYPE_IGNORE.
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
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 initializing the event, you can pass the array to k_poll() . You can specify a timeout to wait only for a specified amount of time, or set the special value K_NO_WAIT and K_FOREVER specifies not to wait or wait for the event condition to be met.
A list of pollers is provided on each semaphore or FIFO and can wait for as many events as possible according to the needs of the application. Please note that waiters will be served on a first come, first served basis, not on a priority basis.
If successful, then k_poll() Return 0. If timeout, return- EAGAIN.
// 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 k_poll() When invoked in a loop, the state of the event must be reset by the user. K_POLL_STATE_NOT_READY.
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; } }
2.2 Using k_poll_signal_raise()
One of the event types is K_POLL_TYPE_SIGNAL : This is the "direct" signal for polling events. This can be seen as a lightweight binary semaphore that only one thread can wait for.
The polling signal is k_poll_signal A separate object of type that must be attached to k_poll_event, similar to semaphore or FIFO. It must pass first K_POLL_SIGNAL_INITIALIZER() or k_poll_signal_init() Initialize.
struct k_poll_signal signal; void do_stuff(void) { k_poll_signal_init(&signal); }
It is through k_poll_signal_raise() Function to notify. This function accepts a user result parameter that is opaque to the API and can be used to pass additional information to threads waiting for events.
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_raise(&signal, 0x1337); }
If the signal is to be polled in the loop, if the signal has been polled, its event state and its signaled field must be reset in each iteration.
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; } }
3. Suggested Uses - (suggested uses)
Use k_poll() saves a lot of stack space by merging multiple threads that will be suspended on an object.
Because objects only signal when there are no other threads waiting for them to be available, and only one thread can poll a specific object, polling is best when the object is not contested by multiple threads. Basically, when a single thread runs as the main "server" or "dispatcher" of multiple objects, And is the only thread trying to get these objects.
4. Configuration Options - (configuration options)
Related configuration options:
Reference link
https://docs.zephyrproject.org/latest/reference/kernel/other/polling.html#