I have a question about RTOS scheduling behavior. My understanding is that in a preemptive RTOS, when a higher-priority task becomes ready to run, it should immediately preempt any lower-priority task that is currently executing.
Consider a scenario where a low-priority task and a high-priority task share a resource protected by a mutex. The low-priority task acquires the mutex first and starts using the shared resource. Later, the high-priority task becomes ready and attempts to acquire the same mutex.
Since the mutex is already owned by the low-priority task, the high-priority task cannot proceed and is placed in the Blocked state while waiting for the resource to become available.
As a result, the low-priority task continues executing until it releases the mutex, effectively blocking the high-priority task.
Doesn’t this seem somewhat contrary to the principle of preemptive scheduling, where the highest-priority ready task is expected to run first?
Nope. Because that is the only that that CAN be done. For the Higher Priority task to some how “steal” the mutex means you don’t have the protection of the mutex and it will effectively allow the operation being done by the low priority task that needed to be “atomic” to be seen only partially done.
The point is that your high priority task ISN’T “ready to run” but gets blocked when it tries to take the Mutex that is currently held by the Low Priority Task.
As soon as the lower priority task releases the Mutex, the Higher priority task will be unblocked and will THEN be ready to run and it can take the Mutex.
Let’s say L is running and using the resource. H becomes ready, tries to access the resource, and gets blocked because L is already using it. Then M preempts L and starts running.
In the task state diagram, I see two possible transitions from the Running state: Running → Ready and Running → Blocked.
Yes, a preempted task will move from Running to Ready, so that it will still be considered eligible to be run (since it is ready to run). The arrows between Ready and Running don’t have functions on them as these transitions are controlled by the actions of the scheduler, not the tasks themselves.
I will point out that if the priority of M is between that of L and H, that you are starting to describe the “Priority Inversion” problem. which is solved by using a Mutex to protect resources, which use a method called Priority Inversion, so M becoming Ready will not cause it to preempt L, as when H blocked on the Mutex, task L was given a temporary priority of H, which means it is higher in priority than M, and thus won’t get preempted by it.
FreeRTOS implements Priority Inheritance to address Priority Inversion problem that you described. You can read more about it in sections 8.3.2 and 8.3.3 in the FreeRTOS book.