Added documentation for inter-thread communcation

This commit is contained in:
dario 2025-01-18 22:45:37 +01:00
parent 119dd24dab
commit c307aff3d5

View File

@ -368,11 +368,91 @@ namespace sta
The resulting program switches between the states `PING` and `PONG` and alternately outputs "PING" and "PONG" via UART. While this is just a toy example, building more complicated applications is not much harder!
### Thread Priority Design
Every thread running in your TACOS project has a priority that the underlying RTOS uses for [scheduling](https://en.wikipedia.org/wiki/Scheduling_(computing)). The list of available priorities can be found [here](https://arm-software.github.io/CMSIS_6/v6.0.0/RTOS2/group__CMSIS__RTOS__ThreadMgmt.html). Contrary to operating systems for your personal computers like Linux or Windows, the scheduling rules in the context of TACOS are rather simple: The scheduler always picks the highest priority thread that is active (i.e. not blocked because it is waiting for an event) and allows it to run. If there are multiple active threads with identical priority, the scheduler uses the [round-robin principle](https://en.wikipedia.org/wiki/Round-robin_scheduling).
> [!WARNING]
> If you have a high priority thread that is always active, lower priority threads won't receive any computing time (commonly called [starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science))). You should ensure that all higher priority threads reach the stated blocked eventually.
We conclude this section with a short list of design principles that have been useful in previous projects:
* Find a base priority (usually `osPriorityNormal`) that all threads should have by default. Those are then scheduled via round-robin.
* There is no reason for your logger to log with 1KHz. Use thread blocking to limit the amount of computing time a thread consumes. For example, this could be the method `sleep(ms)` that the TacosThread class provides. It's also always a good idea to use interrupts in combination with RtosEvents (discussed later).
* What are more important tasks in your system? What tasks have strict time requirements? Find a new base priority (for example `osPriorityAboveNormal`) and assign it to all higher priority threads. Repeat this step if these threads are not equally important.
* Use global timers in combination with events (as dicussed later in this chapter) to ensure that threads are unblocked at different times.
### Using Inter-Thread Communication
> [!IMPORTANT]
> Coming soon!
This section serves to give you a brief overview of both the fundamental building blocks and useful design patterns for inter-thread communication.
#### Mutexes
A [Mutex](https://en.wikipedia.org/wiki/Mutual_exclusion) is used for managing access to critical sections in your code. A critical section could be code that performs transmissions
via a bus or a radio module or code that edits specific memory. Remember that the main benefit (and downside!) of a RTOS is that code is running [concurrently](https://en.wikipedia.org/wiki/Concurrency_(computer_science)) or even in parallel. Since there is no guarantee that a thread finishes its operations in a critical section before another thread is given computing time, it is good practise (and often mandatory) that we protect critical sections using mutexes. For example, consider the following scenario in which two threads increment a variable $ X $:
* Thread $ A $ and thread $ B $ are running concurrently on your microcontroller.
* Thread $ A $ reads the value $ 54 $ fromm the global variable $ X $.
* Thread $ B $ reads the value $ 54 $ fromm the global variable $ X $.
* Thread $ B $ writes value $ 54+1 $ into global variable $ X $.
* Thread A writes $ 54+1 $ into global variable $ X $.
At the end, the global variable contains the value $ X $ has the value $ 55 $ since the modification by thread $ B $ was overwritten. Notice, however, that the result depends on the order in which the two threads are executed. In order to keep the result independent of the order of execution, we use mutexes to grant threads exclusive writing and reading rights to $ X $:
```Cpp
// global.hpp
namespace counter
{
void increment();
}
// global.cpp
#include <sta/rtos/mutex.hpp>
namespace counter
{
uint32_t count = 0;
sta::RtosMutex * mutex = nullptr;
void increment()
{
// Create mutex once when this function is called for the first time.
if (mutex != nullptr)
mutex = new sta::RtosMutex("Counter");
mutex->aquire();
count++;
mutex->release();
}
}
```
Next, let show how the `func` of the two threads look like:
```Cpp
// thread_A.cpp
namespace tasks
{
// ...
void ThreadA::func()
{
// Do lots of cool stuff
counter::increment();
}
// ...
void ThreadA::func()
{
// Do lots of cool stuff
counter::increment();
}
// ...
}
```
Before the instruction `count++` is performed, the active thread has to aquire the mutex. If the mutex is currently held by another thread, the active thread will be blocked until the mutex is available. This means that the underlying RTOS will stop it and switches to a different thread that is ready. Once the current owner releases the mutex, the thread will enter the ready state again.
#### Global Timers
### Further information
To look into other function of TACOS please consult the READMEs in the include folder or the doxygen documentation. Also consult the sta-core and rtos2-utils READMEs for further information on the features that TACOS uses.