Storing xTaskHandles in central class

Hi All,

I am trying to implement a program where I am pausing some task and then re-enabling it later.
I am working with an esp32 board using platformio and android framework, but I am quiet sure the issue is not with the board or with the library but with my c++ knowledge.

Let me show an example what I tried so far. GitHub - mblasee/freertos-xTaskHandle-help
This is a stripped, bare minimum example of the program I am working on.

This is the exception that I am getting.

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:8896
load:0x40080400,len:5828
entry 0x400806ac
[I][TaskHandles.cpp:17] TaskHandles(): >>
[I][TaskHandles.cpp:18] TaskHandles(): <<
[I][TaskHandles.cpp:23] getTimerHandle(): >>
[I][TaskHandles.cpp:26] getTimerHandle(): creating new timer handle
[I][TaskHandles.cpp:29] getTimerHandle(): <<
[I][TaskHandles.cpp:37] getTimerStopHandle(): creating new timer stop handle
[I][Controlle ter.cpp:30] timerStopTasimerTask(): k(): startistarting timng timer stoer task
p task
[I][Controllr.cpp:46]
[I][Controller.cpp:33] timerTask(): timer increase task
[I][Controller.cpp:33] timerTask(): timer increase task
[I][Controller.cpp:33] timerTask(): timer increase task
[I][Controller.cpp:33] timerTask(): timer increase task
[I][Controller.cpp:33] timerTask(): timer increase task
[I][Controller.cpp:54] timerStopTask(): suspending
[I][TaskHandles.cpp:23] getTimerHandle(): >>
[I][TaskHandles.cpp:29] getTimerHandle(): <<
Guru Meditation Error: Core  0 panic'ed (StoreProhibited). Exception was unhandled.     
Core 0 register dump:
PC      : 0x4008a2e5  PS      : 0x00060133  A0      : 0x80089187  A1      : 0x3ffb9220  
A2      : 0x3ffb81ec  A3      : 0x3ffb9340  A4      : 0x00000001  A5      : 0x00000001  
A6      : 0x00060120  A7      : 0x00000000  A8      : 0xbaad5678  A9      : 0xabba1234  
A10     : 0x00000004  A11     : 0x0000002e  A12     : 0x00000008  A13     : 0xffffffff  
A14     : 0x00000000  A15     : 0xffffffd1  SAR     : 0x00000004  EXCCAUSE: 0x0000001d  
EXCVADDR: 0xabba123c  LBEG    : 0x400014fd  LEND    : 0x4000150d  LCOUNT  : 0xfffffffc  

Backtrace: 0x4008a2e5:0x3ffb9220 0x40089184:0x3ffb9240 0x400d0cab:0x3ffb9260 0x400d0cb9:0x3ffb9280 0x40087d49:0x3ffb92a0
  #0  0x4008a2e5:0x3ffb9220 in uxListRemove at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/list.c:214
  #1  0x40089184:0x3ffb9240 in vTaskSuspend at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/tasks.c:4560   
  #2  0x400d0cab:0x3ffb9260 in Controller::timerStopTask() at src\Controller/Controller.cpp:56
  #3  0x400d0cb9:0x3ffb9280 in Controller::startTimerStopTask(void*) at src\Controller/Controller.cpp:41
  #4  0x40087d49:0x3ffb92a0 in vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:355 
(discriminator 1)

I also tried that I am creating the xTaskHandle in the Controllers constructor and pausing the task right away and it worked. So both FreeRTOS and the esp32 board works as expected.

The way I am doing it this way is because in real world I am going to have many tasks and I will want to pause and restart them from various places in the code, so it looks straightforward to me to store them in a central class and get them once I need. If this is not the c++ way(probably not) I am happy to learn how should I do it :slight_smile:

Thanks,
Balazs

What is the data-type xTaskHandle : freertos-xTaskHandle-help/TaskHandles.h at main ¡ mblasee/freertos-xTaskHandle-help ¡ GitHub

It seems thatTaskHandles::getTimerStopHandle() returns xTaskHandle * and you seem to use that as TaskHandle_t: freertos-xTaskHandle-help/Controller.cpp at main ¡ mblasee/freertos-xTaskHandle-help ¡ GitHub

If you mean to have task handle, why can’t you just have TaskHandle_t as this is an opaque pointer:

class TaskHandles
{
public:
    static TaskHandles *getInstance();
    TaskHandles();

    TaskHandle_t getTimerHandle();
    TaskHandle_t getTimerStopHandle();

private:
    static TaskHandles *instance;
    TaskHandle_t timerHandle = nullptr;
    TaskHandle_t timerStopHandle = nullptr;
};

TaskHandle_t TaskHandles::getTimerHandle()
{
    return timerHandle;
}

TaskHandle_t TaskHandles::getTimerStopHandle()
{
    return timerStopHandle;
}

Thanks.

Honestly there is not much reason. I’ve used xTaskHandle instead of TaskHandle_t because I’ve seen that one in multiple tutorials and it was working when everything was in one file.
I tried to modify the code according to your advises but still no luck.

I’ve actually tried two things(and updated the repo):
1: Just pasted what you advised, strangely after this the exception went away but the timerTask() continued to write to the output.
2: added these two lines to the TaskHandles constructor:
timerHandle = new TaskHandle_t();
timerStopHandle = new TaskHandle_t();

I am suprised that the exceptions didnt occure anymore in the option 1 because as I see I was just passing null pointers arround. To overcome this I tried to give value to them but this resulted in the very same exception.

Thanks for the help!

Something seems strange here. First, TaskHandle_t is itself a pointer to the Task Control Block, (an opaque pointer as the definition of that structure is never made public), so using ‘new’ to create one of these on the heap is not very useful. This means that your timerHandle is a pointer to a pointer to the TCB, and everywhere you access it you are going to need an extra level of indirection. The ONLY major use for something like this would be if you had a task that you kept on creating and deleting, so the value of the handle kept changing, but you want to be able to pass to some function WHICH of these tasks that you kept on creating ad deleting you wanted to work with.

Let me describe my use case here, maybe my whole approach is wrong.
So I am going to read multiple sensor values, for example I will read from gps module, I will read from a joystick module, I will update screen and another task will update a ble characteristic.
What I am aiming for is to create these tasks and be able to access to their handles from different places in the code.
For example create the joystick reading task but only actually start after my ble connection callback fired. Generally I would like to start and pause my tasks responding to various events. To be able to achieve this it seemed convenient to hold task handles in a central location and just get the handler once I want to do something with the task.

@blase

this sounds like a problem that I am currently trying to solve:

  1. Create TaskManager (as a task with it’s own queue)
  2. Pass TaskManagerQueue to tasks we want controlled (pass in setup data i.e. pvParameters)
    This is so that they can place a message on the TaskManagerQueue to say that it needs registering
  3. TaskManager stores the TaskHandle_t in an array
  4. Some task sends a “Suspend” message to the TaskManagerQueue
  5. TaskManager loops over TaskHandle_t array calling vTaskSuspend for each handle
  6. Some task sends a “Resume” message to the TaskManagerQueue
  7. TaskManager loops over TaskHandle_t array calling vTaskResume for each handle

Hello @blase,

Since the FreeRTOS Kernel can do what you are trying to do, why not let it take care of “starting” (resuming) and “stopping” (suspending) tasks?

Since you are waiting for events in each task, why not use Event Groups? That way, you can signal multiple event and the task blocked on the event will be resumed by the FreeRTOS kernel.

Does that seem apt? Did I understand your problem correctly?

IMO (asynchronously) suspend/resuming tasks from an other task is not the best approach to design a robust and modular application. Why not just signaling events to tasks and process the events in the task (state machine) ? I don’t really understand the benefit of using async. suspend/resume for task synchronization.

I agree with Hartmut here. Why do you need to actually suspend the tasks. They will block on their queues waiting for data, that’s as good (or better) then suspending them.

@richard-damon My use case is:

when the battery voltage drops below a certain threshold we want to suspend all tasks and then resume them again when the battery has charged to another threshold

        TaskManagerMessage_t* message = nullptr;
        float voltage = BatteryReader_t::ReadVoltage();

        if(voltage < LOWEST_ALLOWED_BATTERY_VOLTAGE && !IamRunning)
        {
            message = new TaskManagerMessage_t(nullptr, TaskManagerFunctions::Suspend);
            IamRunning = true;
        }
        else if(voltage >= WATCHDOG_BATTERY_CUTOFF && IamRunning)
        {
            message = new TaskManagerMessage_t(nullptr, TaskManagerFunctions::Resume);
            IamRunning = false;
        }

        xQueueSendToBack(taskManagerQueue, (void*)message, portMAX_DELAY);
        delete message;

this scenario will come into play if there is not much sunlight, for example, and the solar panel is not charging the battery as quickly as it is discharged.

The point I think you are missing is that if the task just blocks because it isn’t getting data, it will save just as much power as suspending it.

In fact suspending a task save NO power, just gives other tasks more time, just as blocking does. To save power you need to get the processor to be able to drop into a low power mode for it.

1 Like

The point I think you are missing is that if the task just blocks because it isn’t getting data, it will save just as much power as suspending it.

You might be right there -as in I may have overlooked that nugget!

The power saving from suspending a task itself is negligible for sure, however when the tasks are running sensors/components which do draw power, suspending those tasks (be it by not receiving data, or by a call to vTaskSuspend) will have the desired effect of reducing power drain

Though you’ve definitely given me cause to reconsider my approach to this, since task suspending/resuming is feeling more and more like the wrong way to manage this.

Be aware that suspending/resuming the sensor task can happen e.g. right in the middle of an I/O sequence because it’s not synchronized to task execution. That might cause side effects. I’d tell the sensor task to stop polling the sensors by a command or event and waiting for a resume command/event to continue.

1 Like

What I’ve found out this evening is that I really should have spent a bit of time sooner, reading the FreeRTOS documentation

I will point out that suspending a task doesn’t reduce any power consumption of the task, unless the I/O that task is doing is costing the power. The issue is the processor will still be executing instructions just as fast, as if nothing else is running, the idle task will be.

What can be done, is use some of the Hooks is to let the idle task detect that things won’t need work for awhile and then have that move to a lower power mode. One simple one is to just add a Wait For Interrupt instruction for some modest savings. Beyond that, it depends on what other lower power modes the processor has.

It is also much better to program the task to understand when it needs to ‘power down’ and take the appropriate actions, and then block until needed. This then allows it to communicate a busyness to the idle task to allow it to properly decide about low power modes.

First of all thanks for everyone for the ideas and for helping me!
I think I solved my original problem, however not in the way I intended the first place. Looking back that wasnt even a good idea not sure why I was trying to do that for so long(freertos-xTaskHandle-help/src at main ¡ mblasee/freertos-xTaskHandle-help ¡ GitHub)

@bill-richards I would love to see your project if you can share it!

@kanherea This actually sounds great, I am definitely going to give it a try, thanks!

@hs2 The idea was to have something like this. This is a freertos task in the end and it calls a ble method which writes to the characteristic. The idea was to not let it run until there is alive BLE connection. So I create the task and pause it and after the BLE callback tells me that there is a connection I just unpause it and it starts to do its job. Actually I was kind of reinventing wheel and trying to do something event groups are for.


void SensorReader::publishSpeed()
{
....
BLE::getInstance()->writeSpeedCharacteristic(mappedSensoreReading);
....
}

@richard-damon the idea was to create all the tasks at the beginning and then manage them when I actually need them. So to create the ble characteristic publisher task and then just enable its running once we have ble connection. I am going to dig myself into event groups as that seems to be the way it should be solved.

Yes, rather than have an external agent suspend the task, make the task know when it should be doing its job and just block when it doesn’t have something useful to do.

That is almost always a better solution.