Teensy 3.x multithreading library first release

so if a locked scope processes another function call that also has a lock on it, thats a guarenteed deadlock? why not check if the ownership of the current mutex lock is of same ownership of the one being called in the other called function, letting it pass through like a normal single threaded sketch? i would assume that as being recursive, correct? why wouldnt this be useful?

If you are locking the same mutex then it's a deadlock.

Lets say I start a lock, I would then assume responsibility of all current and future calls, of other functions, regardless if they also include locks of their own, as long as I currently hold the MAIN lock status flag, I should be able to bypass other lock sections to process their code as well. Recursive would be more ideal then. The owner could basically be the thread id (id1) for example. And if thread(id2) tried to call a function with a lock, his access, recursive or not, will be queued until thread(id1) lock (recursive or not) is removed.

Some platforms have recursive mutexes. But it's an advanced topic. I'm not sure it's suitable for a simple platform like Teensy so I didn't implement them.

Some also have a type of lock called a semaphone that might work for something like this. It keeps track of the number of locks but doesn't actually block you. So if you lock, it adds 1 to the count. When you unlock it subtracts. Your code has to figure out what to do based on the count. I didn't implement this either because it's so simple, you can easily implement it in your own code.

thread1: im locked, i own all current and future code, any locks in sub functions will be ignored as I will run their code anyways! (Basically same concept as an everyday single threaded arduino sketch.
thread2: arg, thread1 owns the main lock, I will just wait since I need to aquire a lock.
thread3: gibbs on next queue! I'll be right after you!

if thread(id1) is locked, and a subfunction calls to lock a mutex, I could imagine HIS scope would exit the lock, but, wouldn't the library function just say:

thread(id1) is locked, i need to call foo();, oh! there is a scope lock in that function, im told to not set it as I already own the main lock, that way when it exits I dont remove the main lock, oh great, i returned to my scope, and ended the brace, release the main lock. (this would be sort of like a master lock in single thread mode).
isnt this safe?

All these things are possible, but they are a bit too specialized for particular purposes and scenarios to be included in a general purpose library. If you look at the code, it actually relatively simple to implement your own specialized locking logic.
 
yeah i guess i could implement that in the mutex locking function to check for existing locks, it's just something to watch out for as it wont be persistant through library updates
 
so if a locked scope processes another function call that also has a lock on it, thats a guarenteed deadlock? why not check if the ownership of the current mutex lock is of same ownership of the one being called in the other called function, letting it pass through like a normal single threaded sketch? i would assume that as being recursive, correct? why wouldnt this be useful?
Recursive locks are evil. They are nothing but a source of extremely hard to track down bugs.

Some reading:

http://www.zaval.org/resources/library/butenhof1.html
http://www.fieryrobot.com/blog/2008/10/14/recursive-locks-will-kill-you/
http://blog.stephencleary.com/2013/04/recursive-re-entrant-locks.html
 
if one or more locks appear after the current call, we could just simply have the function return so the lock never happens while the current lock state is owned.

basically

if ( ownership exists ) return;, as a top line of the lock function, that way there is no recursive locking at all, even if sub functions contain locking scopes, the function doesnt process anything as it's returned leaving the code run normal. if course, you'd have to write it specifically if the ownership is from thread1, then only return (dont process) any further locks until the scope is finally done. this will still keep the other threads suspended, only the 1st lock thread will do whatever it wants using a single lock, no matter how many locks in runs through
This is not recursive locking, this is letting the current lock run all code it needs to do to do it's job without processing any extra locks

essentially, this would leave only a single lock in the system, that will go through any code it needs to and ignore the successive Threads::Scope scope(x); locks, as if those lines never existed. It will only exist to the threads not holding the lock.

so if you put a scope lock on the entire loop() for example, no other threads will ever process until the loop scope is done from start to end. All the Threads::Scope locks within that loop are ignored only by the thread holding the lock aka thread0, all other threads must follow the mutex lock routine same as before, only the mutex holder doesnt process additional Threads::Scope lock functions (as if the lines were never put in the sketch.

I think this is a great idea, and is not recursive locking, it might be very practical, and add compatibility for many code/libraries involved in your sketch
 
Last edited:
if one or more locks appear after the current call, we could just simply have the function return so the lock never happens while the current lock state is owned.

basically

if ( ownership exists ) return;, as a top line of the lock function, that way there is no recursive locking at all, even if sub functions contain locking scopes, the function doesnt process anything as it's returned leaving the code run normal. if course, you'd have to write it specifically if the ownership is from thread1, then only return (dont process) any further locks until the scope is finally done. this will still keep the other threads suspended, only the 1st lock thread will do whatever it wants using a single lock, no matter how many locks in runs through
This is not recursive locking, this is letting the current lock run all code it needs to do to do it's job without processing any extra locks

essentially, this would leave only a single lock in the system, that will go through any code it needs to and ignore the successive Threads::Scope scope(x); locks, as if those lines never existed. It will only exist to the threads not holding the lock.

so if you put a scope lock on the entire loop() for example, no other threads will ever process until the loop scope is done from start to end. All the Threads::Scope locks within that loop are ignored only by the thread holding the lock aka thread0, all other threads must follow the mutex lock routine same as before, only the mutex holder doesnt process additional Threads::Scope lock functions (as if the lines were never put in the sketch.

I think this is a great idea, and is not recursive locking, it might be very practical, and add compatibility for many code/libraries involved in your sketch

While you are thinking up all these interesting ways to make locking easier, you might want to look at this summary of different types of locks to see if anything else is suitable:

https://www.justsoftwaresolutions.co.uk/threading/locks-mutexes-semaphores.html
 
please check this out, i just implemented it, cpp function edit:

Code:
int Threads::Mutex::lock(unsigned int timeout_ms) {
  static uint8_t ownedThread = -1;
  int p;
  uint32_t start = systick_millis_count;
  if ( state && ownedThread != threads.current_thread ) {
    return 0;
  }
  while (1) {
    p = try_lock();
    if (p) return 1;
    if ( ownedThread == -1 ) ownedThread = threads.current_thread;
    if (timeout_ms && (systick_millis_count - start > timeout_ms)) return 0;
    if (waitthread==-1) { // can hold 1 thread is suspend until unlock
      waitthread = threads.current_thread;
      waitcount = currentCount;
      threads.suspend(waitthread);
    }
    threads.yield();
  }
  ownedThread = -1;
  return 0;
}


and the sketch:

Code:
#include <Arduino.h>
#include "TeensyThreads.h"
#include <i2c_t3.h>

//ThreadWrap(Serial, SerialX);
//ThreadWrap(Wire, WireX);
//#define Serial ThreadClone(SerialX)
//#define Wire ThreadClone(WireX)

volatile uint32_t myCounter = 0;

int count = 0;
const int LED = 13;

Threads::Mutex i2c_lock;
Threads::Mutex serial_lock;
Threads::Mutex scope1;
Threads::Mutex scope2;


void setup() {
  delay(2000);
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);
  Wire.setDefaultTimeout(200000); // 200ms
  pinMode(LED, OUTPUT);

  auto blinky = threads.addThread(blinkthread);
  //  threads.setSliceMicros(515);
  //  threads.setMicroTimer(1);
  threads.setTimeSlice(0, 1);
  threads.setTimeSlice(1, 1);
  //  threads.setTimeSlice(blinky, 1000);
}


void loop() {
  {
    Threads::Scope scope(scope1);
    Serial.println("1");
    Serial.println("2");
    Serial.println("3");

    byte x, y;
    {
      Threads::Scope scope(i2c_lock);
      Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
      Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 2);
      x = Wire.read();
      y = Wire.read();
    }
    Serial.println("4");
    Serial.println("5");
    Serial.println("6");

    {
      Threads::Scope locker(serial_lock);
      if ( x == 191 && y == 127 ) {
        myCounter++;
        digitalWriteFast(LED, 1);
        Serial.print("Wire Register Read Thread1: ");
        Serial.print(x);
        Serial.print(" : ");
        Serial.print(y);
        Serial.print(" GOOD! : Counter: ");
        Serial.println(myCounter);
        Serial.println("7");
        Serial.println("8");
        Serial.println("9");
      }
      else {
        Serial.print("Wire Register Read Thread1: ");
        Serial.print(x);
        Serial.print(" : ");
        Serial.print(y);
        Serial.println(" FAIL!");
      }
      Serial.println("10");
      Serial.println("11");
      Serial.println("12");

      delay(2000);
    }
    //  threads.yield();
  }
}

void blinkthread() {
  while (1) {
    {
      Threads::Scope scope(scope2);
      byte x, y;
      {
        Threads::Scope scope(i2c_lock);
        Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
        Wire.beginTransmission(32); Wire.write(0x13); Wire.endTransmission(); Wire.requestFrom(32, 1);
        x = Wire.read();
        Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 1);
        y = Wire.read();
      }
      {
        Threads::Scope locker(serial_lock);
        if ( x == 127 && y == 191 ) {
          myCounter--;
          digitalWriteFast(LED, 0);
          Serial.print("Wire Register Read Thread2: ");
          Serial.print(x);
          Serial.print(" : ");
          Serial.print(y);
          Serial.print(" GOOD! : Counter: ");
          Serial.println(myCounter);
        }
        else {
          Serial.print("Wire Register Read Thread2: ");
          Serial.print(x);
          Serial.print(" : ");
          Serial.print(y);
          Serial.println(" FAIL!");
        }
      }
      delay(1000);
      //    threads.yield();
    }
  }
}

and, last but not least, the output:

Code:
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967282
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967281
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967280
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967281
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967280
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967279
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967280
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967279
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967278
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967279
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967278
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967277
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967278
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967277
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967276
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967277
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967276
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967275
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967276
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967275
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967274
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967275
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967274
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967273
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967274
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967273
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967272
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967273
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967272
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967271
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967272
7
8
9
10
11
12
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967271
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967270
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967271
7
8
9
10
11
12

both threads have multiple locks, however, only the initial lock is considered, rest ignored. once the entire scope exits, the lock is released to next task switching, and voila, they are running sequentially while ignoring recursive locks as if they never existed!

all the prints belong to thread1 to demonstrate the entire scope effect. thread2 only has 2 prints that print and they are only after thread1, not inbetween threads :)
 
Last edited:
i added a print to thread2 wire function and removed entire global scope on thread2, kept the intact ones in there:
result:

Code:
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967197
7
8
9
10
11
12
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967196
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967195
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967194
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967193
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967192
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967191
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967190
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967189
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967188
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967187
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967186
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967185
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967184
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967183
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967182
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967181
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967180
1
2
3
4
5
6
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4294967181
7
8
9
10
11
12
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967180
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967179
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967178
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967177
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967176
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967175
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967174
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967173
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967172
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967171
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967170
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967169
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967168
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967167
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967166
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967165
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967164
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 4294967163

as you can see, the 2 scope locks on thread2 do not ever touch the scope in thread0
:)
 
TeensyThreads is broken with optimization >= O2 (just run the Tests sketch, the lock test hangs). Threads::start() / Threads::stop() get inlined and the threading suspension gets optimized away.

You should add a compiler barrier to cpsid / cpsie:
__asm volatile("CPSID I" ::: "memory");
__asm volatile("CPSIE I" ::: "memory");
 
@ftrias. Since you posted the link to locks. I saw that there is a reader/writer lock:
Sometimes called shared mutexes, multiple-reader/single-writer mutexes or just read/write mutexes, these offer two distinct types of ownership:
shared ownership, also called read ownership, or a read lock, and
exclusive ownership, also called write ownership, or a write lock.

I know it says shared ownership is for data that is seldom updated it might work for me since it I am dealing essentially with shared data. That is of course if I am reading and interpreting correctly.
 
TeensyThreads is broken with optimization >= O2 (just run the Tests sketch, the lock test hangs). Threads::start() / Threads::stop() get inlined and the threading suspension gets optimized away.

You should add a compiler barrier to cpsid / cpsie:
__asm volatile("CPSID I" ::: "memory");
__asm volatile("CPSIE I" ::: "memory");

Thanks for the heads up about LTO. I replaced the CPSI instructions with __disable_irq() and __enable_irq(), which already contain the memory barrier. I probably should have done this from day 1. There was also a problem in ::wait(), where the flags check would get optimized away. Fixed that. All my tests now work when building with Tools / Optimize / Fastest with LTO. I tried it on a Teensy 3.0, 3.2 and 3.6.

I committed all this to Github.
 
Last edited:
@ftrias. Since you posted the link to locks. I saw that there is a reader/writer lock:


I know it says shared ownership is for data that is seldom updated it might work for me since it I am dealing essentially with shared data. That is of course if I am reading and interpreting correctly.

That's what it is. I think that kind of thing is usually done in databases and the like, where the cost of waiting is very high. In a C++ program, waiting for simple variables is probably insignificant. In any case, the C++17 standard is apparently going to add it:

http://en.cppreference.com/w/cpp/thread/shared_mutex
 
@ftrias. Since you posted the link to locks. I saw that there is a reader/writer lock:
...
I know it says shared ownership is for data that is seldom updated it might work for me since it I am dealing essentially with shared data. That is of course if I am reading and interpreting correctly.
It's rarely particularly useful. Overhead is typically significantly higher (at least for small critical sections) and high lock contention where it would make a difference isn't that common. It also only helps if you actually have multiple concurrent readers.

If you just update a few variables in a short critical section, you definitely don't need it.
 
@ftrias / @tni. Just did a little more research on shared_locks. I see what you all mean now. Thanks for the lessons. Learning quite a bit.
 
@mjs513
I know it says shared ownership is for data that is seldom updated it might work for me since it I am dealing essentially with shared data. That is of course if I am reading and interpreting correctly.

If you are just dealing with sharing simple data types, you may also want to consider using 'std::atomic'. I haven't used it on Teensy, so I'm not sure it really works, but it's in there:

http://en.cppreference.com/w/cpp/atomic/atomic
 
If you are just dealing with sharing simple data types, you may also want to consider using 'std::atomic'. I haven't used it on Teensy, so I'm not sure it really works, but it's in there:

http://en.cppreference.com/w/cpp/atomic/atomic
It works, even without OS support. ARM Cortex M4 has decent atomic op support.
Code:
#include <atomic>

std::atomic<int> i;

int test() {
    return i;
}

int test2() {
    return i++;
}
Code:
test():
        ldr     r3, .L2
        dmb     sy
        ldr     r0, [r3]
        dmb     sy
        bx      lr
.L2:
test2():
        ldr     r3, .L7
        dmb     sy
.L5:
        ldrex   r0, [r3]
        adds    r2, r0, #1
        strex   r1, r2, [r3]
        cmp     r1, #0
        bne     .L5
        dmb     sy
        bx      lr
.L7:
 
good evening everyone
i decided to scope bracket everything and the test seems to be working pretty well, with the output staying near the 0 mark for the counter
I was just wanted any input if i did the sketch properly if the coding is right

Code:
#include <Arduino.h>
#include "TeensyThreads.h"
#include <i2c_t3.h>

volatile uint32_t myCounter = 0;

int count = 0;
const int LED = 13;

Threads::Mutex wire1;
Threads::Mutex spi1;
Threads::Mutex serial;



void setup() {
  delay(2000);
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);
  Wire.setDefaultTimeout(200000); // 200ms
  pinMode(LED, OUTPUT);

  auto blinky = threads.addThread(blinkthread);
  threads.setTimeSlice(0, 1);
  threads.setTimeSlice(1, 1);
  //  threads.setSliceMicros(1);
  //  threads.setMicroTimer(1);
}


void loop() {
  byte x, y;
  { Threads::Scope scope(serial);
    Serial.println("1");
  }
  { Threads::Scope scope(wire1);
    Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
    Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 2);
    x = Wire.read();
    y = Wire.read();
  }
  { Threads::Scope scope(serial);
    Serial.println("2");
    if ( x == 191 && y == 127 ) {
      myCounter++;
      digitalWriteFast(LED, 1);
      Serial.print("Wire Register Read Thread1: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.print(" GOOD! : Counter: ");
      Serial.println(myCounter);
      Serial.println("3");
    }
    else {
      Serial.print("Wire Register Read Thread1: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.println(" FAIL!");
    }
    Serial.println("4");
  }
}

void blinkthread() {
  while (1) {
    byte x, y;
    { Threads::Scope scope(wire1);
      Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
      Wire.beginTransmission(32); Wire.write(0x13); Wire.endTransmission(); Wire.requestFrom(32, 1);
      x = Wire.read();
      Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 1);
      y = Wire.read();
    }
    { Threads::Scope scope(serial);
      Serial.println("Wire Written");
      if ( x == 127 && y == 191 ) {
        myCounter--;
        digitalWriteFast(LED, 0);
        Serial.print("Wire Register Read Thread2: ");
        Serial.print(x);
        Serial.print(" : ");
        Serial.print(y);
        Serial.print(" GOOD! : Counter: ");
        Serial.println(myCounter);
      }
      else
      {
        Serial.print("Wire Register Read Thread2: ");
        Serial.print(x);
        Serial.print(" : ");
        Serial.print(y);
        Serial.println(" FAIL!");
      }
    }


  }
}


output:

Code:
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 0
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 1
3
4
1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 3
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 3
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 3
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 3
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 3
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 4
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 3
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 3
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 2
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1
Wire Written
Wire Register Read Thread2: 127 : 191 GOOD! : Counter: 1
2
Wire Register Read Thread1: 191 : 127 GOOD! : Counter: 2
3
4
1

to my understanding, each uart/spi/i2c must have a different scope name, and if the same port is to be used, those scopes must have a matching name
so, if both threads access "wire1" scope, they take turns, and because the value of counter stays near 0 as it goes up and down between threads and scope, it's also a sign that theres no data loss.

so if i wanted to protect wire,wire1,wire2 for example, i'd setup 3 mutexes, one per port like this:
Code:
Threads::Mutex wire;
Threads::Mutex wire1;
Threads::Mutex wire2;

that would mean wire1 and wire2 can run simuletaneously, while other wire1 are prevented until the wire1 scope exits. Correct?
i presume im doing everything correctly?

also just to note, using microtimer & setTimeslice together results in NO output. however, if you run either timeslice alone or microtimer alone, sketch runs fine with above code.
 
Last edited:
so if i wanted to protect wire,wire1,wire2 for example, i'd setup 3 mutexes, one per port like this:
Code:
Threads::Mutex wire;
Threads::Mutex wire1;
Threads::Mutex wire2;

that would mean wire1 and wire2 can run simuletaneously, while other wire1 are prevented until the wire1 scope exits. Correct?
i presume im doing everything correctly?
Yes, yes.
also just to note, using microtimer & setTimeslice together results in NO output. however, if you run either timeslice alone or microtimer alone, sketch runs fine with above code.
You need to use a reasonable value. If you set both to 1, Teensy will be stuck in interrupts the entire time and can't process anything else.

"threads.setMicroTimer(1);" that's 1'000'000 interrupts per second.
 
@tonon81. I am looking at your example and am a bit confused with a couple things (probably because I haven't spent enough time understanding threading). Anyway, when I look at:

Code:
  { Threads::Scope scope(serial);
    Serial.println("2");
    if ( x == 191 && y == 127 ) {
      myCounter++;
      digitalWriteFast(LED, 1);
      Serial.print("Wire Register Read Thread1: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.print(" GOOD! : Counter: ");
      Serial.println(myCounter);
      Serial.println("3");
    }
    else {
      Serial.print("Wire Register Read Thread1: ");
      Serial.print(x);
      Serial.print(" : ");
      Serial.print(y);
      Serial.println(" FAIL!");
    }
    Serial.println("4");
  }

it looks like you are locking Serial so no other thread can use Serial? You also declared Threads::Mutex serial;. I think I get this? Now here is where I am getting confused. In the following code snippet it looks like you lock Wire1 but all your references are to Wire and I never see any calls to Wire?

Code:
  { Threads::Scope scope(wire1);
    Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
    Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 2);
    x = Wire.read();
    y = Wire.read();
  }

Can you all help me understand. Please pardon my ignorance on this.

Thanks
Mike
 
good evening mjs513

correct, "wire1" can be anything
I could call it "blah"
now if a second thread wanted to use "blah", sorry, its locked, wait for it.
blah is released, and next blah can lock.

basically, the class name itself is locked, no similar locks will work together, and take turns
hope i said it right for you

in my case i could call wire,wire1,wire2 as a mutex class in the sketch
anytime i write to a SHARED wire1 bus, ONLY wire1 mutexes are blocked while wire1 is locked.
so basically, only SAME NAMES cant run, so if one thread locks wire1, another thread won't be able to access wire1, BUT, it could still lock and use wire2

by labelling them something easier to understand, we will know that whenever we do an i2c read/write on wire1, we add a scope lock to wire1, so that nothing else talks to that i2c bus.
so if i use 11 i2c devices on the same bus
they will all use wire1 scope lock

technically, we're only blocking same mutexes and not others. Or in other words, we're locking down the name of the class (wire1) and no one else can use wire1 until it's released, but you can still access other mutexes, only the same name convection ones are locked. this is why you can access serial, serial1, serial2, etc, wire1 wire2, spi1, spi2 all at the same time, but they each get a priority lock, other accesses are blocked, as 2 spi1 locks will never happen, the second one only happens after the first one unlocks.

and yes, im using wire1 in the demo, even im using wire, because my project uses wire1, but it shows you you can name your locks anything you want

Threads::Mutex wire1;
Threads::Mutex spi1;
Threads::Mutex serial;
Threads::Mutex mjs513; <-- new lock

This could also not only be for hardware use, but for regular coding use, variables, etc. You could use atomic or whatever, or use this for specific code you want to run

Code:
 { Threads::Scope scope(mjs513);
    //as an example, since theres only 2 scopes, we'll let them run sequentially, with combined commands, because, even with combined serial and i2c in the scope, the 2 scopes will never run together.
    Serial.println("This is a test");
    Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
    Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 2);
    x = Wire.read();
    y = Wire.read();
  }
  { Threads::Scope scope(mjs513);
    //as an example, since theres only 2 scopes, we'll let them run sequentially, with combined commands, because, even with combined serial and i2c in the scope, the 2 scopes will never run together.
    Serial.println("This is a test");
    Wire.beginTransmission(32); Wire.write(0x0C); Wire.write(0xFF); Wire.write(0xFF); Wire.endTransmission();
    Wire.beginTransmission(32); Wire.write(0x12); Wire.endTransmission(); Wire.requestFrom(32, 2);
    x = Wire.read();
    y = Wire.read();
  }

this is an example of dual serial and i2c inside the same lock using your name, since the same name scopes are set, and theres only 2 scopes, it shows you how each scope can run sequentually, and never together, no matter what bus it accesses
this might give you a better understanding
but the method above your post that i did is the right way to access multiple devices on the same bus without issues.
 
Last edited:
if you did a different lock for both scopes, and they each would be in a different thread, then Serial and Wire would collide.
Hope my explanation is understandable ;P
 
Hi tonton81.

Thanks a bunch for your explanation the fog is lifting as they say. As I read and re-read it more it becomes clearer. I really do appreciate you taking the time to explain this to me. What I need to do is set up an example for me to test all these concepts with HW in the loop to make sure I get it and to see what works and doesn't work for myself. Best way for me anyway. Think these discussion will help newbies like me to understand threading a little bit more.

Anyway, have another question off topic, but what's the maximum length of wire I could use before degradation of the signals? Looking at running wires from my upper arm to my wrist?

Thanks
Mike
 
i been running the above loop all day, the volatile counter is always going up and down and maintaining the 0 value position so there doesnt seem to be any missed threads going on :)
also all the reads and writes theough switching are all reported as GOOD

depends, i run 22 gauge solid wire 5 foot from an 8xmcp23s17 spi expander, about 10foot 22 gauge solid wire to my serial uart lcd screens (x2) and 4 of the 11 i2c devices on wire1 bus are on UTP cable about 10feet
 
Last edited:
here is a good question
is it possible to lock 2 different mutexes before running a function since A) reads a value from i2c and B) writes update to spi bus
ex

Code:
{
Threads::Scope scope(wire1);
Threads::Scope scope(spi1);
if (read wire byte ) write (spi byte) <-- yes i know this isnt right but you get what i mean
}

will this dual lock work?

lets not forget when reading from canbus i would also like to lock serial as well since the value is read from spi and sent via uart, in my setup

thanks
 
Last edited:
Hi tonton81.

Thanks a bunch for your explanation the fog is lifting as they say. As I read and re-read it more it becomes clearer. I really do appreciate you taking the time to explain this to me. What I need to do is set up an example for me to test all these concepts with HW in the loop to make sure I get it and to see what works and doesn't work for myself. Best way for me anyway. Think these discussion will help newbies like me to understand threading a little bit more.

Anyway, have another question off topic, but what's the maximum length of wire I could use before degradation of the signals? Looking at running wires from my upper arm to my wrist?

Thanks
Mike

im no expert but i kept trying till i got it right, thanks to tni for confirming i didnt do it wrong, at least i understand it better too

arm to wrist should be fine without signal loss
 
Back
Top