Binary Semaphore problem

My code is like this(FreeRTOS v10.0 , cmsis_V2):

osSemaphoreId semAB;
int count_a = 0;
int count_b = 0;
int count_c = 0;

void A(void const *  para)
{
    while(1)
    {
        osSemaphoreWait(semAB, osWaitForever);
        count_a++;
        osDelay(10);
        count_c++;
        osSemaphoreRelease(semAB);
    }
}

void B(void const *  para)
{
    while(1)
    {
        osSemaphoreWait(semAB, osWaitForever);
        count_b++;
        osSemaphoreRelease(semAB);
        osDelay(1000);
    }
}

osSemaphoreDef(socketlock);

//acturaly is "vSemaphoreCreateBinary"
semAB = osSemaphoreCreate(osSemaphore(socketlock), 1);

//创建数据采集线程
osThreadDef(CollectSensor, A, osPriorityNormal, 0, 512);
osThreadCreate(osThread(CollectSensor), NULL);
B(NULL);

Note:Task a and task b have the same priority.

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?

Thanks everyone!!!

If the task that runs A has a hogher task priority than the task that runs B, this is expected behavior. A effectively starves out B.

Task a and task b have the same priority.

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, osDelay is my longtime code.
Mute’s implement is also the queue,should be have the same problem.

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.

There is no point in that, though. But I guess you know what you are doing.

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.

Thanks everyone again!

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.

we had this discussion before:

The feasibility of having timer instantiated for EACH request to be sent [posix] - Libraries - FreeRTOS Community Forums

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.

This topic has done its job,let’s closed it!

Good luck!

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.