I am in a situation where I start a data transfer and want to check the transfer is done within certain amount of time. I use semaphoreTake() in the task and semaphoreGiveFromISR() in the transfer completion ISR. However, I do not want to be blocked forever if there is anything wrong or longer-than-usual delay on the hardware, so I give semaphoreTake() some timeout. Here comes the issue, nobody knows how long it will take for the hardware/external device/bus to complete the transfer, the ISR may be fired after my semaphoreTake() returns from timeout, in which case I have a pending semaphore and will be unblocked immediately when I am initiating another transfer, which is undesired.
In short, I want to give a semaphore from ISR only if some task is blocked by the same semaphore. Is it possible with current FreeRTOS?
I also notice there is an event list xTasksWaitingToReceive in the Queue_t structure. Can we extend the queue API so it can support the above behavior?
I don't think there is an API for determining if there is a task blocked on the semaphore or not, but as you note there is a list that holds such tasks so you could create an API.
There are APIs that return the semaphore's count, so you might be able to derive the information from that. http://www.freertos.org/uxSemaphoreGetCount.html is version that can't be used from an ISR, but you can also pass a semaphore into http://www.freertos.org/a00018.html#ucQueueMessagesWaitingFromISR for use in an ISR.
My alternate method of solving the problem you don't mention, but may be the real issue is to do a semaphoreTake with a 0 timeout in the task before initiating the operation in case something happened like you discribed and the ISR left the semaphore given. Thus the task will wait when after it starts the operation it reaches the take.
I have actually thought about it but it is not a concrete solution since the ISR may be fired after semaphoreTake() with timeout 0. The wait time for the operation is just undeterministic.
I may just make a new API for now, but would love to have official release supporting it if possible so that I do not need to maintain a private feature branch.
Using semaphore count would not solve this problem as it does not reflect the blocking task list in the semaphore.
If the Interrupt really can occur at any time, then you have a fundamental race in your system. What happens if the interrupt comes BEFORE you get to the take(), with your idea it will be loss. I was presuming that there was some point in the setting up of an operation where you know that the hardware isn't busy with a previous operation, so you are safe from that, giving you a point you can clear the semaphore before starting up the next transfer. If that isn't true, how do you know if the interrupt you just got was for the transfer you just were beginning?
Good point even though the isr triggering before take() is unlikely to happen. As you said, if I add a hardware busy status check before the next transfer, I basically can avoid the trouble. Thank you very much!