Teensy 3.x multithreading library first release

... Teensy that communicates through UART to a slow cellular modem part (a SIM800). The library I'm using is blocking

That sounds like a good use case from what I saw in some prior notes. The UART should keep working.

Guess:: Perhaps using one of the samples put all current loop() code on one thread - and put a simple blink/print(millis) on another thread. If that still works being switched then you could segment the loop() code so another thread handles the UART xfers and the rest is then separate thread(s) - which may require some appropriate handshake/safety between the threads to keep data safely used when ready and paused when not if the remainder code is doing processing on ready data.
 
yes epyon, you can have that done with 2 threads easily, 1 thread doing your sim800, another running your code, using 4 threads in my setup so waiting not an issue at all

regarding the safety, mutexes will protect concurrent connections to the same hardware, the threads will take turns when accessing that port without collision
 
Returning to the problem of using too much memory for unused threads, I have made a small change in the library on Github. Before this change, the library would statically allocate the memory needed to save the state of 8 threads, which could add up to 8K or more depending on the CPU. After this change, it will allocate the memory dynamically on the heap so if you don't use a thread, it won't use the memory. In the past, I had suggested using a linked list for this. However for the sake of time and simplicity, I create an array of pointers for each thread (4 bytes each X 8 threads = 32 bytes static memory). As threads are used, the thread states get allocated and the pointers get filled. One side effect of this is that if you use an invalid thread id in any of the calls, you will probably crash the system. Let me know if this causes any problems.
 
honestly, the user should be already aware of writing to invalid locations will cause crashes, so i see no issue with this
 
Hi Fritas,

I'm experimenting with using your new Threading library to an existing project based on the single threaded, superloop design. My plan was to identify logical sections which need to run simultaneously & put those into threads. I realize this may be a lot harder than it sounds, as I assume that none of the Arduino device libraries etc. can be assumed to be thread-safe?

So for the first pass, I was going to get just two threads running, and wrap any peripheral objects in the threadsafe macro I saw defined somewhere in the implementation. Do you think this will work? If it does, it's going to be a bit of a pain to change all Serial.prints to SerialX->print, but that that's the best way to go about making it robust then that's acceptable.
 
Hi Fritas,

I'm experimenting with using your new Threading library to an existing project based on the single threaded, superloop design. My plan was to identify logical sections which need to run simultaneously & put those into threads. I realize this may be a lot harder than it sounds, as I assume that none of the Arduino device libraries etc. can be assumed to be thread-safe?

So for the first pass, I was going to get just two threads running, and wrap any peripheral objects in the threadsafe macro I saw defined somewhere in the implementation. Do you think this will work? If it does, it's going to be a bit of a pain to change all Serial.prints to SerialX->print, but that that's the best way to go about making it robust then that's acceptable.

The macro is a bit of a cludge that relies on compiler tricks. It's there so you don't have to change all your calls to try the library. First, it hasn't been tested very well, so please let us all know if there are problems. Second, it is pretty slow and adds a lot of overhead. The fastest method is still to use a Mutex.

To explain the usage, I'll elaborate on the readme.md file example:

Code:
ThreadWrap(Serial, SerialX);
#define Serial ThreadClone(SerialX)

int thread_func()
{
    Serial.println("begin");
}

It will create a new class `SerialX` that wraps the original `Serial` object. Then it will use a macro to redefine `Serial` to `SerialX` for the rest of the file (every line after the #define statement). So every time you specify `Serial`, it will be replaced to be `SerialX`. In this way all you have to do is add the two top lines after all your "#include" statements.
 
fredb, none of the libraries ive used has issues with ftrias's library, making them thread-safe would be properly handling the mutexes involved to prevent 2 or more threads from accessing the same port, thus if both try to access the same port, they will wait until its available before accessing it, like taking turns. to simplify my multi-thread & multi lcd code i created a pass by reference function with the mutex lock within it, and changed all library calls to go through there to process their commands
 
That sounds like a good use case from what I saw in some prior notes. The UART should keep working.

Guess:: Perhaps using one of the samples put all current loop() code on one thread - and put a simple blink/print(millis) on another thread. If that still works being switched then you could segment the loop() code so another thread handles the UART xfers and the rest is then separate thread(s) - which may require some appropriate handshake/safety between the threads to keep data safely used when ready and paused when not if the remainder code is doing processing on ready data.

yes epyon, you can have that done with 2 threads easily, 1 thread doing your sim800, another running your code, using 4 threads in my setup so waiting not an issue at all

regarding the safety, mutexes will protect concurrent connections to the same hardware, the threads will take turns when accessing that port without collision
For what it's worth, I decided not to use multithreading because it would not solve my specific problem. The slow cellular modem is transferring a file from SD, while the main loop is writing data to SD. This means one thread would be blocked by the other anyway because they need access to the same peripheral. I have rewritten my cellular library into a state machine where the main loop now periodically checks for (acknowledgement) replies from the modem, in stead of waiting for them before proceeding. This has pretty much solved the issue for now.
 
Detect stack overflow

I'm considering adding support to detect stack overflows. With Teensy's small memory footprint, optimizing memory use seems important because it's easy to introduce undetected bugs if the stack overflows. This can easily happen if inadvertently you call library functions that allocate a lot of memory. smachin1000 opened an issue on GitHub about this that prompted me to revisit the problem.

Currently, the functions `getStackUsed()` and `getStackRemaining()` can be used to monitor memory usage in your code, but an automatic method would be much easier to implement.

In keeping with the style of existing fault ISRs, I plan to add a `stack_overflow_isr()` that gets called in the event of an overflow. By default it will just kill the thread, but you can override this by adding your own `stack_overflow_isr()` function in your code.

It will not check for overflow on thread 0 because thread 0 doesn't have a fixed stack size. Basically, thread 0 stack grows until it hits the heap, which also grows dynamically. It's possible to fix this eventually, but it may require changes to the memory allocation code. These are the changes I propose:

Code:
diff --git a/TeensyThreads.cpp b/TeensyThreads.cpp
index d86397b..74f4b59 100644
--- a/TeensyThreads.cpp
+++ b/TeensyThreads.cpp
@@ -48,6 +48,13 @@ extern "C" {
   }
 }
 
+extern "C" void stack_overflow_default_isr() { 
+  currentThread->flags = Threads::ENDED;
+}
+extern "C" void stack_overflow_isr(void)       __attribute__ ((weak, alias("stack_overflow_default_isr")));
+
+// extern unsigned long _estack;   // the main thread 0 stack
+
 Threads::Threads() : current_thread(0), thread_count(0), thread_error(0) {
   // initilize thread slots to empty
   for(int i=0; i<MAX_THREADS; i++) {
@@ -65,6 +72,8 @@ Threads::Threads() : current_thread(0), thread_count(0), thread_error(0) {
   currentActive = FIRST_RUN;
   threadp[0]->flags = RUNNING;
   threadp[0]->ticks = DEFAULT_TICKS;
+  // threadp[0]->stack = (uint8_t*)&_estack - DEFAULT_STACK_SIZE;
+  // threadp[0]->stack_size = DEFAULT_STACK_SIZE;
   currentUseSystick = 1;
 }
 
@@ -103,6 +112,11 @@ void Threads::getNextThread() {
   // First, save the currentSP set by context_switch
   threadp[current_thread]->sp = currentSP;
 
+  // did we overflow the stack (don't check thread 0)?
+  if (current_thread && ((uint8_t*)currentThread->sp - currentThread->stack <= 8)) {
+    stack_overflow_isr();
+  }
+
   // Find any priority threads
   int priority_thread = -1;
   for(int i=0; i < MAX_THREADS; i++) {
diff --git a/TeensyThreads.h b/TeensyThreads.h
index 8ea7d7b..7dc7017 100644
--- a/TeensyThreads.h
+++ b/TeensyThreads.h
@@ -77,6 +77,7 @@ extern "C" {
   void context_switch_pit_isr(void);
   void systick_isr(void);
   void loadNextThread();
+  void stack_overflow_isr(void);
 }
 
 // The stack frame saved by the interrupt

You would use it as follows. In this example, the `recursive_thread` will use up all available stack space because it has a bug.

Code:
volatile int stack_fault = 0;

void stack_overflow_isr(void) {
  stack_fault = 1;
  threads.kill(threads.id());
}

void recursive_thread() {
  if (stack_fault) return;
  /* do something buggy that never calls return ... */
  recursive_thread();
}

void setup() {
  threads.addThread(recursive_thread);
}
 
Teensy Threads & yield() call

Guys,

Is yield() still not thread-safe in the current code (commit 2e3183bff21eb4da23c6484b373efb394ed35655, Aug 23 07:50:37 2017 -0400)? And if it is not thread-safe, is the workaround still to call threads.delay(1) instead?

Thanks for the feedback on the Teensy 3.5. I just got a Teensy 3.6. I am waiting for a 3.5 to test. There are two issues:

1. The new Teensy has a floating point unit and its state must be saved between threads. That should not be to difficult to add.

2. The yield() function is not thread-safe because of some of the functions it calls. That is, it can't be called from two threads at the same time. Unfortunately, it is called by "delay()" and after every "loop()" call. Because of this, you will cause a crash if you use delay() within a thread. The Test example makes copious use of delay() and thus fails, whereas the Print example does not. I'm not sure why this isn't a problem for the Teensy 3.2, but maybe I just haven't seen it yet.

As a workaround, you can use threading library's "threads.delay()" function to yield CPU time to other threads. This should work fine and is probably preferred anyway. I will look into the root cause of why yield() is not thread-safe to see if there is a more universal solution.
 
Guys,

Is yield() still not thread-safe in the current code (commit 2e3183bff21eb4da23c6484b373efb394ed35655, Aug 23 07:50:37 2017 -0400)? And if it is not thread-safe, is the workaround still to call threads.delay(1) instead?

I don't think it's thread-safe. But if you call it from only one thread, that should be OK. As far as I can see, yield() still calls Serial objects and it can't be run on two threads simultaneously. But perhaps the Serial objects are now thread-safe?
 
@fritas, so to be 100% clear, we are saying that the old library version of yield() is not thead-safe, but the new one you've implemented in TeensyThreads is thead-safe (i.e. threads.yield())?
 
@fritas, so to be 100% clear, we are saying that the old library version of yield() is not thead-safe, but the new one you've implemented in TeensyThreads is thead-safe (i.e. threads.yield())?

Yes because they do different things. Maybe it was a mistake to call it "threads.yield()". The "yield()" function in Teensy flushes out the serial devices and does other housekeeping chores. It's called for every time "loop()" is run. It needs to be called somehow every now and then.

The "threads.yield()" function just tells TeensyThreads to give up the rest of the time slice and go to the next thread. It does not flush out the serial devices or do any of the other housekeeping things that yield() does.
 
The old PJRC yield() is in place to maintain Arduino compatibility - it calls out to any user implemented serialEvent() functions when bytes are ready to read on any Serial# port on the device. If serialEvent is not used it has no value.

It was recently changed to process "EventResponder::runFromYield();" - when called after each loop() completes.
 
Actually your implementation makes the most sense to me, the call to yield is doing just what I'd expect. I would not expect a call to yield to cause side effects on the serial port.
 
Guys,

Do we have any general guidelines for using TeensyThreads on existing platformio projects that use the Arduino APIs? E.g. apart from adding locks to functions that access shared resources, what other steps should be taken to ensure thread safety & reliable operation?

Also I notice my gcc toolchain has been built with the "--disable-threads" flag. Won't that be a problem if trying to call stdlib. functions from Teensy threads?
 
Same here. Got my project mostly working but had to disable SD card access (haven't fully investigated what it would take to make that thread-safe yet), and got a lockup problem I need to resolve. Working well apart from that.
 
@fritas and others. We are porting over some older code to use TeensyThreads. There are several sections of the old code that briefly disable interrupts, e.g.:

noInterrupts();
...
...
interrupts();

Will blocks like these interfere with TeensyThreads?
 
@fritas and others. We are porting over some older code to use TeensyThreads. There are several sections of the old code that briefly disable interrupts, e.g.:

noInterrupts();
...
...
interrupts();

Will blocks like these interfere with TeensyThreads?

noInterrupts()/interrupts() will stop all thread switches (and all other interrupts). So it should be ok.
 
Hi @fritas. I was looking at GitHub issue on sleep on just came across a challenge in that I wanted to the thread to sleep or wait until another thread completed. The code is shown on this post. What it is doing I using eventresponder to turn the led on and off (I know there are easier ways but I was playing). To get it to work properly I had to basically suspend the ledOn thread until the ledOff thread completed. Think that is where a threads.sleep(thread ID) might be useful without putting the whole teensy to sleep. Not necessarily for turning LEDs on and off but for other purposes.

Thanks
Mike
 
Working on a new project involving RTK and waypoint navigation for a rc car and came across a question I never thought about regarding tracking time between readings of sensors. The question is when does the thread actually start executing - after creating in setup of when primary loop starts?

Thanks
Mike
 
Back
Top