ArduinoCore-API icon indicating copy to clipboard operation
ArduinoCore-API copied to clipboard

extension request: preemptive multithreading lib for M0, M3, M4 (like std::thread, ftrias/TeensyThreads, ESP32)

Open dsyleixa opened this issue 6 years ago • 7 comments

hi, coincidentally I found the lib ftrias/TeensyThreads https://github.com/ftrias/TeensyThreads, unfortunately working just for Teensys. IIUC, it's not trivial to port that preemptive multithreading lib to standard Arduinos... :( So what would be realy awesome is if there was such a std::thread implementation available for M0, M3, M4 Arduino/Adafruit cores!

dsyleixa avatar Mar 29 '19 08:03 dsyleixa

There is a project called FreeRTOS that already implements this across almost every uC and CPU that exists There are Arduino ports of FreeRTOS for AVR 8-bit boards, STM32 and Atmel SAMD21 boards

cheetor5923 avatar Apr 16 '19 21:04 cheetor5923

There is a project called FreeRTOS that already implements this across almost every uC and CPU that exists There are Arduino ports of FreeRTOS for AVR 8-bit boards, STM32 and Atmel SAMD21 boards

no, I meant an extension for the standard Arduino API/ IDE, just to have threads additionally using std::thread :

as simple to use as this:

// 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) {
        // do something 
        counter++;
    }
}

void blinker_loop() {
    thread_local uint32_t counter = 0;
    while(true) {
        //do something
        counter++;
    }
}


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

void setup() {
  Serial.begin(115200);
  thread_1 = new std::thread(counter_loop);
  thread_2 = 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);
}

dsyleixa avatar Apr 17 '19 11:04 dsyleixa

Coincidentally I meanwhile also found thei std::thread implementation to ESP32: https://github.com/espressif/esp-idf/blob/master/examples/system/cpp_pthread/main/cpp_pthread.cpp

dsyleixa avatar May 23 '19 12:05 dsyleixa

update: for ESP32 (Adafruit Feather ESP32) std::thread works fine now: https://github.com/espressif/arduino-esp32/issues/2814#issuecomment-495525603

but the code doen't compile for Arduinos, neither for Arduino Due nor Arduino M0/Zero nor for (Adafruit) SAMD51/M4 (e.g., many error messages are about max() and min() ):

// 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::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);
}


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

dsyleixa avatar May 24 '19 08:05 dsyleixa

PS,

as tested, std::string by esp32 implementation appears to work as preemptive:

// std::thread for ESP32, Arduino IDE

#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.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++;
        Serial.print("blinker_loop counter: "); Serial.println(counter); 
        
    }
}

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



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


void setup() {
  Serial.begin(115200);
  //debug
  delay(2000);
  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.print("main loop: ");
    Serial.println(main_loop_counter);
    delay(10000);
}

output:

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

dsyleixa avatar May 25 '19 11:05 dsyleixa

Finally and thankfully now also Arduino.cc has figured out which terrific advantages preemptive multithreading offers for more powerful ARM Cortex MCUs! https://store.arduino.cc/nano-33-ble-with-headers https://store.arduino.cc/nano-33-ble-sense

As many of you may know, Mbed is a fully preemptive RTOS (real-time operating system), meaning you can run multiple “programs” (more specifically, threads) at the same time, much like what happens in your notebook or smartphone

dsyleixa avatar Aug 31 '19 15:08 dsyleixa

update: it turned out that the preemptive ESP32 std: thread implementation (based on freeRTOS) is stable and not hooking up the entire program any more when blocking or long-time calculating or continuously running loops (while forever) are started by thread prio=0 (same prio as RTOS watchdog). In that case the scheduler switches the time slices in any case, even without delays (as expected) Threads started by prio >0 (1...5) need to provide yield() or thread_sleepfor() not to block different lower-prio threads (also as expected). (tested, works like a charm)

That is how it would be wishful also for Arduino M0 (Zero), M3 (Due) and perhaps 3rd party M4 (e.g., by Adafruit).

dsyleixa avatar Aug 31 '19 16:08 dsyleixa