How can I find out if another task is currently blocked waiting for my binary semaphore?

I have a created a binary semaphore using xSemaphoreCreateBinary and I have successfully taken it using xSemaphoreTake inside a function, myFunction. Later on inside myFunction I want to give the binary semaphore, but only if some other tasks actually wants it (otherwise, I should simply keep it and continue doing more things). How can I find out if another task is currently blocked waiting for my binary semaphore?

What happens if another tasks asks for the semaphore immediately after you decided to not give it away? Your use case is somewhat unusual and prone to race conditions. Most likely there is a better way to accomplish what you want, can you sketch more precisely what you want to gain by doing it that way?

The long story is that I’m working on a UART-based bus that has a master and a slave and both of them can be requestors (can read/write from/to a server) and servers (can answer to read/write requests from a requestor). If they both want to access the bus, the master decides who gets the access, but there’s a rule that the master must be fair (as long as the slave wants to initiate an access, it should should be granted at least every other access). If the slave just finished an access and wants to initiate another one, then I need to find out if the master also wants to initiate an access before I decide to simply hold on to the binary semaphore for the pending slave-initiated access as well.

In your race condition example, the master would have to wait until the slave has completed the pending access before it can initiate its own access.

So do I get it right that you intend to funnel concurrent accesses to THE SAME UART from several tasks through some kind of serialzation architecture? We discussed this many times before, there are much better (more efficient and less error prone) ways to accomplish this, typically via a single task that does the I/O and acts as a message pump, receiving the requests from other tasks and serving them serialized in a single process.

My current approach is that I have readRegs and writeRegs-functions and there’s no restriction on how many tasks are allowed to call these functions. In addition, I need to hook up handleReceivedReadRegs and handleReceivedWriteRegs callbacks for the incoming read/write requests. I have no queues or serialization, my idea was that if the bus is busy, then the calls to readRegs/writeRegs will block until the bus becomes ready. I have a dedicated task that handles (via xEventGroup and event bits) everything that “touches” the UART (configuration of the UART/DMA peripherals to initiate the accesses, Tx/Rx interrupts, timer expiration for polling for connect/disconnect events, external interrupts for the slave’s request-to-send pin). I think it would be complicated to go through a queue because task A could write 1 byte, task B could read 300 bytes, task C could write 200 bytes and task D could read 1 byte. So, it would be a difficult queue to implement and probably inefficient and/or RAM-consuming.

I realize now that the solution is very easy:

volatile int numTasksTryingToTakeThisSemaphore = 0;

numTasksTryingToTakeThisSemaphore++;
if (xSemaphoreTake(handle, 0xFFFFFFFF)) {
numTasksTryingToTakeThisSemaphore–;
} else {
numTasksTryingToTakeThisSemaphore–;
// Error handling…
}

I’m not sure … also note that inc/decrementing a variable is not atomic.

This only works for truly atomic test-and-increment operations, otherwise you will run into all kinds of concurrency issues. Oops… Hartmut was faster.

Also note that all of what you do requires that your serial protocol is single byte full duplex fully asynchronous. If that is not the case (applies to at least 99% of all serial protocols), you will not gain anything but problems.

This then?

#include “atomic.h”
volatile uint32_t numTasksTryingToTakeThisSemaphore = 0;

Atomic_Increment_u32(&numTasksTryingToTakeThisSemaphore);
if (xSemaphoreTake(handle, 0xFFFFFFFF)) {
Atomic_Decrement_u32(&numTasksTryingToTakeThisSemaphore);
} else {
Atomic_Decrement_u32(&numTasksTryingToTakeThisSemaphore);
// Error handling…
}

no, because testing and incrementing must also happen uninterruptable.

So how about your serial protocol? Does it support random access to the medium at all?

Then I’ll just turn off interrupts (__disable_irq() and __enable_irq() on my microcontroller).

The UART protocol itself is off-topic in this thread, but I have it working on 3 other released products running on “bare metal” so the protocol works.

No, it isn’t, but if you wish to fall into the trap doors yourself, that’s fine. All the best for the upcoming year to you!

1 Like

This is my final solution (I use an STM32U-microcontroller). I would like to thank everybody who contributed in this thread.

#define DISABLE_ALL_INTS()     uint32_t old_primask = __get_PRIMASK(); \
                                                __disable_irq()
#define ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED()  if (!old_primask) {   \
                                                    __enable_irq();    \
                                                }

volatile int numTasksTryingToTakeThisSemaphore = 0;

DISABLE_ALL_INTS();
numTasksTryingToTakeThisSemaphore++;
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
if (xSemaphoreTake(handle, 0xFFFFFFFF)) {
    DISABLE_ALL_INTS();
    numTasksTryingToTakeThisSemaphore--;
    ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
} else {
    DISABLE_ALL_INTS();
    numTasksTryingToTakeThisSemaphore--;
    ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
    // Error handling…
}

I’d also use an other approach to solve the problem as mentioned by @RAc .
However, just as a hint C11 resp. C++11 support atomics and so do C/C++11 compliant compilers like GCC since many years.
See e.g. Atomic operations library - cppreference.com.
Good luck :+1:

One other option is for the task to just give the semaphore, yield, then try to take it again.

This only works if the other task has at least as high of a Task Priority as this one.

That’s what I tried at first. But I did not receive a new external interrupt from the slave after I had given the semaphore so I had to set an event bit manually to tell my UART-task to handle a slave-initiated access. However, this only gave a window of a about 400 microseconds for the master-initiated access to take the semaphore and this was too little time for it to do so. I could use a timer to delay the setting of the event bit to initiate a slave-initiated access, but I think it’s cleaner to use the numTasksTryingToTakeThisSemaphore variable.

Excuse the wording, but the code as it stands is unusuable. First of all, it will not compile (you probably meant to write – instead of -), second, both the positive and negative outcome of your semaphore taking code yield the same code (so you would not need an if in the first place), third, you call xSemaphoreTake with a timeout of portMAX_DELAY, meaning that an error return means something fatal and undefined, and finally, there is no code that tests on numTasksTryingToTakeThisSemaphore and takes actions - and you will find that the problems begin to occur in exactly that code branch.

Good luck, again.

Something seems off in your description of the problem then. You are asking about another task actually waiting on taking the semaphore, and if that is the case, and it has a priority of at least the level of the task giving it up, it will IMMEDIATELY take the semaphore.

IF you are talking about some protocol to change the mode, then that all needs to be coded in your software, with its own decision process for how and when to change modes.

  1. Yes, you are correct, it was an auto-formatting thing on this forum (look at your own post) that it auto-changes two minus signs into – in plain text mode (i.e. non-preformatted text) that I copied-and-pasted into preformatted text after I found that button. I have edited the post.
  2. In the code I’m working with we trace out an error message in case there’s an error, but I thought it was unnecessary to include that (it’s not relevant to the topic of this thread).
  3. In the code I’m working with we have a define for the delay (that is set to 5000), but I thought it was unnecessary to include that (it’s not relevant to the topic of this thread).
  4. In the code I’m working with I made small function that uses the numTasksTryingToTakeThisSemaphore variable, but I thought that how I intend to use it was quite obvious:
bool masterInitiatedAccessIsPending() {
    return (0 < numTasksTryingToTakeThisMutex);
}

I have tested my code, when I run my stress test it works fine and I can see that the slave gets granted every other access now.