I have two tasks that share access to a resource via a RecursiveMutex. Both have the same priority. One task does not yield to the OS (relying on the OS context switching). When this task takes and gives the mutex (which happens 1/second), the second tasks can not get access to to it unless the first task calls vTaskDelay() or taskYIELD somewhere it its loop. Is this normal?
Yes - see section 8.3.6 of the FreeRTOS book.
Thanks, I see that there would be some delays (and perhaps some bad coding on my part) but I dont see why the second task never gets access as it is just waiting in xSemaphoreTakeRecursive() to get control.
If you have time slicing and preemption disabled, your second task will have to wait until the first one voluntarily releases the CPU (your system effectively enforces what is called “cooperative multitasking.”)
If you configured your FreeRTOS as truly concurrent (ie time slicing enabled), then tasks of equal priority are sort of a borderline case - some RTOS will require explicit yielding to transfer control, some will time slice between equal priority. FreeRTOS, as far as I remember, should do the latter.
In any case, immediate yielding to a waiter on a mutex at mutes give time will only happen if the task waiting on the mutex has a higher priority than the task that releases it.
Time slicing is enable. I expected that task two would get control on its next time slice (or even the one after that) but it never does. I change my code (for the better) so that task one has yields (or delays) so its no longer a problem for me but it seems like a bug.
can we see your code? Could it be that task 1 reclaims the mutex very quickly after having released it? Such that, for example, the chances of the timer tick expiring before task 2 has a chance to obtain the mutex before task 1 reclaims it are rather slim? I believe we had a similar case not too long ago.
When the task that is holding the Mutex gives it, since the other task is not of a HIGHER priority, there is no forced task switch, and it will continue to run, and then take it again.
There is no code setup to automatically make the block task take the Mutex when it was given, and that would likely need some fairly complicated code to actually try to implement.
Code with tasks sharing a Mutex like that should probably yield after releasing the Mutex.
I will work up some sample code but it may take a bit as I’m heading out of town…
Task 1 only grabs the Mutex 1/second for about 200ms. Task switching is at 1ms and there are 10 tasks, all at the same priority. I dont expect or need Task 2 to process “now”, just at its next normal context switching. Task 2 should have plenty of opportunity to grab the free mutex. But it never gets it. It does not matter what time I put into xSemaphoreTakeRecursive().
the question is not how long the tasks holds the mutex but what the average time is between releasing and reclaiming the mutex within the same task.
Do you have the round robin scheduler and pre-emption enabled? If not, the currently running task will just keep running until to does something to make it block or yield.
The section 8.3.6 of the book that Richard linked explains exactly this in detail. You may also consider using Tracelyzer to see what is going on in this case.
Consider this possible scenario:
- Task 1 acquires mutex
- Task 1 begins executing code that requires mutex.
- Task 2 preempts Task 1 for its time slice.
- Task 2 blocks trying to acquire mutex.
- Task 3-10 run for their time slice.
- Task 1 completes executing code that requires mutex.
- Task 1 releases mutex.
- Task 2 unblocks but is not running.
Then repeat that over and over.
Having Task 1 and Task 2 yield when they give the mutex solves this. Another consideration would be to combine the two tasks into 1 to avoid the overhead of a mutex altogether.
This is a contradiction in itself.
Again, my suspicion is that task1 re-aquires the mutex after releasing it so fast that task2 does not have a chance to claim it. Could that be possible?
Can you run your application under control of tracealyzer?
And, since you had no step after the last to let Task2 run, Task1 will just take it again when you loop to the top.
If you have a step after the end where Task1 continues executing code for long enough for a Tick interrupt to occur, AND configUSE_PREEMPTION is 1, AND configUSE_TIME_SLICING is 1, then Task2 should run. If the tick interrupt is happening while Task1 is running without holding the Mutex, and Task 2 doesn’t run, then one of those flags must not be set, and perhaps Tasks 3-10 ran because Task1 DID yield/block while it held the mutex, but not when it doesn’t.
Task 2 would go from blocked to ready when the mutex is released so I am not sure why that is a contradiction - Task 1 remains running. Task 2 will re-block on its next attempt to acquire the mutex since Task 1 will re-acquire it. I am agreeing with you thus why I had “repeat over and over”. Task 2 will never acquire the mutex in the scenario above because Task 1 re-acquires the mutex too quickly.