I was not aware that the main loop thread on the Electron is only called 100 times per second, why is this?
Is this also true for SYSTEM_THREAD(ENABLED)
With SYSTEM_THREAD(ENABLED) I think there is already a queue on the application thread wrapped by the ActiveObjectCurrentThreadQueue and helper macros like APPLICATION_THREAD_CONTEXT_ASYNC can/should user firmware code use them or should it create its own queue for stuff it would like to delegate/send to application thread.
The Electron loop() only runs 100 times per second vs. 1000 on the Photon when you are not using SYSTEM_THREAD(ENABLED).
However, when you have SYSTEM_THREAD(ENABLED), it runs really fast, using all excess CPU, at least in 0.7.0 on the Electron.
I’d probably just use the macros like APPLICATION_THREAD_CONTEXT_ASYNC in system_threading.h. I should probably add those to the tutorial as I used the queue directly mainly to illustrate the queue functions, not as the best practice to defer to application thread. Good idea.
EDIT: I don’t think you can use APPLICATION_THREAD_CONTEXT_ASYNC from user firmware. Particle.h doesn’t include system_threading.h, and even if you include it manually, ApplicationThread is not exported. So I do think you need to use your own queue or other method.
First of all, thanks for this, some great stuff in one place.
One note that might be useful to some folks - one can declare the thread before initializing it, giving an opportunity to set flags, initialize variables and queues in setup() or anywhere else as long as you do it before you initialize the thread. I don’t know of any disadvantage of this approach, though would gladly be corrected if it’s bad practice. An implementation might look like:
// declarations up top
Thread myThread;
void myThreadFunction(void *param);
setup()
{
// do some init stuff that myThread will use
myThread = Thread("myThreadName", myThreadFunction); // starts the thread
}
Unrelated Question:
Any advantage to using os_thread_delay_until(); instead of delay();?
I’m guessing with the normal delay it still visits the thread regularly even if it doesn’t continue execution of lines of code?
The tip to delay start of the thread until setup is good. However, it’s also currently broken in 0.7.0 so you may not want to use it right now.
Ah, well that explains why my firmware hard faults on 0.7.0 (at least partly)! I didn't bother debugging it, just stuck with 0.6.4. I have a feeling you just saved me a lot of future debugging time... thanks!
I suppose also that as long as the initialization code you need to run is practically scoped only to that thread you can also just put your init code in the body of the thread function, but before the loop portion of the thread, eg:
void threadFunction(void *param) {
// do your init stuff here
//
//
while(true) {
// do the normal thread stuff here
//
// Delay so we're called every 1 millisecond (1000 times per second)
os_thread_delay_until(&lastThreadTime, 1);
}
// You must not return from the thread function
}
Hello! This is a fantastic tutorial. I was reading up on priorities in the FreeRTOS documentation and was wondering what the OS_THREAD_PRIORITY_DEFAULT is and does.
I’m very new to Threads so bear with me for this question on fundamentals. In the first block of code, under How fast does a thread run? with the incrementing a counter example, could you explain the order of execution of the setup, loop, and threadFunction? I’m assuming first, setup runs. Then, does the thread function run after every time the loop runs? Do they go back and forth in running? Thanks.
Threads and functions are different concepts.
When you take setup() and loop() they are functions which run on the so-called application thread, with SYSTEM_THREAD(ENABLE) you have one other (separate) thread for the system functions to be run on.
Both these threads run independently virtually along side each other, so you never know what function is "in execution" on the one thread while you have a particular function running on the other. That's where synchronisation objects are needed, to "correlate" the to threads again.
If you then happen to open a new thread with its own "driving" function, you'll just have another independent thread running along side the others.
So to your question about order of execution (assuming SYSTEM_THREAD(ENABLED))
All object constructors are executed
System thread is initialised and starts running its functions
Application thread is initialised and starts running its functions
While the system thread is doing its stuff the application thread first executes the STARTUP() functions (only once), followed by setup() (only once) and then keeps calling loop() and other regular functions like serialEvent() and the likes.
If you are starting seperate threads, they will be split of the application thread and then execute independently with the fixed 1ms time slices.
Is there a way to run the thread function periodically (call it every 2 seconds or so) without looping forever within the thread function? Can the thread function be called externally?
If you want one thread for all timers, run sequentially, use the software timers. There will only be one thread, and it will call each timer in sequence. The downside is that a mis-behaved timer can stop all the others from executing.
The flip side is one thread per timer, using os_thread_delay_until. There’s no way they can interfere with each other (unless they disable interrupts), but there’s a lot of overhead.
Is it know when one thread will stop the other thread if one thread is taking awhile?
Lets say in Thread 1, I have a while loop that may take upto 60 seconds and I also have Thread 2 doing other tasks. Is it possilble Thread 2 can stop the processing of the Thread 1 when its in the while loop? Like the while loop just suddenly exits before its finished by Thread 2?
Yes, FreeRTOS threads are preemptive and the execution of one thread will stop pretty much at any time.
You can voluntarily have a thread give up its time slice by calling os_thread_yield(), os_thread_wait_until, delay() and a number of other functions.
If you need to prevent switching threads you can use a SINGLE_THREADED_BLOCK but you should avoid doing so more than hundreds of microseconds at a time because it can adversely affect the performance of the system.
The thread task switcher normally runs at 1 millisecond intervals.