Using uVision v5.27.1.0 (RTOS 1) with STM32L4xxx. In an ISR two different signals are sent to the same thread like below:
.
osSignalSet(myTask, SIGNAL_1);
osSignalSet(myTask, SIGNAL_2);
In the waiting task myTask, the following code is executed:
for (;;) {
osEvent evt = osSignalWait(0, osWaitForever);
u32 signals = evt.value.signals;
The "signals " will NOT contain SIGNAL_2. Only SIGNAL_1. In other words, the waiting task will wake up on the first of the two signals, even though both of them were posted in the same ISR at the same time.
I think it is a bug .
The work around would be, in general case, to add the following code to code above:
evt = osSignalWait(0, 0);
signals |= evt.value.signals;
Now both SIGNAL_1 and SIGNAL_2 are consumed, and are in the variable "signals " .
Cheers!
Looking at the RTOS code the behavior you are describing does seem to be what will happen.
The 1st signal gets processed in the PENDSV and it sees that the task is waiting on a signal. The signal generated fully matches what is being waiting on (any signal) and it changes the task to READY instead of waiting on a signal AND sets the return value to the current signals set.
On the 2nd signal, the PENDSV sees it and set the signal, but since the task is no longer "waiting" on a signal the return value is not updated. The signal is not lost it is just not returned along with the first signal set.
As a note, if you set 2 signals in a task in 2 separate set_signal calls you will also get the first signal returned from the 1st wait and the 2nd signal will come in a "future" wait.
When first looking at your description it did seem like something might be missed, but it is not. One of the reasons it was done the way it was done is so that no signal would ever be missed.
All the os_wait() is waiting for is the criteria for what you are waiting on to be satisfied. The signals returned will be the ones that contribute to that wait being satisfied at the point that it was satisfied. This is why when you set the first signal, that is all you get in return from the wait. The 2nd signal was not part of / contributing to the satisfying of the wait.
IF you want make sure that you get both signals, you will need to make sure that they are both present when you set the signal. Do not make 2 separate calls to the Set.
No indications that a signal has been set are lost in this implementation. The behavior in the implementation is (very) appropriate and not a bug.
The "work around" you have probably works fine, but the 1 thing that may not be obvious is that you don't actually know if both signals happened at the same time, all you know is that both have happen before your task has run again. It could easily be that the IRQ was called and 1 of the signals was set and before your thread ran the next wait, the IRQ was called again setting the other signal.
Maybe you shouldn't set the two signal in separate calls to osSignalSet to begin with, but rather:
osSignalSet(myTask, SIGNAL_1| SIGNAL_2);
Robert,
Can you please confirm what happens in the following situation:
A task is ready to run, but preempted by a higher priority task. While being preempted, two signals are sent to it, first signal_A and then signal_B. At some point the task is finally running again and enters the wait for any signal. Since the signals are already "posted", the wait will immediately return. Which signals will be returned? Only the first qualifying signal_A?
Sergey
Hi Broeker,
of course, in real code we do not send two signals back-to-back. But if you use a peripheral, for example a timer with 4 capture/compare channels, then the same ISR will process 4 totally independent requests from independent channels. The ISR would normally have 4 "if" statements to check each of the channel interrupt requests, and inside that 'If" code there will be an osSignalSet() call that would set its corresponding signal. If some requests happen to come at the same time, like in my case, there will be multiple independent osSignalSet calls within one ISR. Although it is possible to write the ISR code so that there is only one osSignalSet with the combination of signals, but it is not how it should be done normally, I think.
Also, if there are nested interrupts from whatever sources, it will be impossible to combine signals into one osSignalSet call, which will result, in essense, in side-by-side calls, as I indicated in my post.
No. In the case you describe you will get both signals. IF A task is in a wait when the 2 signals are sent, then you will only get Signal_A. You will need to do another wait to get Signal_B.
Thanks, good to know.
It would be a nice idea to clarify the effects of the first qualifying signal for a waiting task somewhere in the documentation. None of the resources mention it.
And the last question. If a task was in a wait, but another, higher priority task, was running, and in that time two signals are sent to the waiting task. When the waiting task gets to run and immediately returns from the wait, will it also return only the first signal?
You are correct in assuming that you cannot make any assumptions about when 2 different signals have been set. That is what you are trying to do and you really cannot do that and that is what is causing you your perceived issue.
Maybe you need to rethink your application code.
while (1) { TheEvent = osWaitEvent(0,osWaitForever); if (TheEvent.status == osEventSignal) { if (TheEvent.value.signals & SIGNAL1) { } if (TheEvent.value.signals & SIGNAL2) { } if (TheEvent.value.signals & SIGNAL3) { } if (TheEvent.valeu.Signals & SIGNAL4) { } }
This is exactly what I do. My issue was that I assumed (wrongly) that two signals set independently in the same ISR would be consumed and returned by the wait at the same time. Say, in a imaginary application a timer keeps track of hours_event and minutes_event using two output compare channels. The timer ISR would set two signals: s_hour and s_minute to signal that an hour has changed or the minute has changed. Every 60 minutes there will be two signals in the same ISR.
If in the application code there is error checking: "if s_hour came, assert that s_minute also came with it", then this assertion will fail if in the ISR the s_minute interrupt request was processed first.
My issue was caused by a perception that we normally treat interrupts as an atomic operation when related to a task level. That is why is seemed totally normal and natural to expect the RTOS to return both signals. The reason why it returns the first qualifying signal only is purely implementation specific, I think.
Yes. Once you are in a wait, you will get the first signal(s) that satisfy the wait. If signals are already present, you will get them all. If none are present you will get the first call to osSetSignal() that satisfies the wait. (so if the first call has multiple signals, you will get multiple signals)
****
When a task enters osWaitSignal(), and not signals are waiting, it is put into the blocked queue and set the SIGNAL WAIT state. The first call that fully satisfies the SIGNAL_WAIT causes the task to be removed from the blocked queue and the return value set to and to be osEventSignal and the value set to the signals set at this point in time. This task is then put into the ready queue. At this point, the task is not considered to be waiting on signals any more. Any signal the task receives from this point until another osSignalWait() is called will be kept as if you are not currently waiting for signals.
We are focusing too much about how it is actually implemented as opposed to what you can count on from the signals functions.
Make no assumption about when 2 signals have been set in relation to each other. Your code should not need to know or care if SIGNAL1 has been set before, after or at the same time as SIGNAL2. You only know that they are both set.
I agree. Thanks!