Crash when accessing attribute from thread

fcipaq
Posts: 10
Joined: Fri May 31, 2024 7:02 pm

Crash when accessing attribute from thread

Postby fcipaq » Fri May 31, 2024 7:31 pm

First of all: Hi to everyone. I have been reading here for while but never joined the community. This site really helped me a lot - thank you!

Now, I have the following problem: When accessing an attribute of the instance of an object from a different thread, the system panics.

This is an example code (that does compile and run):

main

Code: Select all

#include "freertos/FreeRTOS.h"

#include "backends/platform/esp32/osystem.h"

extern "C" {
  extern void app_main();
}

void app_main(void) {

  _Esp32::OSystem_Esp32 * g_system = new _Esp32::OSystem_Esp32();
  assert(g_system);

  while (1) {
    vTaskDelay(1);
  }
  
}
class file

Code: Select all

#include "osystem.h"

#include "freertos/FreeRTOS.h"

namespace _Esp32 {

	OSystem_Esp32::OSystem_Esp32() {
	   xTaskCreatePinnedToCore((TaskFunction_t) &_Esp32::OSystem_Esp32::input_task, "input_task", 1024, NULL, 5, NULL, 0);
	}
	
	OSystem_Esp32::~OSystem_Esp32() {}

	void OSystem_Esp32::input_task(void* arg) {

	    _value++;  // LoadProhibited
	    
	  while (1) {
	    vTaskDelay(1);
	  }

	}
	
} // namespace _Esp32
and the header

Code: Select all

namespace _Esp32 {

class OSystem_Esp32 {
public:

	OSystem_Esp32();
	virtual ~OSystem_Esp32();
	
private:
	int _value;

private:
	void input_task(void* arg);

};

} // namespace _Esp32
And I get:

Code: Select all

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x4200916e  PS      : 0x00050030  A0      : 0x00000000  A1      : 0x3fca6f70  
0x4200916e: _Esp32::OSystem_Esp32::input_task(void*) at /home/pico/scummesp32/test/components/backends/platform/esp32/osystem.cpp:15

A2      : 0x00000000  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x00000000  
A6      : 0x00000000  A7      : 0x00000000  A8      : 0x00000000  A9      : 0x3fc96eb8  
A10     : 0x00000000  A11     : 0x00000000  A12     : 0x3fc971b0  A13     : 0x3fca70ec  
A14     : 0x3fc971a8  A15     : 0x3fc96eb8  SAR     : 0x00000000  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000004  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  


Backtrace: 0x4200916b:0x3fca6f70
0x4200916b: _Esp32::OSystem_Esp32::input_task(void*) at /home/pico/scummesp32/test/components/backends/platform/esp32/osystem.cpp:13
I am using Linux and the current ESP IDF.

Why can't I access the attribute from the spun of thread and what would be the proper way to work around this issue?

Any explainations/suggestions are highly appreciated!

ESP_Sprite
Posts: 9577
Joined: Thu Nov 26, 2015 4:08 am

Re: Crash when accessing attribute from thread

Postby ESP_Sprite » Sat Jun 01, 2024 7:59 am

I don't know what that TaskFunction_t thing is supposed to do, but normally a task is started using a C function pointer. Specifically, a call through that loses the value of the 'this' pointer. The classic way is to use a static C++ function member as the task starting target, and pass the 'this' pointer through the user argument of xTaskCreatePinnedToCore. See e.g. this topic, specifically enitalps response, for how to do that.

fcipaq
Posts: 10
Joined: Fri May 31, 2024 7:02 pm

Re: Crash when accessing attribute from thread

Postby fcipaq » Sat Jun 01, 2024 9:49 am

Thank you, ESP_Sprite. Ah, I see... losing the 'this' pointer's value also explains:

Code: Select all

EXCVADDR: 0x00000004
The explicit conversion of "TaskFunction_t" was about a compiler error:

Code: Select all

error: cannot convert 'void (_Esp32::OSystem_Esp32::*)(void*)' to 'TaskFunction_t' {aka 'void (*)(void*)'}
This really helped me a lot. Thanks for the clarification!

MicroController
Posts: 1552
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Crash when accessing attribute from thread

Postby MicroController » Sun Jun 02, 2024 6:02 pm

That compiler error was actually telling you that a pointer to a member function in _Esp32::OSystem_Esp32 cannot be converted to the required non-member-function pointer, and the compiler was correct as you noticed.

Here's another example of how a member function can be used in this case:

Code: Select all

class MyClass {
 
  public:
  
  MyClass() {
    xTaskCreate(
      &MyClass::task_func, // task_func is a _static_ member function!
      "MyClassTask",
      1024,
      (void*)this, // "this" goes into the task as argument
      5,
      NULL);
  }
  
  protected:
  // Member function to run in the task.
  void run() {
    // Can freely use 'this' and all members of this instance.
    // Could also be (purely) virtual to allow child classes to implement different tasks.
    while(1) {
      // ...
    }
  }
  
  private:
  
  // STATIC function to be called as the task entry point:
  // Serves as an  'adapter' between a void (*)(void*) and MyClass::run()
  static void task_func(void* arg) {
  
    ((MyClass*)arg)->run(); // Take the instance passed in as argument and call run() on it.
    
    vTaskDelete(NULL); // Just in case run() would accidentally return...
  }  

}
Btw, I recommend to not create a task in the constructor. For one, if task creation should fail, a constructor cannot return a value indicating this, it can only throw an exception. And secondly, task creation via constructor will (probably) not work if you sensibly decide to make a task class' instance a global (singleton) variable. Consider creating a "bool start()" member function or something like that to separate initialization of the instance from creating/running its task.

fcipaq
Posts: 10
Joined: Fri May 31, 2024 7:02 pm

Re: Crash when accessing attribute from thread

Postby fcipaq » Mon Jun 03, 2024 5:15 pm

Thank you, MicroController for the explanation and the suggestion! It worked perfectly!

Who is online

Users browsing this forum: No registered users and 224 guests