-
Notifications
You must be signed in to change notification settings - Fork 2.1k
core: add wait queue wrapper around thread flags group #21265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
320e88f
to
ef2b82a
Compare
5c96ba0
to
42f01fa
Compare
Now that #21254 is merged, you can rebase this. |
* } | ||
* ``` | ||
* | ||
* If you don't want to wait indefinitely, you can do it with a timeout: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should mention here that the ztimer
has to be acquired to avoid that it's free'd somewhere else and the wait is indefinite again.
Your code currently does not acquire the ztimer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the docs from ztimer_now()
:
* A clock is also guaranteed to be active from the time any timer is set
* (the first opportunity to get a "now" value from it is the return value of
* @ref ztimer_set) until the time the timer's callback returns.
ztimer_set_timeout_flag()
will call ztimer_set()
under the hood, which will acuire the timer, if required. In fact, all ztimer
API which registers something is acquiring the timer, as that is trivial to do. The timer only has to be acquired if the application needs it to be running, but the clock is not aware of that (as in the case of ztimer_now()
).
850eb1c
to
4c07d0e
Compare
Contribution description
Note: This PR builds on top of #21254
From
core/include/wait_queue.h
:This API is a thin wrapper around thread flags group and requires
"core_thread_flags_group" to be enabled in USEMODULE.
Wait queues enable lock-free, IRQ-safe condition signaling. This API is
inspired from the Linux Kernel.
Wait queues have similar semantics to condition variables, but don't require
setting the condition + signaling to be atomic, hence no mutex is needed to
function properly. In turn, one may safely call
queue_wake()
from an ISR.Note, while
cond_signal()
andcond_broadcast()
are safe to call from an ISRcontext too, doing so will probably cause a race condition elsewhere.
Consider the following scenario using condition variables:
Note, the mutex is there only for the
cond_wait()
API call, as we're notallowed to call
mutex_lock()
inside the ISR. This alone is a hint thatsomething isn't right and indeed, the following sequence of events is
possible:
THRESHOLD
, and is about to call cond_wait()THRESHOLD
and signals the conditioncond_wait()
and goes to sleep, possibly foreverUsing a wait queue, we can do this:
This is free of the race condition above because if the ISR fires between
the condition check and the
queue_wait()
call, queue_wait() will returnbecause the
THREAD_FLAG_WAIT_QUEUE
thread flag used in theimplementation must also have been set. In other words, if the condition is
true, then the thread flag is also set.
If you have a simple condition check expression, the waiter boiler plate
code can be eliminated by using the
QUEUE_WAIT()
macro, which combinesthe join, wait, and leave operations:
If you don't want to wait indefinitely, you can do it with a timeout:
Limitations
Be aware that the condition checking is fenced but not atomic w.r. to
signaling, so you have to ensure that by other means. E.g. in the example
code above this is enforced by
atomic_load_u64()
.When to use?
If you know for sure you're synchronizing between threads only (no ISR),
then the condition variable has the advantage of implicit condition
setting/checking atomicity through the mutex. Otherwise go for the wait
queue, as it's more flexible by allowing the waker to be in ISR context.
Testing procedure
Run the test application on:
Issues/PRs references