Arduino-esp32: std::thread implementation for ESP32 to Arduino API/IDE

Created on 23 May 2019  Â·  99Comments  Â·  Source: espressif/arduino-esp32

wishful IMO for Arduino:
a std::thread implementation for ESP32, like meanwhile already available for Teensy (TeensyThread):
https://github.com/ftrias/TeensyThreads
or perhaps native C++ <thread>

stale

All 99 comments

Std::thread is usable already in arduino-esp32. Have you tried it already and found it not working?

Std::thread is usable already in arduino-esp32. Have you tried it already and found it not working?

I don't see an Arduino .ino example anywhere for testing

  • can you please provide a working .ino Example to test?

There may not be an example in the arduino-esp32 tree but there are a few in the esp-idf tree. The only change you would need to make to those is move the thread related calls from app_main() to setup(). There are also a few helper API from esp-idf to control stack size which is not part of the c++ spec.

sorry, I don't understand you what you mean...
tbh, I would need a working .ino code.

This is based on https://github.com/espressif/esp-idf/blob/master/examples/system/cpp_pthread/main/cpp_pthread.cpp with most of it stripped out so you have an example of usage. You can see more more options to configure the threads in the linked example.

#include <Arduino.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <memory>
#include <string>
#include <sstream>
#include <esp_pthread.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

const auto sleep_time = seconds
{
    5
};

void print_thread_info(const char *extra = nullptr)
{
    std::stringstream ss;
    if (extra) {
        ss << extra;
    }
    ss << "Core id: " << xPortGetCoreID()
       << ", prio: " << uxTaskPriorityGet(nullptr)
       << ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
    Serial.println(ss.str().c_str());
}

void other_thread_func() {
    while(true) {
        print_thread_info("This is the other thread.");
        std::this_thread::sleep_for(sleep_time);
    }
}

void setup() {
    Serial.begin(115200);
    auto cfg = esp_pthread_get_default_config();
    cfg.thread_name = "other thread"; // adjust to name your thread
    cfg.stack_size = 4096; // adjust as needed
    esp_pthread_set_cfg(&cfg);
    std::thread other_thread(other_thread_func);
}
void loop() {
    print_thread_info("This is the loop() thread.");
    std::this_thread::sleep_for(sleep_time);
}

have you tried that by your own?
I tried, but it does not get compiled ( a million errors).
Please try first by your own if you suggest a ino code ;)

PS:
it would be good to have an example like the following:

a) using just Arduino Serial.print(),
no << stream (that doesn't understand no common Arduino user)

b) explicite variable types (auto is not common),,
no sleep() but Arduino-like delay()

c) run 1 thread called
void thread1()
by
blinking LED_BUILTIN
delay(2000)
and monitoring that by Serial.println("thread1: LED ON") / ("LED OFF")

d) run 1 thread called
void thread2()
by
counting an uint_32_t counter each second by
Serial.print("thread2: "); Serial.println(counter);

e) run extra thread in the main loop() by
counting an uint32_t counter every 10 seconds by
Serial.print("main loop : "); Serial.println(counter);

f) show how to stop threads (thread1 or thread2) prematurely and to join threads .

have you tried that by your own?
I tried, but it does not get compiled ( a million errors).
Please try first by your own if you suggest a ino code ;)

I will admit I did not test it, it was copied almost verbatim from ESP-IDF. There are a few problems with the code though due to arduino-esp32 including IDF 3.2. The following will do what you are asking:

#include <Arduino.h>
#include <thread>
#include <chrono>

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

const auto one_sec = std::chrono::seconds
{
    1
};

void counter_loop() {
    uint32_t counter = 0;
    while(true) {
        Serial.print("counter_loop: ");
        Serial.println(counter);
        std::this_thread::sleep_for(one_sec);
    }
}

void blinker_loop() {
    uint32_t counter = 0;
    while(true) {
        digitalWrite(LED_BUILTIN, HIGH);
        std::this_thread::sleep_for(one_sec);
        digitalWrite(LED_BUILTIN, LOW);
        std::this_thread::sleep_for(one_sec);
    }
}


void setup() {
    Serial.begin(115200);
    pinMode(LED_BUILTIN, OUTPUT);
    std::thread counter_loop_thread(counter_loop);
    std::thread blinker_loop_thread(blinker_loop);
}

uint32_t main_loop_counter = 0;
void loop() {
    main_loop_counter++;
    Serial.print("main loop: ");
    Serial.println(main_loop_counter);
    delay(10000);
}

As for stopping a thread, you will need to refer to the C++ spec or add some sort of global flag that the threads will check to see if they should keep running. That is not specific to arduino-esp32 and would likely be better answered on the forums.

thank you,
it can be compiled and uploaded, but lots of runtime errors:

Rebooting...
ets Jun 8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
counter_loop: 0
abort() was called at PC 0x400e5ca7 on core 1

Backtrace: 0x40089150:0x3ffb1ed0 0x4008937d:0x3ffb1ef0 0x400e5ca7:0x3ffb1f10 0x400e5cee:0x3ffb1f30 0x400d0da2:0x3ffb1f50 0x400d0f92:0x3ffb1f70 0x400d1bc3:0x3ffb1fb0 0x40087c9d:0x3ffb1fd0

Rebooting...
ets Jun 8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
counter_loop: 0
abort() was called at PC 0x400e5ca7 on core 1

(repeatedly)

so again, please test by yourself before posting it 8)

so again, please test by yourself before posting it 8)

I'm only providing a sample for you to start with, you can decode the stacktrace and go from there. I'm not using this functionality myself or continuing to work on this example.

sorry, I appreciate your input, but a really working example is needed, for common use to common Arduino users.
I personally don't know C++ std::thread at all, I am only a little (minor) experienced in POSIX C99 pthread, and most of the Arduino users do not even know THAT ;)

That's why this (working) functionality is needed, for educational reasons and purposes 8)

but a really working example is needed, for common use to common Arduino users.

There is a working example in the IDF tree, but until IDF v3.3 is picked up by arduino-esp32 (after 1.0.3 likely) it is unlikely it will be meaningful here.

I personally don't know C++ std::thread at all, I am only a little (minor) experienced in POSIX C99 pthread, and most of the Arduino users do not even know THAT ;)

std::thread is based on pthread to an extent but there are limitations built into the std::thread specification, thread stack size is one area that was omitted from the specification. There are multiple options on how to work around this though, esp_pthread_get_default_config() being one that is available in IDF v3.3 or later.

That's why this (working) functionality is needed, for educational reasons and purposes 8)

This is also why the forums are ideal for this and not an issue on here.

I have to disagree,
my point is that I am requesting a working example code for Arduino, like the ones which already exist for WifiServer or WebServer or SD or SPIFFS or whatever, integrated into the standard Arduino IDE examples
OTOH, I daresay that my recent topics in the esp32 forum have not be resolved at all, in the absence of participation and content proposals, but again: not discussion is my intent, but a provided working Arduino API example.

@dsyleixa

Wow, "Do it for me, I'm a Snow Flake and Can't do it myself."

You do realize this is a volunteer project? No one owes you anything? If you are dissatisfied with the level of support you are receiving, I will provide fee based support, my current professional rates are $200/hr with 10 hr minimum. For more complex projects monthly or annual contracts are possible.

Chuck.

@dsyleixa

Wow, "Do it for me, I'm a Snow Flake and Can't do it myself."

You do realize this is a volunteer project? No one owes you anything? If you are dissatisfied with the level of support you are receiving, I will provide fee based support, my current professional rates are $200/hr with 10 hr minimum. For more complex projects monthly or annual contracts are possible.

Chuck.

I prefer free support, like I am used to on Arduino platform.
What I pay for is lots of hardware though, as far as I am getting free support for it.
Arduino is designed for hobbyists and artists not having the programming skills like professional computer scientists.
That's finally the reason for world-wide tremendous success of the Arduino project.

I prefer free support, like I am used to on Arduino platform.

free support is one thing, asking someone to write something from scratch for you is another. I've provided you an example to get you started. You then said the program crashed and you provided a stacktrace for it, unfortunately the stacktrace is meaningless without being decoded (the issue template links to how to decode it for inclusion in the issue). If you want to use std::thread in your arduino-esp32 programs it is time to dig into the std::thread reference documentation on https://en.cppreference.com/w/cpp/thread/thread which also provides examples that should work as written on the esp32.

I have no idea what the error messages mean and what causes the repeated reboots, it seems that the esp32 cannot process the std::threads in the way you (or the esp libs) implemented them so it can't run that code accordingly.

update:
thanks to 2 members of a different (German) forum the code could be fixed -
mainly
std::thread counter_loop_thread(counter_loop);
std::thread blinker_loop_thread(blinker_loop);
had to be made global!

(besides that, a counter++ each was missing )

feel free to copy and use that for the official esp32 repo examples!

[CODE]

#include <Arduino.h>
#include <thread>
#include <chrono>

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

const auto one_sec = std::chrono::seconds
{
    1
};

void counter_loop() {
    uint32_t counter = 0;
    while(true) {
        Serial.print("counter_loop: ");
        Serial.println(counter);
        std::this_thread::sleep_for(one_sec);
        counter++;
    }
}

void blinker_loop() {
    uint32_t counter = 0;
    while(true) {
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.print("blinker_loop (HIGH) counter: ");
        Serial.println(counter);
        std::this_thread::sleep_for(one_sec);

        digitalWrite(LED_BUILTIN, LOW);
        Serial.print("blinker_loop (LOW) counter: ");
        Serial.println(counter);
        std::this_thread::sleep_for(one_sec);

        counter++;
    }
}

std::thread counter_loop_thread(counter_loop);
std::thread blinker_loop_thread(blinker_loop);

void setup() {
    Serial.begin(115200);
    pinMode(LED_BUILTIN, OUTPUT);

}

uint32_t main_loop_counter = 0;
void loop() {
    main_loop_counter++;
    Serial.print("main loop: ");
    Serial.println(main_loop_counter);
    delay(10000);
}

[/CODE]

[QUOTE]

main loop: 1
counter_loop: 1
blinker_loop (LOW) counter: 0
blinker_loop (HIGH) counter: 1
counter_loop: 2
counter_loop: 3
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
counter_loop: 4
counter_loop: 5
blinker_loop (LOW) counter: 2
blinker_loop (HIGH) counter: 3
counter_loop: 6
counter_loop: 7
blinker_loop (LOW) counter: 3
blinker_loop (HIGH) counter: 4
counter_loop: 8
counter_loop: 9
blinker_loop (LOW) counter: 4
blinker_loop (HIGH) counter: 5
counter_loop: 10
main loop: 2
counter_loop: 11
blinker_loop (LOW) counter: 5
blinker_loop (HIGH) counter: 6
counter_loop: 12
counter_loop: 13
blinker_loop (LOW) counter: 6
blinker_loop (HIGH) counter: 7
counter_loop: 14
counter_loop: 15
blinker_loop (LOW) counter: 7
blinker_loop (HIGH) counter: 8
counter_loop: 16
counter_loop: 17
blinker_loop (LOW) counter: 8
blinker_loop (HIGH) counter: 9
counter_loop: 18
counter_loop: 19
blinker_loop (LOW) counter: 9
blinker_loop (HIGH) counter: 10
counter_loop: 20
main loop: 3
counter_loop: 21

[/QUOTE]

A perfect base to start from for further applications!
A big leap for mankind! 8)
thanks to all, especially @atanisoft !

edit, update:
interestingly, unexpectedly, the led_blinker_loop counter is incremeted between LED ON/OFF-pairs, although it's in the same thread loop by identical counter state, and the inc had been expected just for either future LED ON/OFF pairs each then.... (?)

I suspect the reason those std::thread objects needed to be global is so they don't go out of scope when setup() completes. Another (and better) way to fix this would be to declare them as std::unique_ptr in global scope and allocate them from setup() so they are guaranteed to start when you expect them to.

As for the odd behavior on the blinker counter, you can observe the same for counter_loop as well. It skips 0 in both cases. I suspect this may be due to global declaration and the thread starting before Serial.begin() has executed, but that is only a guess. It could be something else.

thank you,
as to the thread declarations, do you mean that way...?

std::unique_ptrstd::thread  counter_loop_thread(counter_loop);
std::unique_ptrstd::thread  blinker_loop_thread(blinker_loop);

as to the odd counters:
how could that be fixed?
it looks as if that is a bug in the libs or the esp32 core?
After all, this odd behaviour is not logical, is it?

as to the thread declarations, do you mean that way...?

close, try this:

std::unique_ptr<std::thread>  counter_loop_thread;
std::unique_ptr<std::thread>  blinker_loop_thread;
void setup() {
  Serial.begin(115200L);
  counter_loop_thread.reset(new std::thread(counter_loop));
  blinker_loop_thread.reset(new std::thread(blinker_loop));
}

This effectively splits the declaration and allocation into two distinct steps using C++ notation. It can also be done as:

std::thread *counter_loop_thread;
std::thread *blinker_loop_thread;
void setup() {
  Serial.begin(115200L);
  counter_loop_thread = new std::thread(counter_loop);
  blinker_loop_thread = new std::thread(blinker_loop);
}

You can even go further to use a llamda function in place of counter_loop or blinker_loop if you prefer that style instead.

thank you, that looks fine, especially as to the counters in the blink_loop (AFAICS):

counter_loop: 0
blinker_loop (HIGH) counter: 0
main loop: 1
blinker_loop (LOW) counter: 0
counter_loop: 1
counter_loop: 2
blinker_loop (HIGH) counter: 1
blinker_loop (LOW) counter: 1
counter_loop: 3
counter_loop: 4
blinker_loop (HIGH) counter: 2
blinker_loop (LOW) counter: 2
counter_loop: 5
counter_loop: 6
blinker_loop (HIGH) counter: 3
blinker_loop (LOW) counter: 3
counter_loop: 7
counter_loop: 8
blinker_loop (HIGH) counter: 4
blinker_loop (LOW) counter: 4
counter_loop: 9
counter_loop: 10
blinker_loop (HIGH) counter: 5
main loop: 2
blinker_loop (LOW) counter: 5
counter_loop: 11
counter_loop: 12
blinker_loop (HIGH) counter: 6
blinker_loop (LOW) counter: 6
counter_loop: 13
counter_loop: 14
blinker_loop (HIGH) counter: 7
blinker_loop (LOW) counter: 7
counter_loop: 15
counter_loop: 16
blinker_loop (HIGH) counter: 8
blinker_loop (LOW) counter: 8
counter_loop: 17
counter_loop: 18
blinker_loop (HIGH) counter: 9
blinker_loop (LOW) counter: 9
counter_loop: 19
counter_loop: 20
blinker_loop (HIGH) counter: 10
main loop: 3
blinker_loop (LOW) counter: 10

code:

// std::thread for ESP32, Arduino IDE

#include <Arduino.h>
#include <thread>
#include <chrono>

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

const auto one_sec = std::chrono::seconds
{
    1
};

void counter_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        Serial.print("counter_loop: ");
        Serial.println(counter);
        std::this_thread::sleep_for(one_sec);
        counter++;
    }
}

void blinker_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.print("blinker_loop (HIGH) counter: ");
        Serial.println(counter);
        std::this_thread::sleep_for(one_sec);

        digitalWrite(LED_BUILTIN, LOW);
        Serial.print("blinker_loop (LOW) counter: ");
        Serial.println(counter);
        std::this_thread::sleep_for(one_sec);
        counter++;
    }
}


std::unique_ptr<std::thread>  counter_loop_thread;
std::unique_ptr<std::thread>  blinker_loop_thread;

void setup() {
  Serial.begin(115200);
  //debug
  delay(2000);

  counter_loop_thread.reset(new std::thread(counter_loop));
  blinker_loop_thread.reset(new std::thread(blinker_loop));
}


uint32_t main_loop_counter = 0;
void loop() {
    main_loop_counter++;
    Serial.print("main loop: ");
    Serial.println(main_loop_counter);
    delay(10000);
}

a question to understanding:
can
std::this_thread::sleep_for(one_sec); // 1 second
always be exchanged by
delay(1000); // 1000 milliseconds
:?:

Possibly, it would depend on the implementation of the std::thread bindings provided by ESP-IDF. I haven't looked at them specifically but if you are using std::thread I'd suggest stick with just it's declared model and not mix in Arduino APIs where there is already an API provided by std::thread.

ok, I'll try out...

1st, your alternative code from above also works like the former one:

std::thread *counter_loop_thread;
std::thread *blinker_loop_thread;


void setup() {
  Serial.begin(115200);
  //debug
  delay(2000);
  counter_loop_thread = new std::thread(counter_loop);
  blinker_loop_thread = new std::thread(blinker_loop);
}

counter_loop: 0
blinker_loop (HIGH) counter: 0
main loop: 1
blinker_loop (LOW) counter: 0
counter_loop: 1
counter_loop: 2
blinker_loop (HIGH) counter: 1
blinker_loop (LOW) counter: 1
counter_loop: 3
counter_loop: 4
blinker_loop (HIGH) counter: 2
blinker_loop (LOW) counter: 2
counter_loop: 5
counter_loop: 6
blinker_loop (HIGH) counter: 3
blinker_loop (LOW) counter: 3
counter_loop: 7
counter_loop: 8
blinker_loop (HIGH) counter: 4
blinker_loop (LOW) counter: 4
counter_loop: 9
counter_loop: 10
blinker_loop (HIGH) counter: 5
main loop: 2
blinker_loop (LOW) counter: 5
counter_loop: 11
counter_loop: 12
blinker_loop (HIGH) counter: 6
blinker_loop (LOW) counter: 6
counter_loop: 13
counter_loop: 14
blinker_loop (HIGH) counter: 7
blinker_loop (LOW) counter: 7
counter_loop: 15
counter_loop: 16
blinker_loop (HIGH) counter: 8
blinker_loop (LOW) counter: 8
counter_loop: 17
counter_loop: 18
blinker_loop (HIGH) counter: 9
blinker_loop (LOW) counter: 9
counter_loop: 19
counter_loop: 20
blinker_loop (HIGH) counter: 10
main loop: 3
blinker_loop (LOW) counter: 10

... to be continued...

ok, I'll try out...

1st, your alternative code from above also works like the former one:

Yes, they are functionally equivalent in this very simple code example. However, this is not always the case, there are also a lot of advantages to std::unique_ptr over a raw pointer declaration. But those are unrelated to the esp32.

ok, so in case it's equivalent at runtime then this 2nd version appears to be more handy and more intuitively understandable to "common Arduino users"


now 2nd, exchanging the std sleeps by delay(1000) seem to work as well, AFAICS:

counter_loop: 0
blinker_loop (HIGH) counter: 0
main loop: 1
blinker_loop (LOW) counter: 0
counter_loop: 1
counter_loop: 2
blinker_loop (HIGH) counter: 1
blinker_loop (LOW) counter: 1
counter_loop: 3
counter_loop: 4
blinker_loop (HIGH) counter: 2
blinker_loop (LOW) counter: 2
counter_loop: 5
counter_loop: 6
blinker_loop (HIGH) counter: 3
blinker_loop (LOW) counter: 3
counter_loop: 7
counter_loop: 8
blinker_loop (HIGH) counter: 4
blinker_loop (LOW) counter: 4
counter_loop: 9
counter_loop: 10
blinker_loop (HIGH) counter: 5
main loop: 2
blinker_loop (LOW) counter: 5
counter_loop: 11
counter_loop: 12
blinker_loop (HIGH) counter: 6
blinker_loop (LOW) counter: 6
counter_loop: 13
counter_loop: 14
blinker_loop (HIGH) counter: 7
blinker_loop (LOW) counter: 7
counter_loop: 15
counter_loop: 16
blinker_loop (HIGH) counter: 8
blinker_loop (LOW) counter: 8
counter_loop: 17
counter_loop: 18
blinker_loop (HIGH) counter: 9
blinker_loop (LOW) counter: 9
counter_loop: 19
counter_loop: 20
blinker_loop (HIGH) counter: 10
main loop: 3
blinker_loop (LOW) counter: 10

// std::thread for ESP32, Arduino IDE

#include <Arduino.h>
#include <thread>
#include <chrono>

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif



void counter_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        Serial.print("counter_loop: ");
        Serial.println(counter);
        delay(1000);  
        counter++;
    }
}

void blinker_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.print("blinker_loop (HIGH) counter: ");
        Serial.println(counter);
        delay(1000);  

        digitalWrite(LED_BUILTIN, LOW);
        Serial.print("blinker_loop (LOW) counter: ");
        Serial.println(counter);

        delay(1000);  
        counter++;
    }
}


std::thread *counter_loop_thread;
std::thread *blinker_loop_thread;


void setup() {
  Serial.begin(115200);
  //debug
  delay(2000);
  counter_loop_thread = new std::thread(counter_loop);
  blinker_loop_thread = new std::thread(blinker_loop);
}


uint32_t main_loop_counter = 0;
void loop() {
    main_loop_counter++;
    Serial.print("main loop: ");
    Serial.println(main_loop_counter);
    delay(10000);
}
  • but IMO, for a public usage that delay() behaviour should preferably be tested by specialists upon thread-safety and race conditions though, what would you think?

ok, so in case it's equivalent at runtime then this 2nd version appears to be more handy and more intuitively understandable to "common Arduino users"

Maybe, but it is unlikely what will be accepted as part of a demo. There are a number of usages of C++ style pointer templates already in use.

  • but IMO, for a public usage that delay() behaviour should preferably be tested by specialists upon thread-safety and race conditions though, what would you think?

It may work this time but there is absolutely no guarantee on safety over the long term. As mentioned above, mixing the Arduino API where a C++ API exists for that purpose should be avoided to ensure everything works as expected under all cases.

ok, I see,
gladly looking forward to a publication as an Arduino ESP32 std::thread example!
thanks a lot for your contributions, and I'd be glad if I could help testing that.

slight refinement:
to make it didactical more clear which one a thread name is vs. a function name, and that both names don't depend on each other, I changed the thread names like following:

std::thread *thread_1;
std::thread *thread_2;

void setup() {
  Serial.begin(115200);
  //debug
  delay(2000);
  thread_1 = new std::thread(counter_loop);
  thread_2 = new std::thread(blinker_loop);
}

HTH!

PS,
Arduino.cc people are extremely restrained as to further documentation to delay() in MT environments, (IIUC) mostly because MT is not very much supported at all yet.
OTOH, both delay() and millis() and microseconds() are the most common and basic Arduino API functions at all, widely used in almost either ino code, and so it would be highly appreciated if esp32-Arduino-APIs ensure to be (downwards) compatible to these Arduino basics.

just observed a new issue about this std::thread behaviour:

after changing the ode of 1 thread (->fibonacci), the main loop counter is no longer printed on Serial:
(both for
delay(5000);
and for
std::this_thread::sleep_for(5*one_sec);
)

// std::thread for ESP32, Arduino IDE
// ver 0.0.5 fibonacci

#include <Arduino.h>
#include <thread>

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

const auto one_sec = std::chrono::seconds
{
    1
};




int fibbonacci(int n) {
   if(n == 0){
      return 0;
   } else if(n == 1) {
      return 1;
   } else {
      return (fibbonacci(n-1) + fibbonacci(n-2));
   }
}



void blinker_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.println((String)"blinker_loop (HIGH) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        digitalWrite(LED_BUILTIN, LOW);
        Serial.println((String)"blinker_loop (LOW) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        counter++;
    }
}


void fibonacci_loop() {
    thread_local uint32_t counter = 0, i=0;
    while(true) {
        for(i=30; i<41; i++) {    // limits: test, debug
          Serial.println( (String)"Fibbonacci of "+i+"="+fibbonacci(i));            
        }        
        Serial.println((String)"fibonacci_loop counter: "+counter);  
        counter++;      
    }
}



std::thread *thread_1;
std::thread *thread_2;


void setup() {
  Serial.begin(115200);
  //debug
  delay(1000);
  thread_1 = new std::thread(blinker_loop);
  thread_2 = new std::thread(fibonacci_loop);
}


uint32_t main_loop_counter = 0;
void loop() {
    main_loop_counter++;
    Serial.println((String)"main loop: " + main_loop_counter);
    //std::this_thread::sleep_for(5*one_sec);
    delay(5000);
}

output:

blinker_loop (HIGH) counter: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
blinker_loop (LOW) counter: 2
blinker_loop (HIGH) counter: 3
Fibbonacci of 36=14930352
blinker_loop (LOW) counter: 3
blinker_loop (HIGH) counter: 4
blinker_loop (LOW) counter: 4
blinker_loop (HIGH) counter: 5
Fibbonacci of 37=24157817
blinker_loop (LOW) counter: 5
blinker_loop (HIGH) counter: 6
blinker_loop (LOW) counter: 6
blinker_loop (HIGH) counter: 7
blinker_loop (LOW) counter: 7
blinker_loop (HIGH) counter: 8
blinker_loop (LOW) counter: 8
Fibbonacci of 38=39088169
blinker_loop (HIGH) counter: 9
blinker_loop (LOW) counter: 9
blinker_loop (HIGH) counter: 10
blinker_loop (LOW) counter: 10
blinker_loop (HIGH) counter: 11
blinker_loop (LOW) counter: 11
blinker_loop (HIGH) counter: 12
blinker_loop (LOW) counter: 12
blinker_loop (HIGH) counter: 13
blinker_loop (LOW) counter: 13
blinker_loop (HIGH) counter: 14
blinker_loop (LOW) counter: 14
Fibbonacci of 39=63245986
blinker_loop (HIGH) counter: 15
blinker_loop (LOW) counter: 15
blinker_loop (HIGH) counter: 16
blinker_loop (LOW) counter: 16
blinker_loop (HIGH) counter: 17
blinker_loop (LOW) counter: 17
blinker_loop (HIGH) counter: 18
blinker_loop (LOW) counter: 18
blinker_loop (HIGH) counter: 19
blinker_loop (LOW) counter: 19
blinker_loop (HIGH) counter: 20
blinker_loop (LOW) counter: 20
blinker_loop (HIGH) counter: 21
blinker_loop (LOW) counter: 21
blinker_loop (HIGH) counter: 22
blinker_loop (LOW) counter: 22
blinker_loop (HIGH) counter: 23
blinker_loop (LOW) counter: 23
Fibbonacci of 40=102334155
fibonacci_loop counter: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
blinker_loop (HIGH) counter: 24
Fibbonacci of 32=2178309
Fibbonacci of 33=3524578
blinker_loop (LOW) counter: 24
Fibbonacci of 34=5702887
blinker_loop (HIGH) counter: 25
blinker_loop (LOW) counter: 25
Fibbonacci of 35=9227465
blinker_loop (HIGH) counter: 26
blinker_loop (LOW) counter: 26
Fibbonacci of 36=14930352
blinker_loop (HIGH) counter: 27
blinker_loop (LOW) counter: 27
blinker_loop (HIGH) counter: 28
blinker_loop (LOW) counter: 28
blinker_loop (HIGH) counter: 29
Fibbonacci of 37=24157817
blinker_loop (LOW) counter: 29
blinker_loop (HIGH) counter: 30
blinker_loop (LOW) counter: 30

just observed a new issue about this std::thread behaviour:

after changing the ode of 1 thread (->fibonacci), the main loop counter is no longer printed on Serial:
(both for
delay(5000);
and for
std::this_thread::sleep_for(5*one_sec);
)

This could be due to the default configuration of threads created via std::thread, the default priority is 5 which is higher than the loop() method runs at. So if you do not have a delay/yield the FreeRTOS task scheduler will not switch to other tasks. I suspect that the std::this_thread::delay_for() may not be using the FreeRTOS vTaskDelay API, but the Arduino API delay/delayMicroseconds does use this internally.

You can see the default configuration for std::thread here: https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/sdkconfig#L743-L744

You can adjust this before creating the std::thread instance by using methods available in https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/pthread/esp_pthread.h (esp_pthread_set_cfg).

thank you,
but although I see

typedef struct {
    size_t stack_size;    ///< the stack size of the pthread
    size_t prio;          ///< the thread's priority
    bool inherit_cfg;     ///< inherit this configuration further
} esp_pthread_cfg_t;

I'm afraid I don't see how to call that anywhere for which special prio and thread adjustement in my ino program regrettably.

something like this possibly:

 esp_pthread_cfg_t cfg;
 esp_pthread_get_cfg(&cfg);
 cfg.prio=1;
 esp_pthread_set_cfg(&cfg);
 thread_1 = new std::thread(blinker_loop);

error:
exit status 1
'esp_pthread_cfg_t' was not declared in this scope

void setup() {
  Serial.begin(115200);
  //debug
  delay(1000);

  esp_pthread_cfg_t cfg;
  esp_pthread_get_cfg(&cfg);
  cfg.prio=3;
  esp_pthread_set_cfg(&cfg);

  thread_1 = new std::thread(blinker_loop);
  thread_2 = new std::thread(fibonacci_loop);
}

You didn't include the required header that defines the function or the data type. Also priority 3 is higher than loop() runs at (which is 1)

ok, I see, thx....
unfortunately the main loop prints are still not visible, I'm afraid

// std::thread for ESP32, Arduino IDE

// ver 0.0.5 fibonacci

#include <Arduino.h>
#include <thread>
#include <esp_pthread.h>


#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

const auto one_sec = std::chrono::seconds
{
    1
};




int fibbonacci(int n) {
   if(n == 0){
      return 0;
   } else if(n == 1) {
      return 1;
   } else {
      return (fibbonacci(n-1) + fibbonacci(n-2));
   }
}



void blinker_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.println((String)"blinker_loop (HIGH) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        digitalWrite(LED_BUILTIN, LOW);
        Serial.println((String)"blinker_loop (LOW) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        counter++;
    }
}


void fibonacci_loop() {
    thread_local uint32_t counter = 0, i=0;
    while(true) {
        for(i=30; i<41; i++) {    // limits: test, debug
          Serial.println( (String)"Fibbonacci of "+i+"="+fibbonacci(i));            
        }        
        Serial.println((String)"fibonacci_loop counter: "+counter);  
        counter++;      
    }
}



std::thread *thread_1;
std::thread *thread_2;


void setup() {
  Serial.begin(115200);
  //debug
  delay(1000);

  esp_pthread_cfg_t cfg;
  esp_pthread_get_cfg(&cfg);
  cfg.prio=1;
  esp_pthread_set_cfg(&cfg);

  thread_1 = new std::thread(blinker_loop);
  thread_2 = new std::thread(fibonacci_loop);
}


uint32_t main_loop_counter = 0;
void loop() {
    main_loop_counter++;
    Serial.println((String)"main loop: " + main_loop_counter);
    //std::this_thread::sleep_for(5*one_sec);
    delay(5000);
}

blinker_loop (HIGH) counter: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
blinker_loop (LOW) counter: 2
blinker_loop (HIGH) counter: 3
Fibbonacci of 36=14930352
blinker_loop (LOW) counter: 3
blinker_loop (HIGH) counter: 4
blinker_loop (LOW) counter: 4
blinker_loop (HIGH) counter: 5
Fibbonacci of 37=24157817
blinker_loop (LOW) counter: 5
blinker_loop (HIGH) counter: 6
blinker_loop (LOW) counter: 6
blinker_loop (HIGH) counter: 7
blinker_loop (LOW) counter: 7
blinker_loop (HIGH) counter: 8
blinker_loop (LOW) counter: 8
Fibbonacci of 38=39088169
blinker_loop (HIGH) counter: 9
blinker_loop (LOW) counter: 9
blinker_loop (HIGH) counter: 10
blinker_loop (LOW) counter: 10
blinker_loop (HIGH) counter: 11
blinker_loop (LOW) counter: 11
blinker_loop (HIGH) counter: 12
blinker_loop (LOW) counter: 12
blinker_loop (HIGH) counter: 13
blinker_loop (LOW) counter: 13
blinker_loop (HIGH) counter: 14
blinker_loop (LOW) counter: 14
Fibbonacci of 39=63245986
blinker_loop (HIGH) counter: 15
blinker_loop (LOW) counter: 15
blinker_loop (HIGH) counter: 16
blinker_loop (LOW) counter: 16
blinker_loop (HIGH) counter: 17
blinker_loop (LOW) counter: 17
blinker_loop (HIGH) counter: 18
blinker_loop (LOW) counter: 18
blinker_loop (HIGH) counter: 19
blinker_loop (LOW) counter: 19
blinker_loop (HIGH) counter: 20
blinker_loop (LOW) counter: 20
blinker_loop (HIGH) counter: 21
blinker_loop (LOW) counter: 21
blinker_loop (HIGH) counter: 22
blinker_loop (LOW) counter: 22
blinker_loop (HIGH) counter: 23
blinker_loop (LOW) counter: 23
Fibbonacci of 40=102334155
fibonacci_loop counter: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
blinker_loop (HIGH) counter: 24
Fibbonacci of 32=2178309
Fibbonacci of 33=3524578
blinker_loop (LOW) counter: 24
Fibbonacci of 34=5702887
blinker_loop (HIGH) counter: 25
blinker_loop (LOW) counter: 25
Fibbonacci of 35=9227465
blinker_loop (HIGH) counter: 26
blinker_loop (LOW) counter: 26
Fibbonacci of 36=14930352

without some sort of delay in the fibbonacci loop you won't see any output from loop() as it doesn't yield to the other tasks that need to run as well.

I acually chose exactly that setup without any delay in 1 thread to test for preemptivity.
If delays are needed to switch the scheduler timeslice, then it's just cooperative, I'm afraid...

But is it perhaps an option to run main loop at prio 4 by default and choose prio <4 for std::threads?

But is it perhaps an option to run main loop at prio 4 by default and choose prio <4 for std::threads?

Unless you switch to using arduino-esp32 as an IDF component you will have no control over the priority of the loopTask.

I acually chose exactly that setup without any delay in 1 thread to test for preemptivity.
If delays are needed to switch the scheduler timeslice, then it's just cooperative, I'm afraid...

This is scheduler dependent and there are no guarantees that it will switch tasks as you might hope/expect. You will need to test and confirm that it behaves as you expect.

Unless you switch to using arduino-esp32 as an IDF component you will have no control over the priority of the loopTask.

But I tried that, lowering the threads to min prio 1.
Having done that already it is not reasonable that a thread still blocks main loop() forcibly absolutely completely.

Just to make things clear for someone dropping in on this conversation, std::thread is NOT the recommended method for creating tasks in esp32. FreeRTOS is available which provides full facilities for tasks, events, mutexes and semaphores.

well, this topic is about implementing C++ std::thread accordingly,
and C++ <thread> is supposed to be a valid and approved standard library, also providing events, mutexes, semaphores, and more .

so just to return to topic,
having lowered the threads to min prio 1, it is not reasonable that a thread still blocks main loop() forcibly absolutely completely.
And just to say, C++ std::thread is not cooperative but preemptive (what is crucial), like POSIX pthread.

And just to say, C++ std::thread is not cooperative but preemptive (what is crucial), like POSIX pthread.

That would be because std::thread is based on pthread in virtually every implementation.

yes, I appreciate that, I always have been fine with pthread on my Raspi, it runs like a charm.

@dsyleixa 1>0, 2>1 If a thread of higher priority is ready, it gets time. lower priority threads or equal priority will stall UNTIL ALL higher priority Thread are satisfied. If you have three threads ready at priority 3, ONLY two of those thread will execute because ESP32 has two cores period. UNTIL one of the executing priority 3 threads stall the third priority 3 thread does not get any time. There is no fairness/round robin scheduler. Pure priority. When you code threads you take the responsibility to share resources. A good thread executes a short task, releases and lets others execute. A continuous task needs to exist in loop(). Tasks should be considered background operations

Chuck.

I know about priorities and sleep/delays from my pthreads on my Raspi Linux.
As to esp32, it would be fine and reasonable to have e.g. the following thread environment setups:

a) either all threads (4,5,6...) incl. main loop() running at the same priority (e.g., 3),
and then the scheduler is expected to split the time slice resources equally (even if there are no delays at all in either thread)

b) or have 1 thread without sleep/delays run at prio 2 and 4 threads running at prio 3 providing internal sleeps/delays:
the scheduler is expected to split the time slice resources between the 4 prio-3-threads equally and in between running the prio-2-thread during avalible intermediate sleep periods

c) of course, having 4 prio-3-threads without sleep/delays and 1 extra prio 2 thread, then the prio 2 thread would have no chance.

All that is supposed to run that way not only on a 4core Raspi but also on a single core Raspi A or B or Zero, no multiple cores needed, and also the same way on an ESP32.

Anyway, but the crucial issue is:
Currently the loop in main has no chance at all, that is not reasonable, as all std::threads have just prio 1. So the main loop prio has to become increased to a higher thread level.

admittedly I do not understand your statement about 2core ESP32, finally preemptive multithreading is not multitasking on different cores: one could have dozens of threads running on 1 core by preemptive timeslice scheduling (and equal prios, no sleeps in between).

That would run also on a single Raspi A or B or Zero, no multiple cores needed.

rPi runs Linux so it has a preemptive kernel. The ESP32 does not run Linux so it is not a comparable platform, regardless of number of cores.

But currently the loop in main has no chance at all, that is not reasonable, as all std::threads have just prio 1. So the main loop prio has to become increased to a higher std:thread level.

As stated previously, unless you use arduino-esp32 as an IDF component you have no control over the priority of the loop task. However, you have full control over the priorities for std::thread IF you call esp_pthread_set_cfg prior to creating the std::thread instance, the std::thread specification does not cover stack size or priority so it is left to the implementation to decide how to declare these.

a) either all threads (4,5,6...) incl. main loop() running at the same priority (e.g., 3),
and then the scheduler is expected to split the time slice resources equally (even if there are no delays at all in either thread)

The FreeRTOS task scheduler is not a preemptive scheduler, it is a co-operative scheduler.

b) or have 1 thread without sleep/delays run at prio 2 and 4 threads running at prio 3 providing internal sleeps/delays:
the scheduler is expected to split the time slice resources between the 4 prio-3-threads equally and in between running the prio-2-thread during avalible intermediate sleep periods

The FreeRTOS scheduler will rotate between the highest priority tasks that are ready to run, when the highest priority tasks are not ready to run it will look at lower priority tasks etc. Tasks can also be bound to run only on one core (loopTask always runs on core 1) so it is possible that tasks that could run will be skipped as their "bound core" is actively running something else.

what s a IDF component? I'm running an Arduino program in the Arduino IDE with Arduino API functions, and this topic is about preemptive std::thread in this environment.

The FreeRTOS task scheduler is not a preemptive scheduler, it is a co-operative scheduler.

If RTOS is just cooperative, then it's the wrong way to use it:
std.:threads needs a preemptive scheduler.

However, you have full control over the priorities for std::thread IF you call esp_pthread_set_cfg prior to creating the std::thread instance, the std::thread specification does not cover stack size or priority so it is left to the implementation to decide how to declare these.

I did that already, I decreased the thread prios to 1, nonetheless main loop() is completely, absolutely blocked.

If RTOS is just cooperative, then it's the wrong way to use it:
std.:threads needs a preemptive scheduler.

And this is why it is not the recommended approach. FreeRTOS is used by ESP-IDF which everything else is built upon, including the pthread wrappers.

what s a IDF component? I'm running an Arduino program in the Arduino IDE with Arduino API functions, and this topic is about preemptive std::thread in this environment.

ESP-IDF is the underlying stack for the arduino-esp32 code, it does NOT run natively inside the Arduino IDE. It requires a different build environment that can be a lot more involved for Arduino IDE users typically will use.

so IIUC, the preemptive scheduler is broken or defective or incomplete for Arduino?
Then the Arduino implementation is faulty and thus has to be changed to the full IDF thing.
preemptive MT is crucial for std::thread, either which extra libs it will demand, it has to be fully integrated to the Arduino IDE.

but after all, my 2 threads in my fibonacci example appear to run simultaneously, even if 1 thread provides no sleeps in between. Just main loop is blocked, not the 2nd thread. Just the main loop thing seems to be defective FTM (not tried 4 or 5 threads w/o delays yet though)

so IIUC, the preemptive scheduler is broken or defective or incomplete for Arduino?
Then the Arduino implementation is faulty and thus has to be changed to the full IDF thing.
preemptive MT is crucial for std::thread, either which extra libs it will demand, it has to be fully integrated to the Arduino IDE.

arduino-esp32 does not provide ANY part of the scheduler, pthread, std::thread, etc. All of that is coming from ESP-IDF. Feel free to open a bug against the ESP-IDF project for their consideration at implementing what you feel is missing there.

so what has to be changed in ESP-IDF exactly to make it run also in arduino-esp32?

so what has to be changed in ESP-IDF exactly to make it run also in arduino-esp32?

That would be up to the ESP-IDF devs, you can take your expectations up with them.

ok, thanks, I assumed that all that about ESP32 is just 1 common project.

Hi @atanisoft,

The FreeRTOS task scheduler is not a preemptive scheduler, it is a co-operative scheduler.

Are you sure about that ? (See: https://www.freertos.org/RTOS.html)

As I remember (long time ago), the precompiled SDK has the freeRTOS component that work using 1ms timeslice (1Khz clock) and the task switching that occur every clock tick (with context saving and so on....), so, it is preemptive.
Of course, you can also use yield and delay functions to switch task cooperatively, but the task switch occur anyway at every interrupt tick (and the control could return to the task itself if there are not same or higher priority tasks that need cpu).

For @dsyleixa, if you need a preemptive solution against the main loop, you need to create the additive task with priority "1", the same used by the Arduino framework.
On another side, the Arduino FW is pinned to the CPU core n.1 (APP_CPU) and cannot span to all cores, so, your created task can supersede Arduino FW even if the priority is the same (there are particular conditions that allow that). It's better to create your task pinned to the core 1 too.

I don't know how do it via std::thread, but you could use xTaskCreatePinnedToCore function instead (see ESP-IDF doc).

Regards.

As I remember (long time ago), the precompiled SDK has the freeRTOS component that work using 1ms timeslice (1Khz clock) and the task switching that occur every clock tick (with context saving and so on....), so, it is preemptive.

You could be right, but it doesn't get a chance to switch tasks if there is no pause point for the scheduler task itself to run. It very well could be a bug in the implementation for multi-core.

Hi,
the scheduler is attached to an hardware timer interrupt. The interrupt shot occur anyway, regardless the current task job, and interrupt handler perform the forced context saving of the actually active task.
The interrupt handler also set the stack pointer to point the stack of a another waiting task and, on the exit from the interrupt handler, the context of the waiting task will be automatically restored causing the resume of that task.
This is the task switching as performed by freeRTOS.

.... This is also the reason because the handler of an higher priority interrupt (interrupt priority, not task priority) cannot interact with the scheduler, even using xxxxFromIsr APIs.
Regards.

igrr suggested the following
- unfortunately it does not compile yet for me, but I'm hoping to get that fixed perhaps, too:

esp_pthread_cfg_t cfg;
  if (esp_pthread_get_cfg(&cfg) != ESP_OK) {
      cfg = esp_pthread_get_default_config();
  }
  cfg.prio=1;
  if (esp_pthread_set_cfg(&cfg) != ESP_OK) {
      printf("esp_pthread_set_cfg failed\n");
      abort();
  };

exit status 1
'esp_pthread_get_default_config' was not declared in this scope
https://github.com/espressif/esp-idf/issues/3544#issuecomment-496157019

perhaps the esp32-IDF version used by https://github.com/espressif/arduino-esp32 needs to be updated to a later version?
https://github.com/espressif/esp-idf/issues/3544#issuecomment-496913029

perhaps the esp32-IDF version used by https://github.com/espressif/arduino-esp32 needs to be updated to a later version?
espressif/esp-idf#3544 (comment)

It is intentionally on the v3.2 branch right now as that is the last "stable" release of ESP-IDF. Until there is a new "stable" it will remain on v3.2.

ok, but std::thread prios cannot arbitrarily "stably" be initialized on 3.2 :-/

ok, but std::thread prios cannot arbitrarily "stably" be initialized on 3.2 :-/

It can be but you can't get the default config in the manner that was proposed in your referenced issue. Instead you would need to initialize the structure fully in your code and call the set method prior to creating the std::thread instance.

yes, that is still crucial as there is no documentation/example code yet available abot how to do that exactly :-/

@atanisoft : could you please tell me how to solve that?

@atanisoft : could you please tell me how to solve that?

This is 100% untested but should work:

#include <esp_pthread.h>

std::thread *counter_loop_thread;

void setup() {
  esp_pthread_cfg_t cfg;
  esp_pthread_get_cfg(&cfg);
  cfg.prio = 1;
  esp_pthread_set_cfg(&cfg);
  counter_loop_thread = new std::thread(counter_loop);
}

This is based on previous examples in this issue, this should give you a start towards a complete example.

unfortunately not:

blinker_loop (HIGH) counter: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
E (12195) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12195) task_wdt:  - IDLE0 (CPU 0)
E (12195) task_wdt: Tasks currently running:
E (12195) task_wdt: CPU 0: pthread
E (12195) task_wdt: CPU 1: loopTask
E (12195) task_wdt: Aborting.
abort() was called at PC 0x400d63a3 on core 0

Backtrace: 0x40089150:0x3ffbe160 0x4008937d:0x3ffbe180 0x400d63a3:0x3ffbe1a0 0x40081671:0x3ffbe1c0 0x400ea37e:0x3ffb89e0 0x400ea379:0x3ffb8a00 0x400ea379:0x3ffb8a20 0x400ea379:0x3ffb8a40 0x400ea379:0x3ffb8a60 0x400ea379:0x3ffb8a80 0x400ea379:0x3ffb8aa0 0x400ea379:0x3ffb8ac0 0x400ea379:0x3ffb8ae0 0x400ea379:0x3ffb8b00 0x400ea379:0x3ffb8b20 0x400ea379:0x3ffb8b40 0x400ea379:0x3ffb8b60 0x400d0e0f:0x3ffb8b80 0x400ea365:0x3ffb8bc0 0x400e6ced:0x3ffb8be0 0x400e5db8:0x3ffb8c10 0x40087c9d:0x3ffb8c30

Rebooting...
ets Jun  8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
blinker_loop (HIGH) counter: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
E (12195) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12195) task_wdt:  - IDLE0 (CPU 0)
E (12195) task_wdt: Tasks currently running:
E (12195) task_wdt: CPU 0: pthread
E (12195) task_wdt: CPU 1: loopTask
E (12195) task_wdt: Aborting.
abort() was called at PC 0x400d63a3 on core 0

Backtrace: 0x40089150:0x3ffbe160 0x4008937d:0x3ffbe180 0x400d63a3:0x3ffbe1a0 0x40081671:0x3ffbe1c0 0x400ea37e:0x3ffb89e0 0x400ea379:0x3ffb8a00 0x400ea379:0x3ffb8a20 0x400ea379:0x3ffb8a40 0x400ea379:0x3ffb8a60 0x400ea379:0x3ffb8a80 0x400ea379:0x3ffb8aa0 0x400ea379:0x3ffb8ac0 0x400ea379:0x3ffb8ae0 0x400ea379:0x3ffb8b00 0x400ea379:0x3ffb8b20 0x400ea379:0x3ffb8b40 0x400ea379:0x3ffb8b60 0x400d0e0f:0x3ffb8b80 0x400ea365:0x3ffb8bc0 0x400e6ced:0x3ffb8be0 0x400e5db8:0x3ffb8c10 0x40087c9d:0x3ffb8c30

Rebooting...
ets Jun  8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
blinker_loop (HIGH) counter: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
E (12195) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12195) task_wdt:  - IDLE0 (CPU 0)
E (12195) task_wdt: Tasks currently running:
E (12195) task_wdt: CPU 0: pthread
E (12195) task_wdt: CPU 1: loopTask
E (12195) task_wdt: Aborting.
abort() was called at PC 0x400d63a3 on core 0

Backtrace: 0x40089150:0x3ffbe160 0x4008937d:0x3ffbe180 0x400d63a3:0x3ffbe1a0 0x40081671:0x3ffbe1c0 0x400ea37e:0x3ffb89e0 0x400ea379:0x3ffb8a00 0x400ea379:0x3ffb8a20 0x400ea379:0x3ffb8a40 0x400ea379:0x3ffb8a60 0x400ea379:0x3ffb8a80 0x400ea379:0x3ffb8aa0 0x400ea379:0x3ffb8ac0 0x400ea379:0x3ffb8ae0 0x400ea379:0x3ffb8b00 0x400ea379:0x3ffb8b20 0x400ea379:0x3ffb8b40 0x400ea379:0x3ffb8b60 0x400d0e0f:0x3ffb8b80 0x400ea365:0x3ffb8bc0 0x400e6ced:0x3ffb8be0 0x400e5db8:0x3ffb8c10 0x40087c9d:0x3ffb8c30

Rebooting...
ets Jun  8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
blinker_loop (HIGH) counter: 0
Fibbonacci of 30=832040

Actually, that is working EXACTLY as designed. One of your threads (listed as pthread in the output above, which is likely the default name for std::thread) is not resetting the watchdog and is not delaying/yielding to other tasks so the WDT kicks in and resets.

Either you add a small delay (1-2uS) in your std::thread code OR you feed the WDT so it doesn't trigger a restart.

@dsyleixa If your pthread task does not delay() or yield() WDT will expire. It is working as Designed.
Follow the recommendation of @atanisoft.

Chuck.

I can't use delays or yields because it may happen that either task may coincidentally stall at any time (!) and then will hang up the whole system (real code for a mobile robot, not this test!).
That is why the code MUST work without any delays!
That's exactly why a preemptive MT is needed compellingly!
(pthread for my Raspi never hangs up if 1 of the Threads is stalling, and that's how a preemptive MT is designed to work!)

I have no idea though how to feed a watchdog, especially in a stalling thread :-/

Bad design. If your design is that unstable, it will never function in the real world. Your foreground task will be interrupted and delayed by higher priority tasks and interrupts. So if you expect to be able to calculate exact duration of execution you are going to have to design and build your own OS/environment. Something without any interrupt support. Arduino-Esp32 exists in an environment, that environment has to co-exist with real hardware, Input signals by definition are generated externally. You can respond, but you cannot control the timing.

Chuck.

it will work if pthread worked reliably, on my Raspi it does, and here pthread works perfectly preemptive that a system hang-up won't happen if just 1 thread is stalling (tested).
E.g., an issue might be a stalling bus system (i2c).

I have no idea though how to feed a watchdog, especially in a stalling thread :-/

add a call to feedLoopWDT() which will reset the current task's WDT timeout. This is typically used for the loop task but it isn't required to only be used for that task.

it will work if pthread worked reliably, on my Raspi it does, and here pthread works perfectly preemptive

You can not compare a Linux OS vs the ESP32, there is no valid comparison between the two.

where should I add that
feedLoopWDT()
additionally at which code line(s)?

I tried this:

// std::thread for ESP32, Arduino IDE

// ver 0.0.5 fibonacci

#include <Arduino.h>
#include <thread>
#include <esp_pthread.h>

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

const auto one_sec = std::chrono::seconds
{
    1
};



int fibbonacci(int n) {
   if(n == 0){
      return 0;
   } else if(n == 1) {
      return 1;
   } else {
      return (fibbonacci(n-1) + fibbonacci(n-2));
   }
}



void fibonacci_loop() {
    thread_local uint32_t counter = 0, i=0;
    while(true) {
        feedLoopWDT();
        Serial.println((String)"fibonacci_loop counter: "+counter);  
        for(i=30; i<41; i++) {    // limits: test, debug
          Serial.println( (String)"Fibbonacci of "+i+"="+fibbonacci(i));            
        }                
        counter++;      
    }
}



void blinker_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        feedLoopWDT();
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.println((String)"blinker_loop (HIGH) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        digitalWrite(LED_BUILTIN, LOW);
        Serial.println((String)"blinker_loop (LOW) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        counter++;
    }
}



std::thread *thread_1;
std::thread *thread_2;

void setup() {
  Serial.begin(115200);
  //debug
  delay(1000);

  esp_pthread_cfg_t cfg;
  esp_pthread_get_cfg(&cfg);
  cfg.prio = 1;
  esp_pthread_set_cfg(&cfg);

  thread_2 = new std::thread(fibonacci_loop);  
  thread_1 = new std::thread(blinker_loop);


  feedLoopWDT();
}



void loop() {
    static uint32_t main_loop_counter = 0;
    feedLoopWDT();
    Serial.println((String)"main loop: " + main_loop_counter);
    delay(5000);
    main_loop_counter++;
}

result:

fibonacci_loop counter: 0
blinker_loop (HIGH) counter: 0
main loop: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
E (12194) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12194) task_wdt:  - IDLE0 (CPU 0)
E (12194) task_wdt: Tasks currently running:
E (12194) task_wdt: CPU 0: pthread
E (12194) task_wdt: CPU 1: IDLE1
E (12194) task_wdt: Aborting.
abort() was called at PC 0x400d6437 on core 0

Backtrace: 0x40089150:0x3ffbe160 0x4008937d:0x3ffbe180 0x400d6437:0x3ffbe1a0 0x40081671:0x3ffbe1c0 0x400ea415:0x3ffb8980 0x400ea40d:0x3ffb89a0 0x400ea40d:0x3ffb89c0 0x400ea40d:0x3ffb89e0 0x400ea40d:0x3ffb8a00 0x400ea40d:0x3ffb8a20 0x400ea40d:0x3ffb8a40 0x400ea40d:0x3ffb8a60 0x400ea40d:0x3ffb8a80 0x400ea40d:0x3ffb8aa0 0x400ea40d:0x3ffb8ac0 0x400ea40d:0x3ffb8ae0 0x400ea40d:0x3ffb8b00 0x400ea40d:0x3ffb8b20 0x400ea40d:0x3ffb8b40 0x400ea40d:0x3ffb8b60 0x400d0e5d:0x3ffb8b80 0x400ea3f9:0x3ffb8bc0 0x400e6d81:0x3ffb8be0 0x400e5e4c:0x3ffb8c10 0x40087c9d:0x3ffb8c30

Rebooting...
ets Jun  8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
fibonacci_loop counter: 0
blinker_loop (HIGH) counter: 0
main loop: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
E (12195) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12195) task_wdt:  - IDLE0 (CPU 0)
E (12195) task_wdt: Tasks currently running:
E (12195) task_wdt: CPU 0: pthread
E (12195) task_wdt: CPU 1: IDLE1
E (12195) task_wdt: Aborting.
abort() was called at PC 0x400d6437 on core 0

Backtrace: 0x40089150:0x3ffbe160 0x4008937d:0x3ffbe180 0x400d6437:0x3ffbe1a0 0x40081671:0x3ffbe1c0 0x400ea415:0x3ffb8980 0x400ea40d:0x3ffb89a0 0x400ea40d:0x3ffb89c0 0x400ea40d:0x3ffb89e0 0x400ea40d:0x3ffb8a00 0x400ea40d:0x3ffb8a20 0x400ea40d:0x3ffb8a40 0x400ea40d:0x3ffb8a60 0x400ea40d:0x3ffb8a80 0x400ea40d:0x3ffb8aa0 0x400ea40d:0x3ffb8ac0 0x400ea40d:0x3ffb8ae0 0x400ea40d:0x3ffb8b00 0x400ea40d:0x3ffb8b20 0x400ea40d:0x3ffb8b40 0x400ea40d:0x3ffb8b60 0x400d0e5d:0x3ffb8b80 0x400ea3f9:0x3ffb8bc0 0x400e6d81:0x3ffb8be0 0x400e5e4c:0x3ffb8c10 0x40087c9d:0x3ffb8c30

Rebooting...
ets Jun  8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
fibonacci_loop counter: 0
blinker_loop (HIGH) counter: 0
main loop: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
E (12195) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12195) task_wdt:  - IDLE0 (CPU 0)
E (12195) task_wdt: Tasks currently running:
E (12195) task_wdt: CPU 0: pthread
E (12195) task_wdt: CPU 1: IDLE1
E (12195) task_wdt: Aborting.
abort() was called at PC 0x400d6437 on core 0

Backtrace: 0x40089150:0x3ffbe160 0x4008937d:0x3ffbe180 0x400d6437:0x3ffbe1a0 0x40081671:0x3ffbe1c0 0x400ea415:0x3ffb8980 0x400ea40d:0x3ffb89a0 0x400ea40d:0x3ffb89c0 0x400ea40d:0x3ffb89e0 0x400ea40d:0x3ffb8a00 0x400ea40d:0x3ffb8a20 0x400ea40d:0x3ffb8a40 0x400ea40d:0x3ffb8a60 0x400ea40d:0x3ffb8a80 0x400ea40d:0x3ffb8aa0 0x400ea40d:0x3ffb8ac0 0x400ea40d:0x3ffb8ae0 0x400ea40d:0x3ffb8b00 0x400ea40d:0x3ffb8b20 0x400ea40d:0x3ffb8b40 0x400ea40d:0x3ffb8b60 0x400d0e5d:0x3ffb8b80 0x400ea3f9:0x3ffb8bc0 0x400e6d81:0x3ffb8be0 0x400e5e4c:0x3ffb8c10 0x40087c9d:0x3ffb8c30

Rebooting...

Just out of curiosity, how long does your recursive fib() take?

void fibonacci_loop() {
    thread_local uint32_t counter = 0, i=0;
    while(true) {
        Serial.printf("(%dms) fibonacci_loop counter: %d\n",millis(),counter);  
        for(i=30; i<41; i++) {    // limits: test, debug
        feedLoopWDT();
          Serial.printf("(%dms) Fibbonacci of %d =%d\n",i,fibbonacci(i));            
        }                
        counter++;      
    }
}

Chuck.

fibonacci(40): about 15 sec
all fibonacci (30...40): about 45 sec

next try:
outsourcing the main loop to an extra thread_3:
thread_3 = new std::thread(former_main_loop);

now again the "(former_)main_loop does not work again, even as it now runs at the same prio as the other threads!
and THAT can actually not be reasonable!

// std::thread for ESP32, Arduino IDE

// ver 0.0.5 fibonacci

#include <Arduino.h>
#include <thread>
#include <esp_pthread.h>

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

const auto one_sec = std::chrono::seconds
{
    1
};



int fibbonacci(int n) {
   if(n == 0){
      return 0;
   } else if(n == 1) {
      return 1;
   } else {
      return (fibbonacci(n-1) + fibbonacci(n-2));
   }
}



void fibonacci_loop() {
    thread_local uint32_t counter = 0, i=0;
    while(true) {
        Serial.println((String)"fibonacci_loop counter: "+counter);  
        for(i=30; i<41; i++) {    // limits: test, debug
          Serial.println( (String)"Fibbonacci of "+i+"="+fibbonacci(i));            
        }                
        counter++;      
    }
}



void blinker_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.println((String)"blinker_loop (HIGH) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        digitalWrite(LED_BUILTIN, LOW);
        Serial.println((String)"blinker_loop (LOW) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        counter++;
    }
}



void former_main_loop() {
    static uint32_t main_loop_counter = 0;
    Serial.println((String)"main loop: " + main_loop_counter);
    delay(5000);
    main_loop_counter++;
}




std::thread *thread_1;
std::thread *thread_2;
std::thread *thread_3;

void setup() {
  Serial.begin(115200);
  //debug
  delay(1000);

  thread_1 = new std::thread(blinker_loop);
  thread_2 = new std::thread(fibonacci_loop);  
  thread_3 = new std::thread(former_main_loop);

}



void loop() {}
blinker_loop (HIGH) counter: 0
fibonacci_loop counter: 0
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
blinker_loop (LOW) counter: 2
blinker_loop (HIGH) counter: 3
Fibbonacci of 36=14930352
blinker_loop (LOW) counter: 3
blinker_loop (HIGH) counter: 4
blinker_loop (LOW) counter: 4
blinker_loop (HIGH) counter: 5
Fibbonacci of 37=24157817
blinker_loop (LOW) counter: 5
blinker_loop (HIGH) counter: 6
blinker_loop (LOW) counter: 6
blinker_loop (HIGH) counter: 7
blinker_loop (LOW) counter: 7
blinker_loop (HIGH) counter: 8
blinker_loop (LOW) counter: 8
Fibbonacci of 38=39088169
blinker_loop (HIGH) counter: 9
blinker_loop (LOW) counter: 9
blinker_loop (HIGH) counter: 10
blinker_loop (LOW) counter: 10
blinker_loop (HIGH) counter: 11
blinker_loop (LOW) counter: 11
blinker_loop (HIGH) counter: 12
blinker_loop (LOW) counter: 12
blinker_loop (HIGH) counter: 13
blinker_loop (LOW) counter: 13
blinker_loop (HIGH) counter: 14
blinker_loop (LOW) counter: 14
Fibbonacci of 39=63245986
blinker_loop (HIGH) counter: 15
blinker_loop (LOW) counter: 15
blinker_loop (HIGH) counter: 16
blinker_loop (LOW) counter: 16
blinker_loop (HIGH) counter: 17
blinker_loop (LOW) counter: 17
blinker_loop (HIGH) counter: 18
blinker_loop (LOW) counter: 18
blinker_loop (HIGH) counter: 19
blinker_loop (LOW) counter: 19
blinker_loop (HIGH) counter: 20
blinker_loop (LOW) counter: 20
blinker_loop (HIGH) counter: 21
blinker_loop (LOW) counter: 21
blinker_loop (HIGH) counter: 22
blinker_loop (LOW) counter: 22
blinker_loop (HIGH) counter: 23
blinker_loop (LOW) counter: 23
Fibbonacci of 40=102334155
fibonacci_loop counter: 1
Fibbonacci of 30=832040
Fibbonacci of 31=1346269
blinker_loop (HIGH) counter: 24
Fibbonacci of 32=2178309
Fibbonacci of 33=3524578
blinker_loop (LOW) counter: 24
Fibbonacci of 34=5702887
blinker_loop (HIGH) counter: 25
blinker_loop (LOW) counter: 25
Fibbonacci of 35=9227465
blinker_loop (HIGH) counter: 26
blinker_loop (LOW) counter: 26
Fibbonacci of 36=14930352
blinker_loop (HIGH) counter: 27
blinker_loop (LOW) counter: 27
blinker_loop (HIGH) counter: 28
blinker_loop (LOW) counter: 28
blinker_loop (HIGH) counter: 29
Fibbonacci of 37=24157817
blinker_loop (LOW) counter: 29
blinker_loop (HIGH) counter: 30
blinker_loop (LOW) counter: 30
blinker_loop (HIGH) counter: 31
blinker_loop (LOW) counter: 31
blinker_loop (HIGH) counter: 32
blinker_loop (LOW) counter: 32
Fibbonacci of 38=39088169
blinker_loop (HIGH) counter: 33
blinker_loop (LOW) counter: 33
blinker_loop (HIGH) counter: 34
blinker_loop (LOW) counter: 34
blinker_loop (HIGH) counter: 35
blinker_loop (LOW) counter: 35
blinker_loop (HIGH) counter: 36
blinker_loop (LOW) counter: 36
blinker_loop (HIGH) counter: 37
blinker_loop (LOW) counter: 37
blinker_loop (HIGH) counter: 38
Fibbonacci of 39=63245986
blinker_loop (LOW) counter: 38
blinker_loop (HIGH) counter: 39
blinker_loop (LOW) counter: 39
blinker_loop (HIGH) counter: 40
blinker_loop (LOW) counter: 40
blinker_loop (HIGH) counter: 41
blinker_loop (LOW) counter: 41
blinker_loop (HIGH) counter: 42
blinker_loop (LOW) counter: 42
blinker_loop (HIGH) counter: 43
blinker_loop (LOW) counter: 43
blinker_loop (HIGH) counter: 44
blinker_loop (LOW) counter: 44
blinker_loop (HIGH) counter: 45
blinker_loop (LOW) counter: 45
blinker_loop (HIGH) counter: 46
blinker_loop (LOW) counter: 46
blinker_loop (HIGH) counter: 47
Fibbonacci of 40=102334155
fibonacci_loop counter: 2
Fibbonacci of 30=832040
Fibbonacci of 31=1346269

WDT period is less than that, so you have to reset inside loop

I can't reset inside the loop because in "real life" I do not not know where and/or when it may stall, or even how long.
The preemptive roundrobin scheduler is expected to work reliably even then, that is what preemptivity finally is about 8)

The preemptive roundrobin scheduler is expected to work reliably even then, that is what preemptivity finally is about 8)

What part of the std::thread specification states that it is a preemptive scheduler that manages the tasks, I can't find anything that states that it is required to be that way.

There is also no guarantee that the pthread implementation will always use a preemptive kernel.

The WDT period is configured when me-no-dev generates the IDF libraries, I don't know the current value, I think it is about 8 seconds. So, any task needs to complete in less than that duration. if fib(40) takes 15 seconds, then inside of fib() there needs to be a WDT reset call. If you are worried about a specific task locking up, you can add your specific task to the WDT list, and require it to clear it's own WDT. esp_task_wdt_add() this applies to a FreeRTOS task, It looks like the STD::THREAD lives somewhere else.

Chuck.

imagine a i2c device don't answer to a read or write command because of any unknown reason, and the device lib cuts the connection (intermediately or finally), not proceeding with anything afterwards.
Or perhaps another unforeseen issue might have caused that thread to stall intermediately.
As the main task now also stalls, I cannot even evaluate heartbeats any more to detach or join that thread and restart the i2c bus and the thread anew (assuming it was possible).

However, it would be absolutely no problem if the scheduler relentlessly switched e.g. every 1 ms to the next time slice for the next process/thread, independent of everything else.

If that issue is crucial and ineluctable and sort of an immutable basic (mal-)function to a ESP32, then I have to stay with the Raspi unfortunately.

Sounds like a plan, You want/need a complex system that supports a preemptive multitasking, so by spending $35+$10(sdcard) an you will have a much more capably system than a $6 esp32.

Chuck.

yes, I have that already, but I was thinking of downgrading because of the simpler Arduino API, also for WiFi and Webserver vs. C/C++ + makefile/cmake + wiringPi/pigpio and pthread, also for a planned community project. A SD card is always at my Arduinos, and my Adafruit Feather ESP32 costed 24 EUR.

When we don't know something, we search and study. If you don't know c ++ I recommend you get a book (it's "free") and study because arduino is programmed in object oriented c ++. I have a library made for this kind of work of yours. https://github.com/Xx220xX/ThreadEsp32 To install will include include zip library in arduino Ide (if it is very difficult to do this give up programming) then go to Examples and have two examples ps :: is exclusively for Esp32

Actually, the FreeRTOS function vTaskPrioritySet() is available in the std::thread here.

// std::thread for ESP32, Arduino IDE

// ver 0.0.5 fibonacci

#include <Arduino.h>
#include <thread>
#include <freertos/task.h>

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

const auto one_sec = std::chrono::seconds
{
    1
};




int fibbonacci(int n) {
   if(n == 0){
      return 0;
   } else if(n == 1) {
      return 1;
   } else {
      return (fibbonacci(n-1) + fibbonacci(n-2));
   }
}



void blinker_loop() {
    thread_local uint32_t counter = 0;
    Serial.println((String)"blinker_loop Current priority :" + uxTaskPriorityGet(NULL));
    vTaskPrioritySet(NULL,1);//set Priority
    Serial.println((String)"blinker_loop Current priority :" + uxTaskPriorityGet(NULL));
    while(true) {
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.println((String)"blinker_loop (HIGH) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        digitalWrite(LED_BUILTIN, LOW);
        Serial.println((String)"blinker_loop (LOW) counter: "+ counter);
        std::this_thread::sleep_for(one_sec);

        counter++;
    }
}


void fibonacci_loop() {
    thread_local uint32_t counter = 0, i=0;
    Serial.println((String)"fibonacci_loop Current priority :" + uxTaskPriorityGet(NULL));
    vTaskPrioritySet(NULL,1);//set Priority
    Serial.println((String)"fibonacci_loop Current priority :" + uxTaskPriorityGet(NULL));
    while(true) {
        for(i=30; i<41; i++) {    // limits: test, debug
          Serial.println( (String)"Fibbonacci of "+i+"="+fibbonacci(i));            
        }        
        Serial.println((String)"fibonacci_loop counter: "+counter);  
        counter++;      
    }
}



std::thread *thread_1;
std::thread *thread_2;


void setup() {
  Serial.begin(115200);
  delay(1000);
  thread_1 = new std::thread(blinker_loop);
  thread_2 = new std::thread(fibonacci_loop);
}


void loop() {
    delay(500);
    static uint32_t main_loop_counter = 0;
    Serial.println((String)"main loop: " + main_loop_counter);
    delay(5000);
    main_loop_counter++;
}

result:

blinker_loop Current priority :5
fibonacci_loop Current priority :5
blinker_loop Current priority :1
fibonacci_loop Current priority :1blinker_loop (HIGH) counter: 0

Fibbonacci of 30=832040
Fibbonacci of 31=1346269
main loop: 0
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
blinker_loop (LOW) counter: 2
main loop: 1
blinker_loop (HIGH) counter: 3
Fibbonacci of 36=14930352
blinker_loop (LOW) counter: 3
blinker_loop (HIGH) counter: 4
blinker_loop (LOW) counter: 4
blinker_loop (HIGH) counter: 5
Fibbonacci of 37=24157817
blinker_loop (LOW) counter: 5
main loop: 2
blinker_loop (HIGH) counter: 6
blinker_loop (LOW) counter: 6

thank you, this function you mentioned
vTaskPrioritySet(NULL,1);//set Priority
is new to me, I didn't find that so far although asked for sth like that here in this repo.
Thanks for your contribution!

update

I tried that code but I always get a reboot after Fibonacci 35 because of watchdog issues:

Fibbonacci of 30=832040
Fibbonacci of 31=1346269
main loop: 0
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
E (12195) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12195) task_wdt:  - IDLE0 (CPU 0)
E (12195) task_wdt: Tasks currently running:
E (12195) task_wdt: CPU 0: pthread
E (12195) task_wdt: CPU 1: IDLE1
E (12195) task_wdt: Aborting.
abort() was called at PC 0x400d6577 on core 0

Backtrace: 0x40089150:0x3ffbe160 0x4008937d:0x3ffbe180 0x400d6577:0x3ffbe1a0 0x40081671:0x3ffbe1c0 0x400ea4f5:0x3ffb93f0 0x400ea4f5:0x3ffb9410 0x400ea4f5:0x3ffb9430 0x400ea4f5:0x3ffb9450 0x400ea4f5:0x3ffb9470 0x400ea4f5:0x3ffb9490 0x400ea4f5:0x3ffb94b0 0x400ea4f5:0x3ffb94d0 0x400ea4f5:0x3ffb94f0 0x400ea4f5:0x3ffb9510 0x400ea4f5:0x3ffb9530 0x400ea4f5:0x3ffb9550 0x400ea4f5:0x3ffb9570 0x400ea4f5:0x3ffb9590 0x400ea4f5:0x3ffb95b0 0x400d0ec3:0x3ffb95d0 0x400ea4e1:0x3ffb9620 0x400e6e69:0x3ffb9640 0x400e5f34:0x3ffb9670 0x40087c9d:0x3ffb9690

Rebooting...
ets Jun  8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
blinker_loop Current priority :5
blinker_loop Current priority :1
fibonacci_loop Current priority :5
blinker_loop (HIGH) counter: 0fibonacci_loop Current priority :1

Fibbonacci of 30=832040
Fibbonacci of 31=1346269
main loop: 0
Fibbonacci of 32=2178309
blinker_loop (LOW) counter: 0
Fibbonacci of 33=3524578
blinker_loop (HIGH) counter: 1
Fibbonacci of 34=5702887
blinker_loop (LOW) counter: 1
blinker_loop (HIGH) counter: 2
Fibbonacci of 35=9227465
E (12195) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12195) task_wdt:  - IDLE0 (CPU 0)
E (12195) task_wdt: Tasks currently running:
E (12195) task_wdt: CPU 0: pthread
E (12195) task_wdt: CPU 1: IDLE1
E (12195) task_wdt: Aborting.
abort() was called at PC 0x400d6577 on core 0

Backtrace: 0x40089150:0x3ffbe160 0x4008937d:0x3ffbe180 0x400d6577:0x3ffbe1a0 0x40081671:0x3ffbe1c0 0x400ea4f5:0x3ffb93f0 0x400ea4f5:0x3ffb9410 0x400ea4f5:0x3ffb9430 0x400ea4f5:0x3ffb9450 0x400ea4f5:0x3ffb9470 0x400ea4f5:0x3ffb9490 0x400ea4f5:0x3ffb94b0 0x400ea4f5:0x3ffb94d0 0x400ea4f5:0x3ffb94f0 0x400ea4f5:0x3ffb9510 0x400ea4f5:0x3ffb9530 0x400ea4f5:0x3ffb9550 0x400ea4f5:0x3ffb9570 0x400ea4f5:0x3ffb9590 0x400ea4f5:0x3ffb95b0 0x400d0ec3:0x3ffb95d0 0x400ea4e1:0x3ffb9620 0x400e6e69:0x3ffb9640 0x400e5f34:0x3ffb9670 0x40087c9d:0x3ffb9690

Rebooting...
ets Jun  8 2016 00:22:57

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:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c
blinker_loop Current priority :5
blinker_loop Current priority :1
fibonacci_loop Current priority :5
blinker_loop (HIGH) counter: 0fibonacci_loop Current priority :1

Fibbonacci of 30=832040

Hmm, as stickbreaker said, there needs to be a WDT reset call. std::thread here is implemented on top of FreeRTOS in ESP-IDF. Therefore, you can simply increase the WDT timeout value in setup() like this:
#include <esp_task_wdt.h>

#define TWDT_TIMEOUT_S 20
  //Initialize or reinitialize TWDT
  esp_task_wdt_init(TWDT_TIMEOUT_S, false);
  //Subscribe Idle Tasks to TWDT if they were not subscribed at startup
#ifndef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
  esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0));
#endif
#ifndef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
  esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(1));

However, it's not a solution at all...
If you use esp_task_wdt_init(TWDT_TIMEOUT_S, true);, you will got warnings but it won't lead to rebooting.
To avoid that WDT got triggered, you can disable WDT in this way, which works for me:

  //Check which core are Idle Tasks in and disable the core's WDT
#ifndef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
  disableCore0WDT();
#endif
#ifndef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
  disableCore1WDT();
#endif

But I'm not sure whether there will be any errors caused by blocked tasks.

This is non-issue, all that's needed is to set task priority lower than loop task, if I'm not mistaken. Then loop task will feed the watchdog.

And of course, some time should be invested into preventing your tasks from stalling. Stalling a task because of faulty i2c device is definitely a bug.

@Masterlong-Dragon
how did YOU manage the program to work?
I took you code, your serial output from https://github.com/espressif/arduino-esp32/issues/2814#issuecomment-515952643 seemed to be ok,
but for me it does not work
(Arduino IDE: 1.8.9,
board manager: latest ESP32 core 1.0.2)

@me21:
Thank you, I have to agree, when setting the thread to prio 0 then it works!

JTM:
IMO it would be better to set the main loop to a higher prio by default (e.g., 3) to have a wider bandwidth fo thread prios to cooperate!

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

OAXP picture OAXP  Â·  4Comments

Darkhub picture Darkhub  Â·  3Comments

huming2207 picture huming2207  Â·  4Comments

ComputerLag picture ComputerLag  Â·  3Comments

mpatafio picture mpatafio  Â·  4Comments