PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. Teensy Threading Library
  2. ===================================================
  3. Teensy Threading Library implements preemptive threads for the Teensy 3 & 4
  4. platform from [PJRC](https://www.pjrc.com/teensy/index.html). It supports a
  5. native interface and a `std::thread` interface.
  6. Official repsitory https://github.com/ftrias/TeensyThreads
  7. Simple example
  8. ------------------------------
  9. ```C++
  10. #include <TeensyThreads.h>
  11. volatile int count = 0;
  12. void thread_func(int data){
  13. while(1) count += data;
  14. }
  15. void setup() {
  16. threads.addThread(thread_func, 1);
  17. }
  18. void loop() {
  19. Serial.println(count);
  20. }
  21. ```
  22. Or using std::thread
  23. ```C++
  24. #include <TeensyThreads.h>
  25. volatile int count = 0;
  26. void thread_func(int data){
  27. while(1) count += data;
  28. }
  29. void setup() {
  30. std::thread th1(thread_func, 1);
  31. th1.detach();
  32. }
  33. void loop() {
  34. Serial.println(count);
  35. }
  36. ```
  37. Install
  38. -----------------------------
  39. The easiest way to get started is to download the ZIP file from this repository and install it in Arduino using the menu `Sketch` / `Include Library` / `Add .ZIP Library`.
  40. You can then review the Examples provided `File` / `Examples` / `TeensyThreads`.
  41. Usage
  42. -----------------------------
  43. First, include the library with
  44. ```C++
  45. #include <TeensyThreads.h>
  46. ```
  47. A global variable `threads` of `class Threads` will be created and used to
  48. control the threading action. The library is hard-coded to support 8 threads,
  49. but this may be changed in the source code of Threads.cpp.
  50. Threads are created by `threads.addThread()` with parameters:
  51. >`int addThread(func, arg, stack_size, stack)`
  52. >
  53. >- Returns an ID number or -1 for failure
  54. >
  55. >- **func** : function to call to perform thread work. This has the form of `void f(void *arg)` or `void f(int arg)` or `void f()`
  56. >
  57. >- **arg** : (optional) the `arg` passed to `func` when it starts.
  58. >
  59. >- **stack_size** : (optional) the size of the thread stack. If stack_size is 0 or missing, then 1024 is used.
  60. >
  61. >- **stack** : (optional) pointer to a buffer to use as stack. If stack is 0 or missing, then the buffer is allocated from the heap.
  62. All threads start immediately and run until the function terminates (usually with
  63. a return).
  64. Once a thread ends because the function returns, then the thread will be reused
  65. by a new function.
  66. If a stack has been allocated by the library and not supplied by the caller, it
  67. will be freed when a new thread is added, not when it terminates. If the stack
  68. was supplied by the caller, the caller must free it if needed.
  69. The following members of `class Threads` control threads. Items in all caps
  70. are constants in `Threads` and are accessed as in `Threads::EMPTY`.
  71. Threads | Description
  72. --- | ---
  73. int id(); | Get the id of the currently running thread
  74. int getState(int id); | Get the state; see class constants. Can be EMPTY, RUNNING, ENDED, SUSPENDED.
  75. int wait(int id, unsigned int timeout_ms = 0) | Wait until thread ends, up to timeout_ms milliseconds. If 0, wait indefinitely.
  76. int kill(int id) | Permanently stop a running thread. Thread will end on the next thread slice tick.
  77. int suspend(int id) |Suspend a thread (on the next slice tick). Can be restarted with restart().
  78. int restart(int id); | Restart a suspended thread.
  79. int setSliceMillis(int milliseconds) | Set each time slice to be 'milliseconds' long
  80. int setSliceMicros(int microseconds) | Set each time slice to be 'microseconds' long
  81. void yield() | Yield current thread's remaining time slice to the next thread, causing immedidate context switch
  82. void delay(int millisecond) | Wait for milliseconds using yield(), giving other slices your wait time
  83. int start(int new_state = -1) | Start/restart threading system; returns previous state. Optionally pass STARTED, STOPPED, FIRST_RUN to restore a different state.
  84. int stop() | Stop threading system; returns previous state: STARTED, STOPPED, FIRST_RUN
  85. **Advanced functions** |
  86. void setDefaultStackSize(unsigned int bytes_size) | Set the stack size for new threads in bytes
  87. void setTimeSlice(int id, unsigned int ticks) | Set the slice length time in ticks for a thread (1 tick = 1 millisecond, unless using MicroTimer)
  88. void setDefaultTimeSlice(unsigned int ticks) |Set the slice length time in ticks for all new threads (1 tick = 1 millisecond, unless using MicroTimer)
  89. int setMicroTimer(int tick_microseconds = DEFAULT_TICK_MICROSECONDS) | use the microsecond timer provided by IntervalTimer & PIT; instead of 1 tick = 1 millisecond, 1 tick will be the number of microseconds provided (default is 100 microseconds)
  90. In addition, the Threads class has a member class for mutexes (or locks):
  91. Threads::Mutex | Description
  92. --- | ---
  93. int getState() | Get the lock state; 1+=locked; 0=unlocked
  94. int lock(unsigned int timeout_ms = 0) | Lock, optionally waiting up to timeout_ms milliseconds
  95. int try_lock() | If lock available, get it and return 1; otherwise return 0
  96. int unlock() | Unlock if locked
  97. When possible, it's best to use `Threads::Scope` instead of `Threads::Mutex` to ensure orderly locking and unlocking.
  98. Threads::Scope | Description
  99. --- | ---
  100. Scope(Mutex& m) | On creation, mutex is locked
  101. ~Scope() | On descruction, it is unlocked
  102. ```C++
  103. // example:
  104. Threads::Mutex mylock;
  105. mylock.lock();
  106. x = 1;
  107. mylock.unlock();
  108. if (y) {
  109. Threads::Scope m(mylock); // lock on creation
  110. x = 2;
  111. } // unlock at destruction
  112. ```
  113. Usage notes
  114. -----------------------------
  115. The optimizer sometimes has strange side effects because it thinks variables
  116. can't be changed within code. For example, if one thread modifies a variable
  117. and another thread checks for it, the second thread's check may be optimized
  118. away. Here is an example:
  119. ```C++
  120. // BUGGY CODE
  121. int state = 0; // this should be "volatile int state"
  122. void thread1() { state = processData(); }
  123. void run() {
  124. while (state < 100) { // this line changed to 'if'
  125. // do something
  126. }
  127. }
  128. ```
  129. In the code above, seeing that `state` is not changed in the loop, the
  130. optimizer will convert the `while (state<100)` into an `if` statement. Adding
  131. `volatile` to the declaration of `int state` will usually remedy this (as in
  132. `volatile int state`).
  133. ```C++
  134. // CORRECT CODE
  135. volatile int state = 0;
  136. void thread1() { state = processData(); }
  137. void run() {
  138. while (state < 100) { // this line changed to 'if'
  139. // do something
  140. }
  141. }
  142. ```
  143. Locking
  144. -----------------------------
  145. Because the Teensy libraries are not usually thread-safe, it's best to only use
  146. one library in one thread. For example, if using the Wire library, it should
  147. always be used in a single thread. If this cannot be avoided, then all uses
  148. should be surrounded by locks.
  149. For example:
  150. ```C++
  151. Threads::Mutex wire_lock;
  152. void init()
  153. {
  154. Threads::Scope scope(wire_lock);
  155. Wire.beginTransmission(17);
  156. Wire.write(0x1F);
  157. Wire.write(0x31);
  158. Wire.endTransmission();
  159. }
  160. ```
  161. Experimental: For widely used libraries like Serial, adding locks may require
  162. vast changes to the code. Because of this, the library provides a helper class
  163. and set of macros to encapsulate all method calls with a lock. An example
  164. illustrates its use:
  165. ```C++
  166. // this method is experimental
  167. ThreadWrap(Serial, SerialX);
  168. #define Serial ThreadClone(SerialX)
  169. int thread_func()
  170. {
  171. Serial.println("begin");
  172. }
  173. ```
  174. In the code above, every time `Serial` is used, it will first lock a mutex,
  175. then call the desired method, then unlock the mutex. This shortcut will only
  176. work on all the code located below the `#define` line. More information
  177. about the mechanics can be found by looking at the source code.
  178. Alternative std::thread interface
  179. -----------------------------
  180. The library also supports the construction of minimal `std::thread` as indicated
  181. in C++11. `std::thread` always allocates it's own stack of the default size. In
  182. addition, a minimal `std::mutex` and `std::lock_guard` are also implemented.
  183. See http://www.cplusplus.com/reference/thread/thread/
  184. Example:
  185. ```C++
  186. void run() {
  187. std::thread first(thread_func);
  188. first.detach();
  189. }
  190. ```
  191. The following members are implemented:
  192. ```C++
  193. namespace std {
  194. class thread {
  195. bool joinable();
  196. void detach();
  197. void join();
  198. int get_id();
  199. }
  200. class mutex {
  201. void lock();
  202. bool try_lock();
  203. void unlock();
  204. };
  205. template <class Mutex> class lock_guard {
  206. lock_guard(Mutex& m);
  207. }
  208. }
  209. ```
  210. Notes on implementation
  211. -----------------------------
  212. Threads take turns on the CPU and are switched by the `context_switch()`
  213. function, written in assembly. This function is called by the SysTick ISR. The
  214. library overrides the default `systick_isr()` to accomplish switching. On the
  215. Teensy by default, each tick is 1 millisecond long. By default, each thread
  216. runs for 100 ticks, or 100 milliseconds, but this can be changed by
  217. `setTimeSlice()`.
  218. Much of the Teensy core software is thread-safe, but not all. When in doubt,
  219. stop and restart threading in critical areas. In general, functions that share
  220. global variables or state should not be called on different threads at the
  221. same time. For example, don't use Serial in two different threads
  222. simultaneously; it's ok to make calls on different threads at different times.
  223. The code comments on the source code give some technical explanation of the
  224. context switch process:
  225. ```C
  226. /*
  227. * context_switch() changes the context to a new thread. It follows this strategy:
  228. *
  229. * 1. Abort if called from within an interrupt
  230. * 2. Save registers r4-r11 to the current thread state (s0-s31 is using FPU)
  231. * 3. If not running on MSP, save PSP to the current thread state
  232. * 4. Get the next running thread state
  233. * 5. Restore r4-r11 from thread state (s0-s31 for FPU)
  234. * 6. Set MSP or PSP depending on state
  235. * 7. Switch MSP/PSP on return
  236. *
  237. * Notes:
  238. * - Cortex-M4 has two stack pointers, MSP and PSP, which I alternate. See the
  239. * CPU reference manual under the Exception Model section.
  240. * - I tried coding this in asm embedded in Threads.cpp but the compiler
  241. * optimizations kept changing my code and removing lines so I have to use
  242. * a separate assembly file. But if you try C++, make sure to declare the
  243. * function "naked" so the stack pointer SP is not modified when called.
  244. * This means you can't use local variables, which are stored in stack.
  245. * Also turn optimizations off using optimize("O0").
  246. * - Function is called from systick_isr (also naked) via a branch. Again, this is
  247. * to preserve the stack and LR. I override the default systick_isr().
  248. * - Since Systick can be called from within another interrupt, I check
  249. * for this and abort.
  250. * - Teensy uses MSP for it's main thread; I preserve that. Alternatively, I
  251. * could have used PSP for all threads, including main, and reserve MSP for
  252. * interrupts only. This would simplify the code slightly, but could introduce
  253. * incompatabilities.
  254. * - If this interrupt is nested within another interrupt, all kinds of bad
  255. * things can happen. This is especially true if usb_isr() is active. In theory
  256. * we should be able to do a switch even within an interrupt, but in my
  257. * tests, it would not work reliably.
  258. */
  259. ```
  260. Todo
  261. -----------------------------
  262. - Optimize assembler and other switching code.
  263. - Support unlimited threads.
  264. - Check for stack overflow during context_change() to aid in debugging;
  265. or have a stack that grows automatically if it gets close filling.
  266. - Fully implement the new C++11 std::thread or POSIX threads.
  267. See http://www.cplusplus.com/reference/thread/thread/.
  268. Changes
  269. -----------------------------
  270. Revision 0.2: March 2017
  271. 1. Change default time slice to 10 milliseconds
  272. 2. Add setDefaultTimeSlice(), setDefaultStackSize().
  273. 3. Support slices smaller than 1 ms using IntervalTimer; see setMicroTimer().
  274. 4. Rename to use TeensyThreads.h to avoid conflicts
  275. Revision 0.3: April 2017
  276. 1. Rename Threads::Lock to Threads::Suspend
  277. 2. Add setSliceMicros() and setSliceMillis()
  278. 3. "lock" will suspend blocking thread until "unlock" and then give thread priority
  279. 4. Add ThreadWrap macro and supporting classes
  280. 5. Support linking with LTO (link time optimization)
  281. Revision 0.4: July 2017
  282. 1. Make ThreadInfo dynamic, saving memory for unused threads
  283. Other
  284. -----------------------------
  285. See this thread for development discussion:
  286. https://forum.pjrc.com/threads/41504-Teensy-3-x-multithreading-library-first-release
  287. This project came about because I was coding a Teensy application with multiple
  288. things happening at the same time, wistfully reminiscing about multithreading
  289. available in other OSs. I searched for threading tools, but found nothing for my
  290. situation. This combined with boredom and abundant free time resulting in
  291. complete overkill for the solution and thus this implementation of preemptive
  292. threads.
  293. Copyright 2017 by Fernando Trias. See license.txt for details.