Particle threads tutorial

Great tutorial, thank you very much.

I have a some questions:

  • 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.

1 Like

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?

2 Likes

delay() on the Electron just called vTaskDelay so they should be equally efficient. On the Photon it calls into WICED, but it probably works the same.

The main difference is that os_thread_delay_until takes care of the math to keep the period constant.

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.

1 Like

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
}

5 posts were split to a new topic: Loop not getting called when using SYSTEM_THREAD(ENABLED)

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

  1. All object constructors are executed
  2. System thread is initialised and starts running its functions
  3. 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.

3 Likes

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?

No, that’s not how the threads work.

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.

2 Likes

Are os_mutex_lock and os_mutex_unlock safe to use in an ISR?

os_mutex_lock and os_mutex_unlock are not safe from an ISR.

If you look at concurrent_hal.cpp you can see that they map to xSemaphoreTake and xSemaphoreGive.

From the FreeRTOS docs, there are the functions xSemaphoreTakeFromISR and xSemaphoreGiveFromISR.

Those are the functions you’d need to use from an ISR.

1 Like

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?

Thanks

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.

1 Like

Thanks, so do you think the use of mutex to stop the other thread while the while operation is happening would be a safe route to use?

does below look like the correct way to use a mutex instead of a thread lock?

Is there any way of checking if a queue is empty without taking an item?

You can make the variable “haveEvent” as an external varibale then use that to see if there’s anything in the queue. Thats what I did.

The API isn’t exposed, but the FreeRTOS function xQueuePeek is what you want.

If you look at the source to os_queue_take you can see how trivial the mapping to the underlying xQueue* functions is:

int os_queue_take(os_queue_t queue, void* item, system_tick_t delay, void*)
{
    return xQueueReceive(queue, item, delay)!=pdTRUE;
}

Thanks but what headers do I need to include in my application to access the underlying method? Just include "FreeRTOS.h doesn’t work.