Producer consumer multiple thread and ISR synchronization

Hello!

I have an STM32 application that has three tasks (Task1, Task2, Task3) in a producer consumer architecture. Task 1 has Normal priority, Task 2 Normal1 priority and Task3 Normal2 priority. Every task has a while loop and I am using two binary semaphores. Execution starts with Task1 because the other two tasks are in blocked state because they don’t have available semaphores. In the end of the while loop semaphore for Task2 is released and the execution continues in Task2. When the execution is finished in Task2 semaphore for Task3 is released and Task2 is imediatelly preempeted by Task3. At the end of while loop in Task3 the execution continues again in Task1 with lowest priority because Task2 and Task3 are in BLOCKED state. The cycle continues forever.

In addition I would like to implement a hardware triggered ISR triggered by DMA controller. The ISR should change the execution of Task1 (in case DMA has finished read the values) but in case ISR did not happen the whole program should not go into blocked state. Meaning even if the ISR is not called I want to realease a semaphore that starts Task2 so that the cycle continues even if the DMA does not feed data. This is why i would like to find something else than xTaskNotifyFrom ISR(). I have one solution but it is not very beautiful.

Does anyone have any idea or advice on how to continue the scheduled cycle even if the ISR is not triggered?

Why not using task notifications for all synchronizations/signaling following the
Using RTOS task notifications As Light Weight Event Group scheme at least for Task1 waiting for ISR and task events simultaneously ?
For Task2/3 you can use task notifications as a light weight binary semaphore.
See FreeRTOS task notifications, fast Real Time Operating System (RTOS) event mechanism

Hmm. Maybe I could use xTaskNotifyWait function, but do you know maybe if xMaxBlockTime can be set also to 0? I would like my loop to be extremely efficient and waiting with all the tasks blocked for 1ms might be a bit too much.

xResult = xTaskNotifyWait( pdFALSE, /* Don’t clear bits on entry. /
ULONG_MAX, /
Clear all bits on exit. /
&ulNotifiedValue, /
Stores the notified value. */
xMaxBlockTime );

Sure you could set the wait ticks resp. time to 0 but I don’t get the point with efficiency :thinking:
If you want efficiency or best responsiveness you shouldn’t poll but wait for a events and process them.

As mentioned, the max block time is your friend, if you want to wait until the notification comes, but wait at most, a certain amount of time, use the max block time.

If you want the time to not start until task1 does something, then use two synchronizations, the first to wait for task1 to do that thing, and then the second one to wait for time or the operation to be done.

As mentioned, Direct to Task Notification using bits would be ideal here.

The other thing is that if you always are doing task1, which then starts task2 (which blocks task1), and then task2 starts task3 and when that is done you go back to task1, so you only want one of these tasks to run at a time, really sounds like this is actually 1 task with phases.

I am realizing now that if I put 0 on block time it really means that I am polling the system. I will think of how I can use bigger blocking time and spend the blocked timing of Task1 in other tasks.

Richard regarding your comment about 1 task with 3 phases I would partially agree but in future there will be a need to run few things concurently and in such way I need to have the architecture prepared. More tasks are to come.

Actually, if your enforce the execution order 1-2-3 via your cascaded semaphores, why use 3 tasks in the first place? You might as well do it all in a single task. Incorporating pseudo concurrency where it is not needed will not accomplish anything but introduce error potential and unnecessary os overhead.

One of the golden rules of concurrent design is that too much of something (eg synchronization) is as bad as too little of it, so a sound system design is essential.

Adding to what everyone else has said (possibly single task, use task notifications)…

Depending how ‘in the future’ the upcoming tasks will be added, and their interplay with tasks 1, 2, and 3, you could create a single task with some future proofing by coding with the SOLID principles in mind. A FreeRTOS task simply runs a function. Rather than having 3 tasks running the 3 functions and synchronizing them with task notification, you could just as well have the single task calling the nearly identical functions in sequence.