Also, it needs to be repeated (we had this many many times) that the UART example you sketched out exposes a few design flaws that are nevertheless educational:
- In your code, several tasks attempt to access an inherently serialized device concurrently, ie unpredictably. This in itself is rather error prone and sort of undermines the idea of a serial device. Some argue in favor of mutex controlled access (but see below), but this is first of all highly protocol dependent and secondly still subject to subtle timeout and race conditions. The preferrable approach is to have a single task serve the UART and feed that task with asynchronous requests from client tasks (for example via a message pump).
- In your design, the UART Rx side is being polled. There is a wide consensus that this should be avoided because the polling wastes CPU cycles. The preferred way is to let an ISR queue the incoming characters and let a medium to high priority task buffer them for the client tasks to pick them up as needed without the risk of buffer overruns. Of course there are protocol dependencies and subleties here as well, but the isr driven design is widely agreed upon to be superior to polling.
You man want to query the forum for UART, there have literally been dozens to hundreds of in depth discussions about the pros and cons of different system designs concerning interacting with serial devices.