The problem:
I think the right case is that variable âcount_bâ will increase 1 every second, but I get the result is that the âcount_bâ is zero all the time.
Variable âcount_aâ and âcount_câ is increase 100 every second.
I debuged the FreeRTOS kernel code, I found in âxQueueGenericSendâ : task A add uxMessagesWaiting to 1 and move task B to readylist.
But before task B runing, the task A get the Semaphore again by call osSemaphoreWait, when task B runing ,the Semaphore will move task B to pendinglist.
then loop like upâŠ
Is this is correct or incorrect? Or my code has problem?
Still expected behavior.
A giving the semaphore will wake B, but until A blocks or a tick arrives that causes a time slicing rescheduling, B wonât run. Unless there is some higher priority task taking time, since A just did a delay, we know we are near the beginning of a tick when A does the release, so it will always get back to the take before B is given a chance to run.
If you want to change that, you could have A to a taskYield() after releasing the semaphore, then if B is ready to run, it will get its chance.
BTW, your code cries for a mutex instead of a binary semaphore. What you want realize here is mutual exclusion for which a mutex is a pre-built and superior primitive.
Also, I assume that the osDelay() in task A is just experimental code, right? Normally there is no useful application for a mutex to be held while the holding task delays.
Yes, without adding a yield after the release, a release followed quickly by a retake will likely starve a second same priority task. To avoid starving a lower priority task you need to block on something after the release.
I think itâs not properly for userâ app to judge if need to add yield,this will cause yield follow every release when the usercodeâs run time followed release is not steady.
I just have one suggestion about the operation in release:the release signal has passed to task b by wake up it to readylist,the add 1 operation should not be work again.
Task bâ wait is early to the task aâs second wait,but canât get signal.
Itâs not fair to task b,because a and b have the same priority.
Just one suggestion,I will add same yield follow the release in same place to avoid this problem.
The fundamental problem is the behavior of task A. It releases the immediately reclaims a resource. THAT behavior only allows HIGHER priority tasks to share the resource. Adding the yield extends that to also sharing with equal priority tasks. Unless task A blocks, it will NEVER share with a lower priority tasks.
In my mind, this is very much something that the designer of task A needs to think about.
If I recall correctly this behaviour is described in the book. It is definitely something I used to talk about in training. The code in the first post will only allow a context switch if a tick interrupt happens to occur between releasing the and re-taking the semaphore - which in the code above is a tiny time window that opens very infrequently. The suggested addition of a yield in the loop will ensure a task of equal priority always has the opportunity to obtain the mutex whether a tick interrupt occurs or not.
the most obvious and organic solution would be to raise Bâs priority. Another question to ask, again, is why A delays while holding the mutex. If the sequence b++/c++ must be âuniterruptableâ even with task A doing nothing in between ( a delay effectively is doing nothing), there is no solution to this problem, at least as long as there is a good reason to leave both tasks at the same priorityâŠ
These are architectural considerations, though. If the TO has decided to structure his design this way, he can probably justify it.
I think the example is designed to be a âtoyâ demonstration of a bigger issue, with simple to see behavior. Likely in ârealâ life, Task A would be some operation that needs exclusive access to some data, and during its working on that data, it does things (like some I/O) that might need to block for a while.
The âProblemâ is that Task A seems to want to always be trying to work on this data, but occasionally want to let others get in, so it releases its lock on the data.
The problem is that since it doesnât block or yield, only tasks with a higher priority will likely get in to have access to that data.
If he adds a yield, then tasks of the same priority will also be able to get in.
Only by adding some form of blocking behavior OUTSIDE the lock, could a task of lower priority get in. And in this case, making the semaphore a mutex could be very useful, so when task A come back, a lower priority task that got access would be hurried along if we hit.a priority inversion case.
The code in this topic is only the example extract from my app program.
The actually case is:
âsemabâ is a lock for my serial device.
Task a is for receiving data, task b is for sending data.Recv and Send have the independent busyness.âosDelayâ in the example is representative of the received data processing code(sometime excuting very longtime).
In my busyness, task a and b should be have the same priority, because the Recv and Send have the equal treatment.If improve task bâs priority, it will impact other task.
In my opinion,sem and mutex is only the tools provided by OS, we canât care what the user code doing, just give a sensible rule.
The sem tool provided by FreeRTOS has a unreasonable rule.You may say this is a design for it or FreeRTOSâs feature,but I have worked with several other osâs sem tool,they all worded under âfirst come first serviceâ rule(with same priority).This is a commonsense understanding.
I just want use a âfirst come first serviceâ tool from FreeRTOS, but now I canât get it(tried sem and mutex).I accept the suggestion add yield follow release, but this is baffling.
I donât think splitting sends and receives to/from the same serial devices into two tasks is a good design because normally you end up âback door serializingâ your tasks to implement an inherently serialized protocol.
The problems you are seeing may just be corollaries of that architectural problem.
FreeRTOS uses a very simple model for its semaphore. If you try to take a semaphore and it is free, you get it, if not, you are blocked until it becomes free then you get to try again.
This is actually fairly common behavior.
The concept you have makes things much more complicated. First the semaphore needs to keep track of who all has submitted a request for it even while it was held, and not let someone new acquire the semaphore if there is a pending request from someone with the same priority as you that just hasnât had a chance to run.
That or the releasing of a resource that is being waited on by another task of the same priority needs to initiate a context switch, which makes the system have a higher load due to extra task switches and worsens the evenness of the sharing of the CPU between tasks of equal priority.
I will note, that if you really want a semaphore that works the way you describe, it is quite possible to build one based on the FreeRTOS semaphore and a bit of wrapping code. If the semaphore started with all that extra complexity, then EVERYONE would need to pay for it.
I want to say:This isât actually fairly common behavior.
First,my code can be running very well on windows/linux/ucos plateform, and the result is all the same.I think these is fairly common behavior.
Second,my app code want get signal from sem by call OSâs api âwaitâ(wait forever in my case),but it canât get it even someone has released the sem many a time.
How can I get the signal? If âtask aâ was wrote by another programmer, âtask bâ was wrote by me,but the serial device has only one.I onlyconsult with him to change his code, add yield follow release.But another programmer will say: âmy work is very busy and I have realsed it for a while when I am free,you canât get it?â
The âa whileâ is add 10000 times on stm32H7, then task b can get the signal very very small probability.Itâs hard for user to judge how manay code below relase need to add yield or not.
This is not my personal demand,I just think FreeRTOS as a very well known operating system in the world should be more perfect!!!
If the author very very sure this is FreeRTOSâs feature or this design is perfect already,my opinion can be omitted.
Canât provide a full reply on my cell phone keyboard, but the scheduling rules are actually very simple. I think, but am not sure, you are requesting the rules to apply in all cases except where it is convenient for SOME applications.
If two tasks of equal priority block attempting to obtain the same semaphore then the task that attempts to obtain the semaphore first is the task that obtains the semaphore provided it is still blocked waiting for the semaphore when the semaphore becomes available. Note though if there are only two tasks there will never be two blocked at the same time. Also note that giving back a semaphore will unblock the other task but not context switch to it because to do so would break the scheduling rules - the unblocked task has the same priority, not a higher priority, than the running task. That means the running task can obtain the semaphore again as nothing is blocked on the semaphore, so if a tick causes a context switch the other task will attempt to obtain the semaphore again but again find it canât, and re-enters the blocked state for whatever remains of its original block time.
As before, pretty sure this is explained in the book, and is why manually adding a yield after giving the semaphore back enables the other task to run while the semaphore is actually available.
No need active the context switch to set the blocked task to running.The release task only need pass the sem to the first task in the semâs block queue,just set it into readylist.
Then the release task can continue running its code below release until itâs tick disappeared.
But if the code below release has another wait for the same sem,it must enter block,because the sem has given to the first block ed task in the semâs queue.
The blocked queue which have got the sem will run until it got the tick.
I think you underestimate the difficulty of that. Remember, one of FreeRTOSâs goals is to be a lean code base, that is fast and small but still powerful.
Remember also that in FreeRTOS, to achieve this, Semaphores are just a special case of a Queue, so do you also send the data to someone waiting to read from a Queue also?
Also, if you give B the semaphore now but donât run it, what happens if a higher priority task C tries to take it before B gets to it, does B lose it, or does C get pre-empted by the lower priority task that hasnât run yet.
This adds a lot of special cases to the currently simple code.
Other systems might chose to do this, but they likely wonât be as small and quick as FreeRTOS.