Forum Rule: Always post complete source code & details to reproduce any issue!
Page 13 of 16 FirstFirst ... 3 11 12 13 14 15 ... LastLast
Results 301 to 325 of 379

Thread: Teensy 3.x multithreading library first release

  1. #301
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,864
    Quote Originally Posted by Epyon View Post
    ... 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.

  2. #302
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,748
    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

  3. #303
    Senior Member
    Join Date
    Sep 2013
    Location
    Boston, MA
    Posts
    109
    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.

  4. #304
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,748
    honestly, the user should be already aware of writing to invalid locations will cause crashes, so i see no issue with this

  5. #305
    Junior Member
    Join Date
    Jul 2017
    Posts
    1
    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.

  6. #306
    Senior Member
    Join Date
    Sep 2013
    Location
    Boston, MA
    Posts
    109
    Quote Originally Posted by Fredb View Post
    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.

  7. #307
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,748
    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

  8. #308
    Senior Member Epyon's Avatar
    Join Date
    Apr 2013
    Location
    Belgium
    Posts
    443
    Quote Originally Posted by defragster View Post
    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.
    Quote Originally Posted by tonton81 View Post
    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.

  9. #309
    Senior Member
    Join Date
    Sep 2013
    Location
    Boston, MA
    Posts
    109

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

  10. #310
    Junior Member
    Join Date
    Sep 2017
    Location
    Bay Area, USA
    Posts
    17

    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?

    Quote Originally Posted by ftrias View Post
    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.

  11. #311
    Senior Member
    Join Date
    Sep 2013
    Location
    Boston, MA
    Posts
    109
    Quote Originally Posted by smachin View Post
    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?

  12. #312
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,540
    Quote Originally Posted by ftrias View Post
    But perhaps the Serial objects are now thread-safe?
    No, they are not. But again, no problem if the same SerialX is not used in different threads.

  13. #313
    Junior Member
    Join Date
    Sep 2017
    Location
    Bay Area, USA
    Posts
    17
    @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())?

  14. #314
    Senior Member
    Join Date
    Sep 2013
    Location
    Boston, MA
    Posts
    109
    Quote Originally Posted by smachin View Post
    @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.

  15. #315
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,864
    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.

  16. #316
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,748
    why would anyone use serialevent when serial.available is whats commonly used:P

  17. #317
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,864
    Quote Originally Posted by tonton81 View Post
    why would anyone use serialevent when serial.available is whats commonly used:P
    No reason - Once you see what it does. No better than doing it your self during loop() 'if (serial.available)' then call your own function.

  18. #318
    Junior Member
    Join Date
    Sep 2017
    Location
    Bay Area, USA
    Posts
    17
    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.

  19. #319
    Junior Member
    Join Date
    Sep 2017
    Location
    Bay Area, USA
    Posts
    17
    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?

  20. #320
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,748
    im pretty much abusing teensythreads in a big project with mutex locks, its an awesome library

  21. #321
    Junior Member
    Join Date
    Sep 2017
    Location
    Bay Area, USA
    Posts
    17
    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.

  22. #322
    Junior Member
    Join Date
    Sep 2017
    Location
    Bay Area, USA
    Posts
    17
    @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?

  23. #323
    Senior Member
    Join Date
    Sep 2013
    Location
    Boston, MA
    Posts
    109
    Quote Originally Posted by smachin View Post
    @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.

  24. #324
    Senior Member
    Join Date
    Jul 2014
    Location
    New York
    Posts
    1,841
    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

  25. #325
    Senior Member
    Join Date
    Jul 2014
    Location
    New York
    Posts
    1,841
    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •