Search code examples
c++polymorphismesp32freertos

C++ freertos Call overriden function of base class from subclass after getting semaphore failes


I am trying to make a base class that holds a semaphore and wait until it can take the semaphore and then call a function in a derived class. (I want to have a number of different base classes that implement the function different but all of them schuld execute this function only after acquiring the semaphore. I am not very familiar with freertos, so i think probably the error is related to that.

I made some attempts but so far with no success.

Here is the basic code:

The Base class:

class CommandMode{
public:
    CommandMode(xSemaphoreHandle* smphr_calc_steps);
    virtual ~CommandMode(){};
    virtual int GetNextState()=0;

protected:
    static void TSK_CalcSteps(void* params);
    xSemaphoreHandle* smphr_calc_steps;
    virtual void CalculateSteps(){};
};

CommandMode::CommandMode(xSemaphoreHandle* smphr_calc_steps):smphr_calc_steps(smphr_calc_steps){
    xTaskCreate(&TSK_CalcSteps, "output data calc", 2048, this, 1, NULL);
}

void CommandMode::TSK_CalcSteps(void* params){
    CommandMode* cm = (CommandMode*) params;
    while(true){
        //force reevaluation at least every 60s
        xSemaphoreTake(cm->smphr_calc_steps, 60*1000/portTICK_PERIOD_MS); // i think the problem is related to that line
        cm->CalculateSteps();
    }
}

and here the derived class:

class Mode1:public CommandMode{
public:
    Mode1(xSemaphoreHandle* smphr_calc_steps);
    ~Mode1(){};
    int GetNextState() override;
    
protected:
    void CalculateSteps() override;
};

Mode1::Mode1(xSemaphoreHandle* smphr_calc_steps) : CommandMode(smphr_calc_steps){}

void Mode1::CalculateSteps(){
    Serial.println("Mode1::CalculateSteps");
}

int Mode1::GetNextState(){
    Serial.println("Mode1::GetNextState");
    return 5;
}

Then i try to invoke them similar to this:

CommandMode* current = nullptr;
xSemaphoreHandle smphr = xSemaphoreCreateBinary();
//xSemaphoreHandle smphr2 = xSemaphoreCreateBinary();

Serial.println("init with mode1");
delay(200);
Mode1* a = new Mode1(&smphr);
a->GetNextState(); //This will work
current = a;
current->GetNextState(); //This will work as well

xSemaphoreGive(smphr);  //This does not work as intended/causes the issue

Im also not sure about the line cm->CalculateStepes(). Since i passed 'cm' as a void* will it still be evaluating the correct subclass's module? I am running this on an ESP32 with Plattform IO in case this is important.

So far i sometimes got a watchdog error, but mostly i get the following two errors:

/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c:1443 (xQueueGenericReceive)- assert failed!
abort() was called at PC 0x40087f1d on core 0

assertion "res == coreID || res == portMUX_FREE_VAL" failed: file "/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/portmux_impl.inc.h", line 105, function: vPortCPUAcquireMutexIntsDisabledInternal
abort() was called at PC 0x400d8cf3 on core 0

I tried use the whole code out of a task itself and experimented with multiple delays(), but I never got it working.

I would be glad if someone could tell me where the problem is, or has a suggestion how to implement this behavior in a better way? I want to have other functions and values in the base class as well. This works without a problem but the calling of the derived class's function based upon the semaphore is not working at all.


Solution

  • xSemaphoreTake expects the handle to be passed by value, you pass it by pointer, that is definitely wrong.

    The code compiles because SemaphoreHandle_t itself is a hidden pointer (hence this not being a good practice, but occurs often in low-level stuff for other good reasons). This also means that you can and should pass and store smphr_calc_steps as a value in your class, being a pointer its cheap to copy. The extra indirection is not needed and only complicates stuff.

    FreeRTOS allocates the semaphore on the heap and returns a pointer(=the handle) to you, your only responsibility is to call vSemaphoreDelete sometime in the future to free up the memory.