Delays in Threaded mode

Since system threaded is becoming the default I am trying to figure out how to use it correctly. It remains unclear to me the ramifications of threaded vs. unthreaded. As a newbie to threading I find the Particle documentation unclear. For years I mostly used unthreaded, partly based on the recommendations of rickkas7 in 2018 to avoid it. I think I gather that threading is only implemented for system calls but some of the documentation makes me wonder if every function of mine has a separate thread.

I use SYSTEM_MODE(SEMI_AUTOMATIC); and am trying to use SYSTEM_THREAD(ENABLED); I have also tried SYSTEM_THREAD(DISABLED); I only connect to the cloud if I have enough battery and I limit attempts to connect to 3 minutes. Since I am running on solar power in northern winters and poor cell reception, conserving power is important. This makes for a bit complicated code and timing.

Recently with a Boron 404X on OS-6.3.4, I seem to be getting timings wrong. I am very confused about the behavior of waitFor() in threaded and unthreaded modes and how to create a simple delay in code.

FOR NOW, I am just trying to figure out how to do a simple delay in my code (not system calls). Several years ago, creating a function with while() containing Particle.process() was highly recommended instead of using delay(). I don’t think that is working for me anymore. What is the best way to create a delay in code these days with threading enabled? And do I need to use a different delay for system processes, vs my functions?

thanks for any guidance that might get me back on track. john

There are at least three threads in a normal threaded application: the system thread, the application ("loop") thread, and the software timer thread. There may be some other threads behind the scenes that aren't particularly relevant.

Each thread runs until relinquished, the thread timeslice time is exceeded, or a higher priority thread requires service. A thread is relinquished when waiting on a mutex that is blocked, calling delay(), returning from the loop() function, and a few other situations. If you do not yield the thread with 1 millisecond your thread may be swapped out and another one run, but it will resume on the instructions as if it had been running the whole time.

waitFor and waitUntil block the current thread until the condition is reached. Other threads will continue to run normally.

In threaded mode, Particle.process doesn't do anything because the thread scheduler determines when the system gets processing time, you don't need to manually.

The old technique of calling Particle.process in a loop instead of using delay should not be used anymore. The reason is that delay used to only service the cloud once per second in delay, which wasn't enough processing time when doing an OTA at the same time. This is no longer relevant in threaded mode because the thread scheduler handles this, not delay.

If you have timing-sensitive code, there are various ways of handing this, but a worker thread for your timing sensitive code is usually the best way. Using a SINGLE_THREADED_BLOCK, and other techniques can be used, but there are trade-offs with doing this and you may want to make sure that is really the best way, because often it is not and blocking thread swapping can interfere with system operation.

Thanks for the response. I'm glad to hear that "loop" has only one thread. So I don't need to worry about my own functions running over each other, right?.
Three followup question:

  1. If I do 2 system calls separated by delay() do they get run in sequence or simultaneously? e.g:
    Particle.connect();
    delay(500);
    Particle.reset();

  2. If I do a system call , a delay(), and then a call to a function of mine, will the system call run at the same time as my function? e.g.:
    Particle.connect();
    delay(500);
    my.function.call();

  3. If I do 2 of my own function calls separated by a delay() they will not run over each other and there will be a 500ms delay between them, e.g:
    my.function.call1();
    delay(500);
    my.function.call2();

My guess is that:
for #1 the two system calls will not run at the same time, but I'm unsure if they will have a 500ms delay between them.
for #2, if the system call takes longer than 500ms it will still be running when my.function.call runs.
for #3, my function calls will not run simultaneous and will have a 500ms delay between them.

Finally, my guess is that this simple description breaks down if I put a system call within my function. Corrections to my thoughts here greatly appreciated.

For both 1 and 2 it's mostly irrelevant whether the function runs in the system thread or not. What is important is that Particle.connect() is non-blocking. It returns and executes the next line before the connection is made. It doesn't matter whether you're making another system call or your own code.

When you write your code, each function, including the loop function, executes linearly. In 3, call1 and call2 will be separated by a minimum of 500 milliseconds.

but for examples 1&2 even though the Particle.connect() is running in the background, wouldn't the delay(500) delay the execution of the second function by 500ms?
or is delay(500) ignored and in #1 the second system call executed immediately? So I think my question is: Does delay(500) delay execution of the next call, even if the 2nd call is a system call on the system thread?

and different but related:
So if I want Particle.connect() to complete before continuing with additional code I need to do something with waitFor()? like I've seen done for Cellular.connect, eg:
if (!waitFor( Cellular.connect, 120000)) { do something if it can't connect for 2 minutes, like go to sleep }
or
if (!waitFor(Cellular.ready, 120000)) { do something if no connection for 2 min., like go to sleep}

The delay(500) always occurs regardless of what's after it.

Yes, you can wait on Particle.connected (that's the function that returns true if the connection has completed), like you would wait on Cellular.ready.