-
Notifications
You must be signed in to change notification settings - Fork 197
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
strange blocking when subscribing to async sysloop #191
Comments
The problem with your code boils down to the fact, that the async-fied Rust version of the ESP IDF event loops implements a naive Broadcast channel; as in multple-producers(=the folks who The problem with naively-implemented Broadcast queues is that unless ALL consumers are actively consuming messages, at some point the queue will overflow and block, as there will be outstanding messages in the queue that are not "seen"/"consumed" by a certain consumer. In your case, the offending consumer is There is no magical solution; broadcast queues just suffer from this problem. The "non-naive" versions just start skipping messages for some consumers if they lag too far behind, but this is too complex to address in the simple ESP IDF async wrapper we have here.
No. We don't use this call, as it is kind of deprecated anyway. |
If you have digested the above... there is maybe one more problem which exacerbates the situation. If these were |
Now that I think of it, we can implement an opt-in for skipping subscribers (subscribers that start skipping events if not pumped up). This would turn the event loop into some sort of a Signal for those subscribers that are skipping (you need to check the Signal async synchronisation primitive, possibly from Yet, that won't fix your code specifically, as you are awaiting a specific event in
... that would work fine with skipping subscribers. And by the way, you can easily implement this pattern by combining an Oh well. Who says that async is easy? |
Thanks for your great answer. Now I understand the system behind and can deal with these problems better.
I checked my problem with receiving from
You said it's a no go waiting on a specific event. But I receive all events and discard these which not matches to my event because I receive them in a while loop and this is looping as long I get the correct event. But I didn't get why it blocks at subscribing. Or do you mean there is also a queue on witch subscribers are pushed and this is limited to n subscribers per event?
That's true. At some time you think you understood everything and everything works, but then you get to the point you understand nothing. |
(UPDATE: Edited the code for extra clarity.)
Mind sharing how exactly you fixed that?
My point is - again - in the presence of skipping subscribers (which you don't have in your code), you can't rely on waiting for a specific event, because - due to timing issues - that event might get skipped and you might never see it!
It blocks on subscribing because the subscribing code wants to get the queue lock. However - and in the presence of outstanding events not consumed yet by some consumer - this lock is held by the system which waits on it until the consumer consumes the event. However, since you do the subscription and the consumption of your events from the same thread (= you are trying to subscribe instead of consuming events), the queue remains locked.
Persistence usually helps. :) If you share your code, I might be able to help. But then again and in general - the safest is to just use skipping subscribers. The easiest way to simulate these is with a On the other hand, I can't stress how useful let my_notif = Arc::new(Notification::new());
let sub_2 = {
let my_notif = my_notif.clone();
event_bus.subscribe(move |event|) {
if matches!(event, WifiEvent::ScanDone) {
my_notif.notify();
}
})?
};
// Note that with the logic below you have to subscribe _before_ `scan_start` is called
// Note also that `my_notif.notify()` is a) non-blocking and completes immediately and b) as I described - it will "ovewrite" the previous value; but in your use case that does not matter as you are not expecting multiple "scan done" events anyway
wifi_driver.scan_start();
// Wait for scan to be done
my_notif.wait().await; |
Thanks again, for clarification.
I recv() the events in a extra task to empty the queue. I will try it with signal/notification as it is approved by you. There are so many rust sync primitives out there, it needs some time to learn them all and which are best to use in which context. Often I prefer to implement raw futures but they are not 100% tested for every case. |
Coming back to the |
Summarizing what @ivmarkov suggested, the problem is that This behavior is similar to Taking parallel from this I think we can employ a similar tactic when we need to handle multiple futures call: fuse it. This however implies heap memory buffers are definitely needed. This will make the firmware less deterministic. |
IDF-VERSION: v4.4
esp-idf-svc: master
I have the following calling sequence:
Now the call
subscribe()
forsub_2
blocks forever. I tracked the blocking behavior down toesp_event_handler_instance_register()
inEspEventLoop::subscribe_raw()
.Fixes:
sub_1
before reconfiguring the wifi driver (never createsub_1
or dropping it before config wifi as mixed) will fix it.set_configuration(Mixed...)
function call, the second subscription works, but of course, thestart_scan()
fails because wrong configured. (not really a fix)The main problems are the
sub_1
subscription and the calling sequence ofsub_2
andset_configuration(Mixed...)
. But I don't understand why. The second fix is also a little bit strange.Does this problem refere to
from esp-idf.
The text was updated successfully, but these errors were encountered: