embassy
embassy copied to clipboard
Access HAL from multiple tasks?
Hi,
am I getting it right that there is no way to access a specific HAL driver from multiple tasks? Lets say I want to read ADC channel 1 and modulate LED accordingly in the 1st task, in the 2nd task I want to read ADC channel 2 and send it periodically over UART, in the 3rd task I want to parse RX messages from the same UART and perform some actions accordingly. In regular RTOS approach I would just need to synchronize access to the shared resources, but they are available in all threads. How this could be done with embassy? If I am getting it right the HAL drivers uses AtomicWaker that can store only single task to be waken. So there is no way to queue multiple tasks waiting on the same resource.
THX for clarification
To use peripherals from tasks, the easiest way is to pass them as parameters: example, example.
If you want to share the same peripheral between multiple tasks, put it in a static THING: Forever<Mutex<Thing<...>>> = .. in main, then pass a &'static Mutex<Thing<..>> to the tasks. This allows individual tasks to lock it and use it.
Some peripheral drivers have explicit support for "splitting" it into several parts, such as UART, so you can send the "write half" and the "read half" of the same uart to different tasks without having to use mutexes. I don't think this is the case for ADC currently though.
About the Waker internals: Methods that wait on a peripheral usually require &mut self. This enforces only one task can wait on it at a time. If the peripheral is shared with a Mutex and one task finishes and another one starts waiting on it, the waker is updated to point to the new waiting task, so in the end it all works out.
But since executor is running in single thread blocking on mutex will prevent execution of other tasks. Lets say 1st task will take mutex and start peripheral operation, then the second task will block executor thread on the same mutex until the 1st operation is finished preventing 3rd task that is free to run from being executed.
As long as the 3rd task is not using the mutex, it can run. Lets say task 1 is holding the mutex, awaiting peripheral operation to complete, task 2 is waiting to hold mutex, and task 3 is doing something completely different.
- Executor polls task 1, it is returning Pending as it awaits the peripheral operation to complete
- Executor polls task 2, it is returning Pending as it awaits the mutex.lock() operation to complete
- Executor polls task 3, which runs whatever code it can until it is blocked.
As long as the peripherals themselves support async operation, it works just fine. If you have some peripheral that is not async, and running blocking code, you can also create multiple executors running at different priority levels (see this example)
See the book for a description of the executor.
Hope this helps!